Chromium Code Reviews| 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 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 string16* remaining_input) { | 113 string16* remaining_input) { |
| 114 if (!input.allow_exact_keyword_match()) | 114 if (!input.allow_exact_keyword_match()) |
| 115 return NULL; | 115 return NULL; |
| 116 | 116 |
| 117 string16 keyword; | 117 string16 keyword; |
| 118 if (!ExtractKeywordFromInput(input, &keyword, remaining_input)) | 118 if (!ExtractKeywordFromInput(input, &keyword, remaining_input)) |
| 119 return NULL; | 119 return NULL; |
| 120 | 120 |
| 121 // 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 |
| 122 // the model is already loaded. | 122 // the model is already loaded. |
| 123 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile); | 123 TemplateURLService* model = TemplateURLServiceFactory::GetForProfile(profile); |
|
Peter Kasting
2011/08/04 20:07:53
Nit: I wonder if there is a good way to make use o
aaron.randolph
2011/08/04 21:02:50
I can make the helper take a Profile as an argumen
aaron.randolph
2011/08/04 21:22:14
Though to do that, the |model_| check would need t
Peter Kasting
2011/08/04 21:31:42
No, that's precisely what I don't want. That's wh
| |
| 124 DCHECK(model); | 124 DCHECK(model); |
| 125 model->Load(); | 125 model->Load(); |
| 126 | 126 |
| 127 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); | 127 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); |
| 128 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL; | 128 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL; |
| 129 } | 129 } |
| 130 | 130 |
| 131 string16 KeywordProvider::GetKeywordForText( | |
| 132 const string16& text) const { | |
| 133 const string16 keyword(TemplateURLService::CleanUserInputKeyword(text)); | |
| 134 | |
| 135 if (keyword.empty()) | |
| 136 return keyword; | |
| 137 | |
| 138 TemplateURLService* url_service = GetTemplateURLService(); | |
| 139 if (!url_service) | |
| 140 return string16(); | |
| 141 url_service->Load(); | |
| 142 | |
| 143 // Don't provide a keyword if it doesn't support replacement. | |
| 144 const TemplateURL* const template_url = | |
| 145 url_service->GetTemplateURLForKeyword(keyword); | |
| 146 if (!TemplateURL::SupportsReplacement(template_url)) | |
| 147 return string16(); | |
| 148 | |
| 149 // Don't provide a keyword for inactive/disabled extension keywords. | |
| 150 if (template_url->IsExtensionKeyword()) { | |
| 151 const Extension* extension = profile_->GetExtensionService()-> | |
| 152 GetExtensionById(template_url->GetExtensionId(), false); | |
| 153 if (!extension || | |
| 154 (profile_->IsOffTheRecord() && | |
| 155 !profile_->GetExtensionService()->IsIncognitoEnabled(extension->id()))) | |
| 156 return string16(); | |
| 157 } | |
| 158 | |
| 159 return keyword; | |
| 160 } | |
| 161 | |
| 162 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | |
| 163 const string16& keyword, | |
| 164 const AutocompleteInput& input) { | |
| 165 return CreateAutocompleteMatch(GetTemplateURLService(), keyword, input, | |
| 166 keyword.size(), string16(), 0); | |
| 167 } | |
| 168 | |
| 131 void KeywordProvider::Start(const AutocompleteInput& input, | 169 void KeywordProvider::Start(const AutocompleteInput& input, |
| 132 bool minimal_changes) { | 170 bool minimal_changes) { |
| 133 // This object ensures we end keyword mode if we exit the function without | 171 // This object ensures we end keyword mode if we exit the function without |
| 134 // toggling keyword mode to on. | 172 // toggling keyword mode to on. |
| 135 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); | 173 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); |
| 136 | 174 |
| 137 matches_.clear(); | 175 matches_.clear(); |
| 138 | 176 |
| 139 if (!minimal_changes) { | 177 if (!minimal_changes) { |
| 140 done_ = true; | 178 done_ = true; |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 154 // whatever we do here! | 192 // whatever we do here! |
| 155 // | 193 // |
| 156 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for | 194 // 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, | 195 // 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 | 196 // 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. | 197 // this case we'd consider all input here to be query input. |
| 160 string16 keyword, remaining_input; | 198 string16 keyword, remaining_input; |
| 161 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) | 199 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) |
| 162 return; | 200 return; |
| 163 | 201 |
| 164 // Make sure the model is loaded. This is cheap and quickly bails out if | 202 TemplateURLService* model = GetTemplateURLService(); |
| 165 // the model is already loaded. | |
| 166 TemplateURLService* model = | |
| 167 profile_ ? | |
| 168 TemplateURLServiceFactory::GetForProfile(profile_) : | |
| 169 model_; | |
| 170 DCHECK(model); | |
| 171 model->Load(); | |
| 172 | 203 |
| 173 // Get the best matches for this keyword. | 204 // Get the best matches for this keyword. |
| 174 // | 205 // |
| 175 // NOTE: We could cache the previous keywords and reuse them here in the | 206 // 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 | 207 // |minimal_changes| case, but since we'd still have to recalculate their |
| 177 // relevances and we can just recreate the results synchronously anyway, we | 208 // relevances and we can just recreate the results synchronously anyway, we |
| 178 // don't bother. | 209 // don't bother. |
| 179 // | 210 // |
| 180 // TODO(pkasting): http://b/893701 We should remember the user's use of a | 211 // 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 | 212 // search query both from the autocomplete popup and from web pages |
| 182 // themselves. | 213 // themselves. |
| 183 std::vector<string16> keyword_matches; | 214 std::vector<string16> keyword_matches; |
| 184 model->FindMatchingKeywords(keyword, | 215 model->FindMatchingKeywords(keyword, |
| 185 !remaining_input.empty(), | 216 !remaining_input.empty(), |
| 186 &keyword_matches); | 217 &keyword_matches); |
| 187 | 218 |
| 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()); | 219 for (std::vector<string16>::iterator i(keyword_matches.begin()); |
| 191 i != keyword_matches.end(); ) { | 220 i != keyword_matches.end(); ) { |
| 192 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); | 221 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); |
| 222 | |
| 223 // Prune any extension keywords that are disallowed in incognito mode (if | |
| 224 // we're incognito), or disabled. | |
| 193 if (profile_ && | 225 if (profile_ && |
| 194 input.matches_requested() == AutocompleteInput::ALL_MATCHES && | 226 input.matches_requested() == AutocompleteInput::ALL_MATCHES && |
| 195 template_url->IsExtensionKeyword()) { | 227 template_url->IsExtensionKeyword()) { |
| 196 ExtensionService* service = profile_->GetExtensionService(); | 228 ExtensionService* service = profile_->GetExtensionService(); |
| 197 const Extension* extension = service->GetExtensionById( | 229 const Extension* extension = service->GetExtensionById( |
| 198 template_url->GetExtensionId(), false); | 230 template_url->GetExtensionId(), false); |
| 199 bool enabled = | 231 bool enabled = |
| 200 extension && (!profile_->IsOffTheRecord() || | 232 extension && (!profile_->IsOffTheRecord() || |
| 201 service->IsIncognitoEnabled(extension->id())); | 233 service->IsIncognitoEnabled(extension->id())); |
| 202 if (!enabled) { | 234 if (!enabled) { |
| 203 i = keyword_matches.erase(i); | 235 i = keyword_matches.erase(i); |
| 204 continue; | 236 continue; |
| 205 } | 237 } |
| 206 } | 238 } |
| 239 | |
| 240 // Prune any substituting keywords if there is no substitution. | |
| 241 if (TemplateURL::SupportsReplacement(template_url) && | |
| 242 !input.allow_exact_keyword_match()) { | |
| 243 i = keyword_matches.erase(i); | |
| 244 continue; | |
| 245 } | |
| 246 | |
| 207 ++i; | 247 ++i; |
| 208 } | 248 } |
| 209 if (keyword_matches.empty()) | 249 if (keyword_matches.empty()) |
| 210 return; | 250 return; |
| 211 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); | 251 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); |
| 212 | 252 |
| 213 // Limit to one exact or three inexact matches, and mark them up for display | 253 // Limit to one exact or three inexact matches, and mark them up for display |
| 214 // in the autocomplete popup. | 254 // in the autocomplete popup. |
| 215 // Any exact match is going to be the highest quality match, and thus at the | 255 // Any exact match is going to be the highest quality match, and thus at the |
| 216 // front of our vector. | 256 // front of our vector. |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 407 const bool keyword_complete = (prefix_length == keyword.length()); | 447 const bool keyword_complete = (prefix_length == keyword.length()); |
| 408 if (relevance < 0) { | 448 if (relevance < 0) { |
| 409 relevance = | 449 relevance = |
| 410 CalculateRelevance(input.type(), keyword_complete, | 450 CalculateRelevance(input.type(), keyword_complete, |
| 411 // When the user wants keyword matches to take | 451 // When the user wants keyword matches to take |
| 412 // preference, score them highly regardless of | 452 // preference, score them highly regardless of |
| 413 // whether the input provides query text. | 453 // whether the input provides query text. |
| 414 supports_replacement, input.prefer_keyword(), | 454 supports_replacement, input.prefer_keyword(), |
| 415 input.allow_exact_keyword_match()); | 455 input.allow_exact_keyword_match()); |
| 416 } | 456 } |
| 417 AutocompleteMatch result(this, relevance, false, | 457 AutocompleteMatch match(this, relevance, false, |
| 418 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : | 458 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : |
| 419 AutocompleteMatch::HISTORY_KEYWORD); | 459 AutocompleteMatch::HISTORY_KEYWORD); |
| 420 result.fill_into_edit.assign(keyword); | 460 match.fill_into_edit.assign(keyword); |
| 421 if (!remaining_input.empty() || !keyword_complete || supports_replacement) | 461 if (!remaining_input.empty() || !keyword_complete || supports_replacement) |
| 422 result.fill_into_edit.push_back(L' '); | 462 match.fill_into_edit.push_back(L' '); |
| 423 result.fill_into_edit.append(remaining_input); | 463 match.fill_into_edit.append(remaining_input); |
| 424 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd | 464 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd |
| 425 // need CleanUserInputKeyword() to return the amount of adjustment it's made | 465 // 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 | 466 // 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, | 467 // 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. | 468 // we just don't bother to do this, and leave inline autocompletion off. |
| 429 result.inline_autocomplete_offset = string16::npos; | 469 match.inline_autocomplete_offset = string16::npos; |
| 430 | 470 |
| 431 // Create destination URL and popup entry content by substituting user input | 471 // Create destination URL and popup entry content by substituting user input |
| 432 // into keyword templates. | 472 // into keyword templates. |
| 433 FillInURLAndContents(remaining_input, element, &result); | 473 FillInURLAndContents(remaining_input, element, &match); |
| 434 | 474 |
| 435 if (supports_replacement) | 475 if (supports_replacement) |
| 436 result.template_url = element; | 476 match.template_url = element; |
| 437 result.transition = PageTransition::KEYWORD; | 477 match.keyword = keyword; |
| 478 match.transition = PageTransition::KEYWORD; | |
| 438 | 479 |
| 439 return result; | 480 return match; |
| 440 } | 481 } |
| 441 | 482 |
| 442 void KeywordProvider::Observe(int type, | 483 void KeywordProvider::Observe(int type, |
| 443 const NotificationSource& source, | 484 const NotificationSource& source, |
| 444 const NotificationDetails& details) { | 485 const NotificationDetails& details) { |
| 445 TemplateURLService* model = | 486 TemplateURLService* model = GetTemplateURLService(); |
| 446 profile_ ? TemplateURLServiceFactory::GetForProfile(profile_) : model_; | |
| 447 const AutocompleteInput& input = extension_suggest_last_input_; | 487 const AutocompleteInput& input = extension_suggest_last_input_; |
| 448 | 488 |
| 449 switch (type) { | 489 switch (type) { |
| 450 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: | 490 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: |
| 451 // Input has been accepted, so we're done with this input session. Ensure | 491 // 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 | 492 // we don't send the OnInputCancelled event, or handle any more stray |
| 453 // suggestions_ready events. | 493 // suggestions_ready events. |
| 454 current_keyword_extension_id_.clear(); | 494 current_keyword_extension_id_.clear(); |
| 455 current_input_id_ = 0; | 495 current_input_id_ = 0; |
| 456 return; | 496 return; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 513 listener_->OnProviderUpdate(!extension_suggest_matches_.empty()); | 553 listener_->OnProviderUpdate(!extension_suggest_matches_.empty()); |
| 514 return; | 554 return; |
| 515 } | 555 } |
| 516 | 556 |
| 517 default: | 557 default: |
| 518 NOTREACHED(); | 558 NOTREACHED(); |
| 519 return; | 559 return; |
| 520 } | 560 } |
| 521 } | 561 } |
| 522 | 562 |
| 563 TemplateURLService* KeywordProvider::GetTemplateURLService() const { | |
| 564 TemplateURLService* service = profile_ ? | |
| 565 TemplateURLServiceFactory::GetForProfile(profile_) : model_; | |
| 566 // Make sure the model is loaded. This is cheap and quickly bails out if | |
| 567 // the model is already loaded. | |
| 568 DCHECK(service); | |
| 569 service->Load(); | |
| 570 return service; | |
| 571 } | |
| 572 | |
| 523 void KeywordProvider::EnterExtensionKeywordMode( | 573 void KeywordProvider::EnterExtensionKeywordMode( |
| 524 const std::string& extension_id) { | 574 const std::string& extension_id) { |
| 525 DCHECK(current_keyword_extension_id_.empty()); | 575 DCHECK(current_keyword_extension_id_.empty()); |
| 526 current_keyword_extension_id_ = extension_id; | 576 current_keyword_extension_id_ = extension_id; |
| 527 | 577 |
| 528 ExtensionOmniboxEventRouter::OnInputStarted( | 578 ExtensionOmniboxEventRouter::OnInputStarted( |
| 529 profile_, current_keyword_extension_id_); | 579 profile_, current_keyword_extension_id_); |
| 530 } | 580 } |
| 531 | 581 |
| 532 void KeywordProvider::MaybeEndExtensionKeywordMode() { | 582 void KeywordProvider::MaybeEndExtensionKeywordMode() { |
| 533 if (!current_keyword_extension_id_.empty()) { | 583 if (!current_keyword_extension_id_.empty()) { |
| 534 ExtensionOmniboxEventRouter::OnInputCancelled( | 584 ExtensionOmniboxEventRouter::OnInputCancelled( |
| 535 profile_, current_keyword_extension_id_); | 585 profile_, current_keyword_extension_id_); |
| 536 | 586 |
| 537 current_keyword_extension_id_.clear(); | 587 current_keyword_extension_id_.clear(); |
| 538 } | 588 } |
| 539 } | 589 } |
| OLD | NEW |