Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Side by Side Diff: chrome/browser/autocomplete/keyword_provider.cc

Issue 9419043: Revert 122412 - Enabled pressing TAB to traverse through the Omnibox results (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/autocomplete/keyword_provider.h" 5 #include "chrome/browser/autocomplete/keyword_provider.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/string16.h" 10 #include "base/string16.h"
(...skipping 24 matching lines...) Expand all
35 provider_->MaybeEndExtensionKeywordMode(); 35 provider_->MaybeEndExtensionKeywordMode();
36 } 36 }
37 37
38 void StayInKeywordMode() { 38 void StayInKeywordMode() {
39 provider_ = NULL; 39 provider_ = NULL;
40 } 40 }
41 private: 41 private:
42 KeywordProvider* provider_; 42 KeywordProvider* provider_;
43 }; 43 };
44 44
45 // static
46 string16 KeywordProvider::SplitReplacementStringFromInput(
47 const string16& input,
48 bool trim_leading_whitespace) {
49 // The input may contain leading whitespace, strip it.
50 string16 trimmed_input;
51 TrimWhitespace(input, TRIM_LEADING, &trimmed_input);
52
53 // And extract the replacement string.
54 string16 remaining_input;
55 SplitKeywordFromInput(trimmed_input, trim_leading_whitespace,
56 &remaining_input);
57 return remaining_input;
58 }
59
45 KeywordProvider::KeywordProvider(ACProviderListener* listener, Profile* profile) 60 KeywordProvider::KeywordProvider(ACProviderListener* listener, Profile* profile)
46 : AutocompleteProvider(listener, profile, "Keyword"), 61 : AutocompleteProvider(listener, profile, "Keyword"),
47 model_(NULL), 62 model_(NULL),
48 current_input_id_(0) { 63 current_input_id_(0) {
49 // Extension suggestions always come from the original profile, since that's 64 // Extension suggestions always come from the original profile, since that's
50 // where extensions run. We use the input ID to distinguish whether the 65 // where extensions run. We use the input ID to distinguish whether the
51 // suggestions are meant for us. 66 // suggestions are meant for us.
52 registrar_.Add(this, 67 registrar_.Add(this,
53 chrome::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY, 68 chrome::NOTIFICATION_EXTENSION_OMNIBOX_SUGGESTIONS_READY,
54 content::Source<Profile>(profile->GetOriginalProfile())); 69 content::Source<Profile>(profile->GetOriginalProfile()));
(...skipping 30 matching lines...) Expand all
85 } 100 }
86 }; 101 };
87 102
88 // We need our input IDs to be unique across all profiles, so we keep a global 103 // We need our input IDs to be unique across all profiles, so we keep a global
89 // UID that each provider uses. 104 // UID that each provider uses.
90 static int global_input_uid_; 105 static int global_input_uid_;
91 106
92 } // namespace 107 } // namespace
93 108
94 // static 109 // static
95 string16 KeywordProvider::SplitKeywordFromInput(
96 const string16& input,
97 bool trim_leading_whitespace,
98 string16* remaining_input) {
99 // Find end of first token. The AutocompleteController has trimmed leading
100 // whitespace, so we need not skip over that.
101 const size_t first_white(input.find_first_of(kWhitespaceUTF16));
102 DCHECK_NE(0U, first_white);
103 if (first_white == string16::npos)
104 return input; // Only one token provided.
105
106 // Set |remaining_input| to everything after the first token.
107 DCHECK(remaining_input != NULL);
108 const size_t remaining_start = trim_leading_whitespace ?
109 input.find_first_not_of(kWhitespaceUTF16, first_white) : first_white + 1;
110
111 if (remaining_start < input.length())
112 remaining_input->assign(input.begin() + remaining_start, input.end());
113
114 // Return first token as keyword.
115 return input.substr(0, first_white);
116 }
117
118 // static
119 string16 KeywordProvider::SplitReplacementStringFromInput(
120 const string16& input,
121 bool trim_leading_whitespace) {
122 // The input may contain leading whitespace, strip it.
123 string16 trimmed_input;
124 TrimWhitespace(input, TRIM_LEADING, &trimmed_input);
125
126 // And extract the replacement string.
127 string16 remaining_input;
128 SplitKeywordFromInput(trimmed_input, trim_leading_whitespace,
129 &remaining_input);
130 return remaining_input;
131 }
132
133 // static
134 const TemplateURL* KeywordProvider::GetSubstitutingTemplateURLForInput( 110 const TemplateURL* KeywordProvider::GetSubstitutingTemplateURLForInput(
135 Profile* profile, 111 Profile* profile,
136 const AutocompleteInput& input, 112 const AutocompleteInput& input,
137 string16* remaining_input) { 113 string16* remaining_input) {
138 if (!input.allow_exact_keyword_match()) 114 if (!input.allow_exact_keyword_match())
139 return NULL; 115 return NULL;
140 116
141 string16 keyword; 117 string16 keyword;
142 if (!ExtractKeywordFromInput(input, &keyword, remaining_input)) 118 if (!ExtractKeywordFromInput(input, &keyword, remaining_input))
143 return NULL; 119 return NULL;
144 120
145 // Make sure the model is loaded. This is cheap and quickly bails out if 121 // Make sure the model is loaded. This is cheap and quickly bails out if
146 // the model is already loaded. 122 // the model is already loaded.
147 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile); 123 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile);
148 DCHECK(model); 124 DCHECK(model);
149 model->Load(); 125 model->Load();
150 126
151 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); 127 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword);
152 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL; 128 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL;
153 } 129 }
154 130
155 string16 KeywordProvider::GetKeywordForText(
156 const string16& text) const {
157 const string16 keyword(TemplateURLService::CleanUserInputKeyword(text));
158
159 if (keyword.empty())
160 return keyword;
161
162 TemplateURLService* url_service = GetTemplateURLService();
163 if (!url_service)
164 return string16();
165
166 // Don't provide a keyword if it doesn't support replacement.
167 const TemplateURL* const template_url =
168 url_service->GetTemplateURLForKeyword(keyword);
169 if (!TemplateURL::SupportsReplacement(template_url))
170 return string16();
171
172 // Don't provide a keyword for inactive/disabled extension keywords.
173 if (template_url->IsExtensionKeyword()) {
174 const Extension* extension = profile_->GetExtensionService()->
175 GetExtensionById(template_url->GetExtensionId(), false);
176 if (!extension ||
177 (profile_->IsOffTheRecord() &&
178 !profile_->GetExtensionService()->IsIncognitoEnabled(extension->id())))
179 return string16();
180 }
181
182 return keyword;
183 }
184
185 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch(
186 const string16& text,
187 const string16& keyword,
188 const AutocompleteInput& input) {
189 return CreateAutocompleteMatch(GetTemplateURLService(), keyword, input,
190 keyword.size(), SplitReplacementStringFromInput(text, true), 0);
191 }
192
193 void KeywordProvider::Start(const AutocompleteInput& input, 131 void KeywordProvider::Start(const AutocompleteInput& input,
194 bool minimal_changes) { 132 bool minimal_changes) {
195 // This object ensures we end keyword mode if we exit the function without 133 // This object ensures we end keyword mode if we exit the function without
196 // toggling keyword mode to on. 134 // toggling keyword mode to on.
197 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); 135 ScopedEndExtensionKeywordMode keyword_mode_toggle(this);
198 136
199 matches_.clear(); 137 matches_.clear();
200 138
201 if (!minimal_changes) { 139 if (!minimal_changes) {
202 done_ = true; 140 done_ = true;
(...skipping 13 matching lines...) Expand all
216 // whatever we do here! 154 // whatever we do here!
217 // 155 //
218 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for 156 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for
219 // keywords, we might suggest keywords that haven't even been partially typed, 157 // keywords, we might suggest keywords that haven't even been partially typed,
220 // if the user uses them enough and isn't obviously typing something else. In 158 // if the user uses them enough and isn't obviously typing something else. In
221 // this case we'd consider all input here to be query input. 159 // this case we'd consider all input here to be query input.
222 string16 keyword, remaining_input; 160 string16 keyword, remaining_input;
223 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) 161 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input))
224 return; 162 return;
225 163
226 TemplateURLService* model = GetTemplateURLService(); 164 // Make sure the model is loaded. This is cheap and quickly bails out if
165 // the model is already loaded.
166 TemplateURLService* model =
167 profile_ ?
168 TemplateURLServiceFactory::GetForProfile(profile_) :
169 model_;
170 DCHECK(model);
171 model->Load();
227 172
228 // Get the best matches for this keyword. 173 // Get the best matches for this keyword.
229 // 174 //
230 // NOTE: We could cache the previous keywords and reuse them here in the 175 // NOTE: We could cache the previous keywords and reuse them here in the
231 // |minimal_changes| case, but since we'd still have to recalculate their 176 // |minimal_changes| case, but since we'd still have to recalculate their
232 // relevances and we can just recreate the results synchronously anyway, we 177 // relevances and we can just recreate the results synchronously anyway, we
233 // don't bother. 178 // don't bother.
234 // 179 //
235 // TODO(pkasting): http://b/893701 We should remember the user's use of a 180 // TODO(pkasting): http://b/893701 We should remember the user's use of a
236 // search query both from the autocomplete popup and from web pages 181 // search query both from the autocomplete popup and from web pages
237 // themselves. 182 // themselves.
238 std::vector<string16> keyword_matches; 183 std::vector<string16> keyword_matches;
239 model->FindMatchingKeywords(keyword, 184 model->FindMatchingKeywords(keyword,
240 !remaining_input.empty(), 185 !remaining_input.empty(),
241 &keyword_matches); 186 &keyword_matches);
242 187
188 // Prune any extension keywords that are disallowed in incognito mode (if
189 // we're incognito), or disabled.
243 for (std::vector<string16>::iterator i(keyword_matches.begin()); 190 for (std::vector<string16>::iterator i(keyword_matches.begin());
244 i != keyword_matches.end(); ) { 191 i != keyword_matches.end(); ) {
245 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); 192 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i));
246
247 // Prune any extension keywords that are disallowed in incognito mode (if
248 // we're incognito), or disabled.
249 if (profile_ && 193 if (profile_ &&
250 input.matches_requested() == AutocompleteInput::ALL_MATCHES && 194 input.matches_requested() == AutocompleteInput::ALL_MATCHES &&
251 template_url->IsExtensionKeyword()) { 195 template_url->IsExtensionKeyword()) {
252 ExtensionService* service = profile_->GetExtensionService(); 196 ExtensionService* service = profile_->GetExtensionService();
253 const Extension* extension = service->GetExtensionById( 197 const Extension* extension = service->GetExtensionById(
254 template_url->GetExtensionId(), false); 198 template_url->GetExtensionId(), false);
255 bool enabled = 199 bool enabled =
256 extension && (!profile_->IsOffTheRecord() || 200 extension && (!profile_->IsOffTheRecord() ||
257 service->IsIncognitoEnabled(extension->id())); 201 service->IsIncognitoEnabled(extension->id()));
258 if (!enabled) { 202 if (!enabled) {
259 i = keyword_matches.erase(i); 203 i = keyword_matches.erase(i);
260 continue; 204 continue;
261 } 205 }
262 } 206 }
263
264 // Prune any substituting keywords if there is no substitution.
265 if (TemplateURL::SupportsReplacement(template_url) &&
266 remaining_input.empty() && !input.allow_exact_keyword_match()) {
267 i = keyword_matches.erase(i);
268 continue;
269 }
270
271 ++i; 207 ++i;
272 } 208 }
273 if (keyword_matches.empty()) 209 if (keyword_matches.empty())
274 return; 210 return;
275 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); 211 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality());
276 212
277 // Limit to one exact or three inexact matches, and mark them up for display 213 // Limit to one exact or three inexact matches, and mark them up for display
278 // in the autocomplete popup. 214 // in the autocomplete popup.
279 // Any exact match is going to be the highest quality match, and thus at the 215 // Any exact match is going to be the highest quality match, and thus at the
280 // front of our vector. 216 // front of our vector.
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
351 return false; 287 return false;
352 288
353 string16 trimmed_input; 289 string16 trimmed_input;
354 TrimWhitespace(input.text(), TRIM_TRAILING, &trimmed_input); 290 TrimWhitespace(input.text(), TRIM_TRAILING, &trimmed_input);
355 *keyword = TemplateURLService::CleanUserInputKeyword( 291 *keyword = TemplateURLService::CleanUserInputKeyword(
356 SplitKeywordFromInput(trimmed_input, true, remaining_input)); 292 SplitKeywordFromInput(trimmed_input, true, remaining_input));
357 return !keyword->empty(); 293 return !keyword->empty();
358 } 294 }
359 295
360 // static 296 // static
297 string16 KeywordProvider::SplitKeywordFromInput(
298 const string16& input,
299 bool trim_leading_whitespace,
300 string16* remaining_input) {
301 // Find end of first token. The AutocompleteController has trimmed leading
302 // whitespace, so we need not skip over that.
303 const size_t first_white(input.find_first_of(kWhitespaceUTF16));
304 DCHECK_NE(0U, first_white);
305 if (first_white == string16::npos)
306 return input; // Only one token provided.
307
308 // Set |remaining_input| to everything after the first token.
309 DCHECK(remaining_input != NULL);
310 const size_t remaining_start = trim_leading_whitespace ?
311 input.find_first_not_of(kWhitespaceUTF16, first_white) : first_white + 1;
312
313 if (remaining_start < input.length())
314 remaining_input->assign(input.begin() + remaining_start, input.end());
315
316 // Return first token as keyword.
317 return input.substr(0, first_white);
318 }
319
320 // static
361 void KeywordProvider::FillInURLAndContents( 321 void KeywordProvider::FillInURLAndContents(
362 Profile* profile, 322 Profile* profile,
363 const string16& remaining_input, 323 const string16& remaining_input,
364 const TemplateURL* element, 324 const TemplateURL* element,
365 AutocompleteMatch* match) { 325 AutocompleteMatch* match) {
366 DCHECK(!element->short_name().empty()); 326 DCHECK(!element->short_name().empty());
367 DCHECK(element->url()); 327 DCHECK(element->url());
368 DCHECK(element->url()->IsValid()); 328 DCHECK(element->url()->IsValid());
369 int message_id = element->IsExtensionKeyword() ? 329 int message_id = element->IsExtensionKeyword() ?
370 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; 330 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH;
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
448 const bool keyword_complete = (prefix_length == keyword.length()); 408 const bool keyword_complete = (prefix_length == keyword.length());
449 if (relevance < 0) { 409 if (relevance < 0) {
450 relevance = 410 relevance =
451 CalculateRelevance(input.type(), keyword_complete, 411 CalculateRelevance(input.type(), keyword_complete,
452 // When the user wants keyword matches to take 412 // When the user wants keyword matches to take
453 // preference, score them highly regardless of 413 // preference, score them highly regardless of
454 // whether the input provides query text. 414 // whether the input provides query text.
455 supports_replacement, input.prefer_keyword(), 415 supports_replacement, input.prefer_keyword(),
456 input.allow_exact_keyword_match()); 416 input.allow_exact_keyword_match());
457 } 417 }
458 AutocompleteMatch match(this, relevance, false, 418 AutocompleteMatch result(this, relevance, false,
459 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : 419 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE :
460 AutocompleteMatch::HISTORY_KEYWORD); 420 AutocompleteMatch::HISTORY_KEYWORD);
461 match.fill_into_edit.assign(keyword); 421 result.fill_into_edit.assign(keyword);
462 if (!remaining_input.empty() || !keyword_complete || supports_replacement) 422 if (!remaining_input.empty() || !keyword_complete || supports_replacement)
463 match.fill_into_edit.push_back(L' '); 423 result.fill_into_edit.push_back(L' ');
464 match.fill_into_edit.append(remaining_input); 424 result.fill_into_edit.append(remaining_input);
465 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd 425 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd
466 // need CleanUserInputKeyword() to return the amount of adjustment it's made 426 // need CleanUserInputKeyword() to return the amount of adjustment it's made
467 // to the user's input. Because right now inexact keyword matches can't score 427 // to the user's input. Because right now inexact keyword matches can't score
468 // more highly than a "what you typed" match from one of the other providers, 428 // more highly than a "what you typed" match from one of the other providers,
469 // we just don't bother to do this, and leave inline autocompletion off. 429 // we just don't bother to do this, and leave inline autocompletion off.
470 match.inline_autocomplete_offset = string16::npos; 430 result.inline_autocomplete_offset = string16::npos;
471 431
472 // Create destination URL and popup entry content by substituting user input 432 // Create destination URL and popup entry content by substituting user input
473 // into keyword templates. 433 // into keyword templates.
474 FillInURLAndContents(profile_, remaining_input, element, &match); 434 FillInURLAndContents(profile_, remaining_input, element, &result);
475 435
476 if (supports_replacement) 436 if (supports_replacement)
477 match.template_url = element; 437 result.template_url = element;
478 match.keyword = keyword; 438 result.transition = content::PAGE_TRANSITION_KEYWORD;
479 match.transition = content::PAGE_TRANSITION_KEYWORD;
480 439
481 return match; 440 return result;
482 } 441 }
483 442
484 void KeywordProvider::Observe(int type, 443 void KeywordProvider::Observe(int type,
485 const content::NotificationSource& source, 444 const content::NotificationSource& source,
486 const content::NotificationDetails& details) { 445 const content::NotificationDetails& details) {
487 TemplateURLService* model = GetTemplateURLService(); 446 TemplateURLService* model =
447 profile_ ? TemplateURLServiceFactory::GetForProfile(profile_) : model_;
488 const AutocompleteInput& input = extension_suggest_last_input_; 448 const AutocompleteInput& input = extension_suggest_last_input_;
489 449
490 switch (type) { 450 switch (type) {
491 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: 451 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED:
492 // Input has been accepted, so we're done with this input session. Ensure 452 // Input has been accepted, so we're done with this input session. Ensure
493 // we don't send the OnInputCancelled event, or handle any more stray 453 // we don't send the OnInputCancelled event, or handle any more stray
494 // suggestions_ready events. 454 // suggestions_ready events.
495 current_keyword_extension_id_.clear(); 455 current_keyword_extension_id_.clear();
496 current_input_id_ = 0; 456 current_input_id_ = 0;
497 return; 457 return;
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 listener_->OnProviderUpdate(!extension_suggest_matches_.empty()); 514 listener_->OnProviderUpdate(!extension_suggest_matches_.empty());
555 return; 515 return;
556 } 516 }
557 517
558 default: 518 default:
559 NOTREACHED(); 519 NOTREACHED();
560 return; 520 return;
561 } 521 }
562 } 522 }
563 523
564 TemplateURLService* KeywordProvider::GetTemplateURLService() const {
565 TemplateURLService* service = profile_ ?
566 TemplateURLServiceFactory::GetForProfile(profile_) : model_;
567 // Make sure the model is loaded. This is cheap and quickly bails out if
568 // the model is already loaded.
569 DCHECK(service);
570 service->Load();
571 return service;
572 }
573
574 void KeywordProvider::EnterExtensionKeywordMode( 524 void KeywordProvider::EnterExtensionKeywordMode(
575 const std::string& extension_id) { 525 const std::string& extension_id) {
576 DCHECK(current_keyword_extension_id_.empty()); 526 DCHECK(current_keyword_extension_id_.empty());
577 current_keyword_extension_id_ = extension_id; 527 current_keyword_extension_id_ = extension_id;
578 528
579 ExtensionOmniboxEventRouter::OnInputStarted( 529 ExtensionOmniboxEventRouter::OnInputStarted(
580 profile_, current_keyword_extension_id_); 530 profile_, current_keyword_extension_id_);
581 } 531 }
582 532
583 void KeywordProvider::MaybeEndExtensionKeywordMode() { 533 void KeywordProvider::MaybeEndExtensionKeywordMode() {
584 if (!current_keyword_extension_id_.empty()) { 534 if (!current_keyword_extension_id_.empty()) {
585 ExtensionOmniboxEventRouter::OnInputCancelled( 535 ExtensionOmniboxEventRouter::OnInputCancelled(
586 profile_, current_keyword_extension_id_); 536 profile_, current_keyword_extension_id_);
587 537
588 current_keyword_extension_id_.clear(); 538 current_keyword_extension_id_.clear();
589 } 539 }
590 } 540 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/keyword_provider.h ('k') | chrome/browser/autocomplete/keyword_provider_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698