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