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