OLD | NEW |
---|---|
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 145 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
156 // 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 |
157 // 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, |
158 // 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 |
159 // 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. |
160 string16 keyword, remaining_input; | 160 string16 keyword, remaining_input; |
161 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) | 161 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) |
162 return; | 162 return; |
163 | 163 |
164 // Make sure the model is loaded. This is cheap and quickly bails out if | 164 // Make sure the model is loaded. This is cheap and quickly bails out if |
165 // the model is already loaded. | 165 // the model is already loaded. |
166 TemplateURLService* model = | 166 TemplateURLService* model = GetTemplateURLService(); |
Peter Kasting
2011/07/29 21:08:44
Nit: I also meant for the DCHECK() and Load() call
| |
167 profile_ ? | |
168 TemplateURLServiceFactory::GetForProfile(profile_) : | |
169 model_; | |
170 DCHECK(model); | 167 DCHECK(model); |
171 model->Load(); | 168 model->Load(); |
172 | 169 |
173 // Get the best matches for this keyword. | 170 // Get the best matches for this keyword. |
174 // | 171 // |
175 // NOTE: We could cache the previous keywords and reuse them here in the | 172 // NOTE: We could cache the previous keywords and reuse them here in the |
176 // |minimal_changes| case, but since we'd still have to recalculate their | 173 // |minimal_changes| case, but since we'd still have to recalculate their |
177 // relevances and we can just recreate the results synchronously anyway, we | 174 // relevances and we can just recreate the results synchronously anyway, we |
178 // don't bother. | 175 // don't bother. |
179 // | 176 // |
180 // TODO(pkasting): http://b/893701 We should remember the user's use of a | 177 // TODO(pkasting): http://b/893701 We should remember the user's use of a |
181 // search query both from the autocomplete popup and from web pages | 178 // search query both from the autocomplete popup and from web pages |
182 // themselves. | 179 // themselves. |
183 std::vector<string16> keyword_matches; | 180 std::vector<string16> keyword_matches; |
184 model->FindMatchingKeywords(keyword, | 181 model->FindMatchingKeywords(keyword, |
185 !remaining_input.empty(), | 182 !remaining_input.empty(), |
186 &keyword_matches); | 183 &keyword_matches); |
187 | 184 |
188 // Prune any extension keywords that are disallowed in incognito mode (if | |
189 // we're incognito), or disabled. | |
190 for (std::vector<string16>::iterator i(keyword_matches.begin()); | 185 for (std::vector<string16>::iterator i(keyword_matches.begin()); |
191 i != keyword_matches.end(); ) { | 186 i != keyword_matches.end(); ) { |
192 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); | 187 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); |
188 | |
189 // Prune any extension keywords that are disallowed in incognito mode (if | |
190 // we're incognito), or disabled. | |
193 if (profile_ && | 191 if (profile_ && |
194 input.matches_requested() == AutocompleteInput::ALL_MATCHES && | 192 input.matches_requested() == AutocompleteInput::ALL_MATCHES && |
195 template_url->IsExtensionKeyword()) { | 193 template_url->IsExtensionKeyword()) { |
196 ExtensionService* service = profile_->GetExtensionService(); | 194 ExtensionService* service = profile_->GetExtensionService(); |
197 const Extension* extension = service->GetExtensionById( | 195 const Extension* extension = service->GetExtensionById( |
198 template_url->GetExtensionId(), false); | 196 template_url->GetExtensionId(), false); |
199 bool enabled = | 197 bool enabled = |
200 extension && (!profile_->IsOffTheRecord() || | 198 extension && (!profile_->IsOffTheRecord() || |
201 service->IsIncognitoEnabled(extension->id())); | 199 service->IsIncognitoEnabled(extension->id())); |
202 if (!enabled) { | 200 if (!enabled) { |
203 i = keyword_matches.erase(i); | 201 i = keyword_matches.erase(i); |
204 continue; | 202 continue; |
205 } | 203 } |
206 } | 204 } |
205 | |
206 // Prune any substituting keywords if there is no substitution. | |
207 if (TemplateURL::SupportsReplacement(template_url) && | |
208 !input.allow_exact_keyword_match()) { | |
209 i = keyword_matches.erase(i); | |
210 continue; | |
211 } | |
212 | |
207 ++i; | 213 ++i; |
208 } | 214 } |
209 if (keyword_matches.empty()) | 215 if (keyword_matches.empty()) |
210 return; | 216 return; |
211 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); | 217 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); |
212 | 218 |
213 // Limit to one exact or three inexact matches, and mark them up for display | 219 // Limit to one exact or three inexact matches, and mark them up for display |
214 // in the autocomplete popup. | 220 // in the autocomplete popup. |
215 // Any exact match is going to be the highest quality match, and thus at the | 221 // Any exact match is going to be the highest quality match, and thus at the |
216 // front of our vector. | 222 // front of our vector. |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
380 bool prefer_keyword, | 386 bool prefer_keyword, |
381 bool allow_exact_keyword_match) { | 387 bool allow_exact_keyword_match) { |
382 if (!complete) | 388 if (!complete) |
383 return (type == AutocompleteInput::URL) ? 700 : 450; | 389 return (type == AutocompleteInput::URL) ? 700 : 450; |
384 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword)) | 390 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword)) |
385 return 1500; | 391 return 1500; |
386 return (allow_exact_keyword_match && (type == AutocompleteInput::QUERY)) ? | 392 return (allow_exact_keyword_match && (type == AutocompleteInput::QUERY)) ? |
387 1450 : 1100; | 393 1450 : 1100; |
388 } | 394 } |
389 | 395 |
396 string16 KeywordProvider::GetKeywordForText( | |
sky
2011/08/01 16:02:15
DOesn't match position in header.
| |
397 const string16& text) const { | |
Peter Kasting
2011/07/29 21:08:44
Nit: Indent 4
| |
398 const string16 keyword(TemplateURLService::CleanUserInputKeyword(text)); | |
Peter Kasting
2011/07/29 21:08:44
Nit: Whole function should be indented 2, not 4
| |
399 | |
400 if (keyword.empty()) | |
401 return keyword; | |
402 | |
403 TemplateURLService* url_service = GetTemplateURLService(); | |
404 if (!url_service) | |
405 return string16(); | |
406 url_service->Load(); | |
407 | |
408 // Don't provide a keyword if it doesn't support replacement. | |
409 const TemplateURL* const template_url = | |
410 url_service->GetTemplateURLForKeyword(keyword); | |
Peter Kasting
2011/07/29 21:08:44
Nit: Indent 4 (3 places)
| |
411 if (!TemplateURL::SupportsReplacement(template_url)) | |
412 return string16(); | |
413 | |
414 // Don't provide a keyword for inactive/disabled extension keywords. | |
415 if (template_url->IsExtensionKeyword()) { | |
416 const Extension* extension = profile_->GetExtensionService()-> | |
417 GetExtensionById(template_url->GetExtensionId(), false); | |
418 if (!extension || | |
419 (profile_->IsOffTheRecord() && | |
sky
2011/08/01 16:02:15
nit: indenting off (should be one past ( on previo
| |
420 !profile_->GetExtensionService()->IsIncognitoEnabled(extension->id()))) | |
421 return string16(); | |
422 } | |
423 | |
424 return keyword; | |
425 } | |
426 | |
427 TemplateURLService* KeywordProvider::GetTemplateURLService() const { | |
428 return profile_ ? TemplateURLServiceFactory::GetForProfile(profile_) : | |
429 model_; | |
430 } | |
431 | |
432 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | |
433 const string16& keyword, | |
434 const AutocompleteInput& input) { | |
435 return CreateAutocompleteMatch(GetTemplateURLService(), keyword, input, | |
436 keyword.size(), string16(), 0); | |
437 } | |
438 | |
390 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | 439 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( |
391 TemplateURLService* model, | 440 TemplateURLService* model, |
392 const string16& keyword, | 441 const string16& keyword, |
393 const AutocompleteInput& input, | 442 const AutocompleteInput& input, |
394 size_t prefix_length, | 443 size_t prefix_length, |
395 const string16& remaining_input, | 444 const string16& remaining_input, |
396 int relevance) { | 445 int relevance) { |
397 DCHECK(model); | 446 DCHECK(model); |
398 // Get keyword data from data store. | 447 // Get keyword data from data store. |
399 const TemplateURL* element( | 448 const TemplateURL* element( |
400 model->GetTemplateURLForKeyword(keyword)); | 449 model->GetTemplateURLForKeyword(keyword)); |
401 DCHECK(element && element->url()); | 450 DCHECK(element && element->url()); |
402 const bool supports_replacement = element->url()->SupportsReplacement(); | 451 const bool supports_replacement = element->url()->SupportsReplacement(); |
403 | 452 |
404 // Create an edit entry of "[keyword] [remaining input]". This is helpful | 453 // Create an edit entry of "[keyword] [remaining input]". This is helpful |
405 // even when [remaining input] is empty, as the user can select the popup | 454 // even when [remaining input] is empty, as the user can select the popup |
406 // choice and immediately begin typing in query input. | 455 // choice and immediately begin typing in query input. |
407 const bool keyword_complete = (prefix_length == keyword.length()); | 456 const bool keyword_complete = (prefix_length == keyword.length()); |
408 if (relevance < 0) { | 457 if (relevance < 0) { |
409 relevance = | 458 relevance = |
410 CalculateRelevance(input.type(), keyword_complete, | 459 CalculateRelevance(input.type(), keyword_complete, |
411 // When the user wants keyword matches to take | 460 // When the user wants keyword matches to take |
412 // preference, score them highly regardless of | 461 // preference, score them highly regardless of |
413 // whether the input provides query text. | 462 // whether the input provides query text. |
414 supports_replacement, input.prefer_keyword(), | 463 supports_replacement, input.prefer_keyword(), |
415 input.allow_exact_keyword_match()); | 464 input.allow_exact_keyword_match()); |
416 } | 465 } |
417 AutocompleteMatch result(this, relevance, false, | 466 AutocompleteMatch match(this, relevance, false, |
418 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : | 467 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : |
419 AutocompleteMatch::HISTORY_KEYWORD); | 468 AutocompleteMatch::HISTORY_KEYWORD); |
420 result.fill_into_edit.assign(keyword); | 469 match.fill_into_edit.assign(keyword); |
421 if (!remaining_input.empty() || !keyword_complete || supports_replacement) | 470 if (!remaining_input.empty() || !keyword_complete || supports_replacement) |
422 result.fill_into_edit.push_back(L' '); | 471 match.fill_into_edit.push_back(L' '); |
423 result.fill_into_edit.append(remaining_input); | 472 match.fill_into_edit.append(remaining_input); |
424 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd | 473 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd |
425 // need CleanUserInputKeyword() to return the amount of adjustment it's made | 474 // need CleanUserInputKeyword() to return the amount of adjustment it's made |
426 // to the user's input. Because right now inexact keyword matches can't score | 475 // to the user's input. Because right now inexact keyword matches can't score |
427 // more highly than a "what you typed" match from one of the other providers, | 476 // more highly than a "what you typed" match from one of the other providers, |
428 // we just don't bother to do this, and leave inline autocompletion off. | 477 // we just don't bother to do this, and leave inline autocompletion off. |
429 result.inline_autocomplete_offset = string16::npos; | 478 match.inline_autocomplete_offset = string16::npos; |
430 | 479 |
431 // Create destination URL and popup entry content by substituting user input | 480 // Create destination URL and popup entry content by substituting user input |
432 // into keyword templates. | 481 // into keyword templates. |
433 FillInURLAndContents(remaining_input, element, &result); | 482 FillInURLAndContents(remaining_input, element, &match); |
434 | 483 |
435 if (supports_replacement) | 484 if (supports_replacement) |
436 result.template_url = element; | 485 match.template_url = element; |
437 result.transition = PageTransition::KEYWORD; | 486 match.keyword = keyword; |
487 match.transition = PageTransition::KEYWORD; | |
438 | 488 |
439 return result; | 489 return match; |
440 } | 490 } |
441 | 491 |
442 void KeywordProvider::Observe(int type, | 492 void KeywordProvider::Observe(int type, |
443 const NotificationSource& source, | 493 const NotificationSource& source, |
444 const NotificationDetails& details) { | 494 const NotificationDetails& details) { |
445 TemplateURLService* model = | 495 TemplateURLService* model = GetTemplateURLService(); |
446 profile_ ? TemplateURLServiceFactory::GetForProfile(profile_) : model_; | |
447 const AutocompleteInput& input = extension_suggest_last_input_; | 496 const AutocompleteInput& input = extension_suggest_last_input_; |
448 | 497 |
449 switch (type) { | 498 switch (type) { |
450 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: | 499 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: |
451 // Input has been accepted, so we're done with this input session. Ensure | 500 // Input has been accepted, so we're done with this input session. Ensure |
452 // we don't send the OnInputCancelled event, or handle any more stray | 501 // we don't send the OnInputCancelled event, or handle any more stray |
453 // suggestions_ready events. | 502 // suggestions_ready events. |
454 current_keyword_extension_id_.clear(); | 503 current_keyword_extension_id_.clear(); |
455 current_input_id_ = 0; | 504 current_input_id_ = 0; |
456 return; | 505 return; |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
530 } | 579 } |
531 | 580 |
532 void KeywordProvider::MaybeEndExtensionKeywordMode() { | 581 void KeywordProvider::MaybeEndExtensionKeywordMode() { |
533 if (!current_keyword_extension_id_.empty()) { | 582 if (!current_keyword_extension_id_.empty()) { |
534 ExtensionOmniboxEventRouter::OnInputCancelled( | 583 ExtensionOmniboxEventRouter::OnInputCancelled( |
535 profile_, current_keyword_extension_id_); | 584 profile_, current_keyword_extension_id_); |
536 | 585 |
537 current_keyword_extension_id_.clear(); | 586 current_keyword_extension_id_.clear(); |
538 } | 587 } |
539 } | 588 } |
OLD | NEW |