OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/strings/string16.h" | 10 #include "base/strings/string16.h" |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 // Helper functor for Start(), for sorting keyword matches by quality. | 79 // Helper functor for Start(), for sorting keyword matches by quality. |
80 class CompareQuality { | 80 class CompareQuality { |
81 public: | 81 public: |
82 // A keyword is of higher quality when a greater fraction of it has been | 82 // A keyword is of higher quality when a greater fraction of it has been |
83 // typed, that is, when it is shorter. | 83 // typed, that is, when it is shorter. |
84 // | 84 // |
85 // TODO(pkasting): http://b/740691 Most recent and most frequent keywords are | 85 // TODO(pkasting): http://b/740691 Most recent and most frequent keywords are |
86 // probably better rankings than the fraction of the keyword typed. We should | 86 // probably better rankings than the fraction of the keyword typed. We should |
87 // always put any exact matches first no matter what, since the code in | 87 // always put any exact matches first no matter what, since the code in |
88 // Start() assumes this (and it makes sense). | 88 // Start() assumes this (and it makes sense). |
89 bool operator()(const string16& keyword1, | 89 bool operator()(const TemplateURL* t_url1, const TemplateURL* t_url2) const { |
90 const string16& keyword2) const { | 90 return t_url1->keyword().length() < t_url2->keyword().length(); |
91 return keyword1.length() < keyword2.length(); | |
92 } | 91 } |
93 }; | 92 }; |
94 | 93 |
95 // We need our input IDs to be unique across all profiles, so we keep a global | 94 // We need our input IDs to be unique across all profiles, so we keep a global |
96 // UID that each provider uses. | 95 // UID that each provider uses. |
97 static int global_input_uid_; | 96 static int global_input_uid_; |
98 | 97 |
99 } // namespace | 98 } // namespace |
100 | 99 |
101 // static | 100 // static |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 return string16(); | 208 return string16(); |
210 } | 209 } |
211 | 210 |
212 return keyword; | 211 return keyword; |
213 } | 212 } |
214 | 213 |
215 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | 214 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( |
216 const string16& text, | 215 const string16& text, |
217 const string16& keyword, | 216 const string16& keyword, |
218 const AutocompleteInput& input) { | 217 const AutocompleteInput& input) { |
219 return CreateAutocompleteMatch(GetTemplateURLService(), keyword, input, | 218 return CreateAutocompleteMatch( |
220 keyword.size(), SplitReplacementStringFromInput(text, true), 0); | 219 GetTemplateURLService()->GetTemplateURLForKeyword(keyword), input, |
| 220 keyword.length(), SplitReplacementStringFromInput(text, true), 0); |
221 } | 221 } |
222 | 222 |
223 void KeywordProvider::Start(const AutocompleteInput& input, | 223 void KeywordProvider::Start(const AutocompleteInput& input, |
224 bool minimal_changes) { | 224 bool minimal_changes) { |
225 // This object ensures we end keyword mode if we exit the function without | 225 // This object ensures we end keyword mode if we exit the function without |
226 // toggling keyword mode to on. | 226 // toggling keyword mode to on. |
227 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); | 227 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); |
228 | 228 |
229 matches_.clear(); | 229 matches_.clear(); |
230 | 230 |
(...skipping 15 matching lines...) Expand all Loading... |
246 // whatever we do here! | 246 // whatever we do here! |
247 // | 247 // |
248 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for | 248 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for |
249 // keywords, we might suggest keywords that haven't even been partially typed, | 249 // keywords, we might suggest keywords that haven't even been partially typed, |
250 // if the user uses them enough and isn't obviously typing something else. In | 250 // if the user uses them enough and isn't obviously typing something else. In |
251 // this case we'd consider all input here to be query input. | 251 // this case we'd consider all input here to be query input. |
252 string16 keyword, remaining_input; | 252 string16 keyword, remaining_input; |
253 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) | 253 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) |
254 return; | 254 return; |
255 | 255 |
256 TemplateURLService* model = GetTemplateURLService(); | |
257 | |
258 // Get the best matches for this keyword. | 256 // Get the best matches for this keyword. |
259 // | 257 // |
260 // NOTE: We could cache the previous keywords and reuse them here in the | 258 // NOTE: We could cache the previous keywords and reuse them here in the |
261 // |minimal_changes| case, but since we'd still have to recalculate their | 259 // |minimal_changes| case, but since we'd still have to recalculate their |
262 // relevances and we can just recreate the results synchronously anyway, we | 260 // relevances and we can just recreate the results synchronously anyway, we |
263 // don't bother. | 261 // don't bother. |
264 // | 262 // |
265 // TODO(pkasting): http://b/893701 We should remember the user's use of a | 263 // TODO(pkasting): http://b/893701 We should remember the user's use of a |
266 // search query both from the autocomplete popup and from web pages | 264 // search query both from the autocomplete popup and from web pages |
267 // themselves. | 265 // themselves. |
268 std::vector<string16> keyword_matches; | 266 TemplateURLService::TemplateURLVector matches; |
269 model->FindMatchingKeywords(keyword, | 267 GetTemplateURLService()->FindMatchingKeywords( |
270 !remaining_input.empty(), | 268 keyword, !remaining_input.empty(), &matches); |
271 &keyword_matches); | |
272 | 269 |
273 for (std::vector<string16>::iterator i(keyword_matches.begin()); | 270 for (TemplateURLService::TemplateURLVector::iterator i(matches.begin()); |
274 i != keyword_matches.end(); ) { | 271 i != matches.end(); ) { |
275 const TemplateURL* template_url = model->GetTemplateURLForKeyword(*i); | |
276 | |
277 // Prune any extension keywords that are disallowed in incognito mode (if | 272 // Prune any extension keywords that are disallowed in incognito mode (if |
278 // we're incognito), or disabled. | 273 // we're incognito), or disabled. |
279 if (profile_ && template_url->IsExtensionKeyword()) { | 274 if (profile_ && (*i)->IsExtensionKeyword()) { |
280 ExtensionService* service = extensions::ExtensionSystem::Get(profile_)-> | 275 ExtensionService* service = extensions::ExtensionSystem::Get(profile_)-> |
281 extension_service(); | 276 extension_service(); |
282 const extensions::Extension* extension = service->GetExtensionById( | 277 const extensions::Extension* extension = |
283 template_url->GetExtensionId(), false); | 278 service->GetExtensionById((*i)->GetExtensionId(), false); |
284 bool enabled = | 279 bool enabled = |
285 extension && (!profile_->IsOffTheRecord() || | 280 extension && (!profile_->IsOffTheRecord() || |
286 service->IsIncognitoEnabled(extension->id())); | 281 service->IsIncognitoEnabled(extension->id())); |
287 if (!enabled) { | 282 if (!enabled) { |
288 i = keyword_matches.erase(i); | 283 i = matches.erase(i); |
289 continue; | 284 continue; |
290 } | 285 } |
291 } | 286 } |
292 | 287 |
293 // Prune any substituting keywords if there is no substitution. | 288 // Prune any substituting keywords if there is no substitution. |
294 if (template_url->SupportsReplacement() && remaining_input.empty() && | 289 if ((*i)->SupportsReplacement() && remaining_input.empty() && |
295 !input.allow_exact_keyword_match()) { | 290 !input.allow_exact_keyword_match()) { |
296 i = keyword_matches.erase(i); | 291 i = matches.erase(i); |
297 continue; | 292 continue; |
298 } | 293 } |
299 | 294 |
300 ++i; | 295 ++i; |
301 } | 296 } |
302 if (keyword_matches.empty()) | 297 if (matches.empty()) |
303 return; | 298 return; |
304 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); | 299 std::sort(matches.begin(), matches.end(), CompareQuality()); |
305 | 300 |
306 // Limit to one exact or three inexact matches, and mark them up for display | 301 // Limit to one exact or three inexact matches, and mark them up for display |
307 // in the autocomplete popup. | 302 // in the autocomplete popup. |
308 // Any exact match is going to be the highest quality match, and thus at the | 303 // Any exact match is going to be the highest quality match, and thus at the |
309 // front of our vector. | 304 // front of our vector. |
310 if (keyword_matches.front() == keyword) { | 305 if (matches.front()->keyword() == keyword) { |
311 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); | 306 const TemplateURL* template_url = matches.front(); |
312 const bool is_extension_keyword = template_url->IsExtensionKeyword(); | 307 const bool is_extension_keyword = template_url->IsExtensionKeyword(); |
313 | 308 |
314 // Only create an exact match if |remaining_input| is empty or if | 309 // Only create an exact match if |remaining_input| is empty or if |
315 // this is an extension keyword. If |remaining_input| is a | 310 // this is an extension keyword. If |remaining_input| is a |
316 // non-empty non-extension keyword (i.e., a regular keyword that | 311 // non-empty non-extension keyword (i.e., a regular keyword that |
317 // supports replacement and that has extra text following it), | 312 // supports replacement and that has extra text following it), |
318 // then SearchProvider creates the exact (a.k.a. verbatim) match. | 313 // then SearchProvider creates the exact (a.k.a. verbatim) match. |
319 if (!remaining_input.empty() && !is_extension_keyword) | 314 if (!remaining_input.empty() && !is_extension_keyword) |
320 return; | 315 return; |
321 | 316 |
322 // TODO(pkasting): We should probably check that if the user explicitly | 317 // TODO(pkasting): We should probably check that if the user explicitly |
323 // typed a scheme, that scheme matches the one in |template_url|. | 318 // typed a scheme, that scheme matches the one in |template_url|. |
324 matches_.push_back(CreateAutocompleteMatch(model, keyword, input, | 319 matches_.push_back(CreateAutocompleteMatch( |
325 keyword.length(), | 320 template_url, input, keyword.length(), remaining_input, -1)); |
326 remaining_input, -1)); | |
327 | 321 |
328 if (profile_ && is_extension_keyword) { | 322 if (profile_ && is_extension_keyword) { |
329 if (input.matches_requested() == AutocompleteInput::ALL_MATCHES) { | 323 if (input.matches_requested() == AutocompleteInput::ALL_MATCHES) { |
330 if (template_url->GetExtensionId() != current_keyword_extension_id_) | 324 if (template_url->GetExtensionId() != current_keyword_extension_id_) |
331 MaybeEndExtensionKeywordMode(); | 325 MaybeEndExtensionKeywordMode(); |
332 if (current_keyword_extension_id_.empty()) | 326 if (current_keyword_extension_id_.empty()) |
333 EnterExtensionKeywordMode(template_url->GetExtensionId()); | 327 EnterExtensionKeywordMode(template_url->GetExtensionId()); |
334 keyword_mode_toggle.StayInKeywordMode(); | 328 keyword_mode_toggle.StayInKeywordMode(); |
335 } | 329 } |
336 | 330 |
(...skipping 20 matching lines...) Expand all Loading... |
357 profile_, template_url->GetExtensionId(), | 351 profile_, template_url->GetExtensionId(), |
358 UTF16ToUTF8(remaining_input), current_input_id_); | 352 UTF16ToUTF8(remaining_input), current_input_id_); |
359 | 353 |
360 // We only have to wait for suggest results if there are actually | 354 // We only have to wait for suggest results if there are actually |
361 // extensions listening for input changes. | 355 // extensions listening for input changes. |
362 if (have_listeners) | 356 if (have_listeners) |
363 done_ = false; | 357 done_ = false; |
364 } | 358 } |
365 } | 359 } |
366 } else { | 360 } else { |
367 if (keyword_matches.size() > kMaxMatches) { | 361 if (matches.size() > kMaxMatches) |
368 keyword_matches.erase(keyword_matches.begin() + kMaxMatches, | 362 matches.erase(matches.begin() + kMaxMatches, matches.end()); |
369 keyword_matches.end()); | 363 for (TemplateURLService::TemplateURLVector::const_iterator i( |
370 } | 364 matches.begin()); i != matches.end(); ++i) { |
371 for (std::vector<string16>::const_iterator i(keyword_matches.begin()); | 365 matches_.push_back(CreateAutocompleteMatch( |
372 i != keyword_matches.end(); ++i) { | 366 *i, input, keyword.length(), remaining_input, -1)); |
373 matches_.push_back(CreateAutocompleteMatch(model, *i, | |
374 input, keyword.length(), | |
375 remaining_input, -1)); | |
376 } | 367 } |
377 } | 368 } |
378 } | 369 } |
379 | 370 |
380 void KeywordProvider::Stop(bool clear_cached_results) { | 371 void KeywordProvider::Stop(bool clear_cached_results) { |
381 done_ = true; | 372 done_ = true; |
382 MaybeEndExtensionKeywordMode(); | 373 MaybeEndExtensionKeywordMode(); |
383 } | 374 } |
384 | 375 |
385 KeywordProvider::~KeywordProvider() {} | 376 KeywordProvider::~KeywordProvider() {} |
386 | 377 |
387 // static | 378 // static |
388 bool KeywordProvider::ExtractKeywordFromInput(const AutocompleteInput& input, | 379 bool KeywordProvider::ExtractKeywordFromInput(const AutocompleteInput& input, |
389 string16* keyword, | 380 string16* keyword, |
390 string16* remaining_input) { | 381 string16* remaining_input) { |
391 if ((input.type() == AutocompleteInput::INVALID) || | 382 if ((input.type() == AutocompleteInput::INVALID) || |
392 (input.type() == AutocompleteInput::FORCED_QUERY)) | 383 (input.type() == AutocompleteInput::FORCED_QUERY)) |
393 return false; | 384 return false; |
394 | 385 |
395 string16 trimmed_input; | 386 string16 trimmed_input; |
396 TrimWhitespace(input.text(), TRIM_TRAILING, &trimmed_input); | 387 TrimWhitespace(input.text(), TRIM_TRAILING, &trimmed_input); |
397 *keyword = TemplateURLService::CleanUserInputKeyword( | 388 *keyword = TemplateURLService::CleanUserInputKeyword( |
398 SplitKeywordFromInput(trimmed_input, true, remaining_input)); | 389 SplitKeywordFromInput(trimmed_input, true, remaining_input)); |
399 return !keyword->empty(); | 390 return !keyword->empty(); |
400 } | 391 } |
401 | 392 |
402 // static | 393 // static |
403 void KeywordProvider::FillInURLAndContents( | |
404 const string16& remaining_input, | |
405 const TemplateURL* element, | |
406 AutocompleteMatch* match) { | |
407 DCHECK(!element->short_name().empty()); | |
408 const TemplateURLRef& element_ref = element->url_ref(); | |
409 DCHECK(element_ref.IsValid()); | |
410 int message_id = element->IsExtensionKeyword() ? | |
411 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; | |
412 if (remaining_input.empty()) { | |
413 // Allow extension keyword providers to accept empty string input. This is | |
414 // useful to allow extensions to do something in the case where no input is | |
415 // entered. | |
416 if (element_ref.SupportsReplacement() && !element->IsExtensionKeyword()) { | |
417 // No query input; return a generic, no-destination placeholder. | |
418 match->contents.assign( | |
419 l10n_util::GetStringFUTF16(message_id, | |
420 element->AdjustedShortNameForLocaleDirection(), | |
421 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE))); | |
422 match->contents_class.push_back( | |
423 ACMatchClassification(0, ACMatchClassification::DIM)); | |
424 } else { | |
425 // Keyword that has no replacement text (aka a shorthand for a URL). | |
426 match->destination_url = GURL(element->url()); | |
427 match->contents.assign(element->short_name()); | |
428 AutocompleteMatch::ClassifyLocationInString(0, match->contents.length(), | |
429 match->contents.length(), ACMatchClassification::NONE, | |
430 &match->contents_class); | |
431 } | |
432 } else { | |
433 // Create destination URL by escaping user input and substituting into | |
434 // keyword template URL. The escaping here handles whitespace in user | |
435 // input, but we rely on later canonicalization functions to do more | |
436 // fixup to make the URL valid if necessary. | |
437 DCHECK(element_ref.SupportsReplacement()); | |
438 match->destination_url = GURL(element_ref.ReplaceSearchTerms( | |
439 TemplateURLRef::SearchTermsArgs(remaining_input))); | |
440 std::vector<size_t> content_param_offsets; | |
441 match->contents.assign(l10n_util::GetStringFUTF16(message_id, | |
442 element->short_name(), | |
443 remaining_input, | |
444 &content_param_offsets)); | |
445 if (content_param_offsets.size() == 2) { | |
446 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], | |
447 remaining_input.length(), match->contents.length(), | |
448 ACMatchClassification::NONE, &match->contents_class); | |
449 } else { | |
450 // See comments on an identical NOTREACHED() in search_provider.cc. | |
451 NOTREACHED(); | |
452 } | |
453 } | |
454 } | |
455 | |
456 // static | |
457 int KeywordProvider::CalculateRelevance(AutocompleteInput::Type type, | 394 int KeywordProvider::CalculateRelevance(AutocompleteInput::Type type, |
458 bool complete, | 395 bool complete, |
459 bool supports_replacement, | 396 bool supports_replacement, |
460 bool prefer_keyword, | 397 bool prefer_keyword, |
461 bool allow_exact_keyword_match) { | 398 bool allow_exact_keyword_match) { |
462 // This function is responsible for scoring suggestions of keywords | 399 // This function is responsible for scoring suggestions of keywords |
463 // themselves and the suggestion of the verbatim query on an | 400 // themselves and the suggestion of the verbatim query on an |
464 // extension keyword. SearchProvider::CalculateRelevanceForKeywordVerbatim() | 401 // extension keyword. SearchProvider::CalculateRelevanceForKeywordVerbatim() |
465 // scores verbatim query suggestions for non-extension keywords. | 402 // scores verbatim query suggestions for non-extension keywords. |
466 // These two functions are currently in sync, but there's no reason | 403 // These two functions are currently in sync, but there's no reason |
467 // we couldn't decide in the future to score verbatim matches | 404 // we couldn't decide in the future to score verbatim matches |
468 // differently for extension and non-extension keywords. If you | 405 // differently for extension and non-extension keywords. If you |
469 // make such a change, however, you should update this comment to | 406 // make such a change, however, you should update this comment to |
470 // describe it, so it's clear why the functions diverge. | 407 // describe it, so it's clear why the functions diverge. |
471 if (!complete) | 408 if (!complete) |
472 return (type == AutocompleteInput::URL) ? 700 : 450; | 409 return (type == AutocompleteInput::URL) ? 700 : 450; |
473 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword)) | 410 if (!supports_replacement || (allow_exact_keyword_match && prefer_keyword)) |
474 return 1500; | 411 return 1500; |
475 return (allow_exact_keyword_match && (type == AutocompleteInput::QUERY)) ? | 412 return (allow_exact_keyword_match && (type == AutocompleteInput::QUERY)) ? |
476 1450 : 1100; | 413 1450 : 1100; |
477 } | 414 } |
478 | 415 |
479 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | 416 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( |
480 TemplateURLService* model, | 417 const TemplateURL* template_url, |
481 const string16& keyword, | |
482 const AutocompleteInput& input, | 418 const AutocompleteInput& input, |
483 size_t prefix_length, | 419 size_t prefix_length, |
484 const string16& remaining_input, | 420 const string16& remaining_input, |
485 int relevance) { | 421 int relevance) { |
486 DCHECK(model); | 422 DCHECK(template_url); |
487 // Get keyword data from data store. | 423 const bool supports_replacement = |
488 TemplateURL* element = model->GetTemplateURLForKeyword(keyword); | 424 template_url->url_ref().SupportsReplacement(); |
489 DCHECK(element); | |
490 const bool supports_replacement = element->url_ref().SupportsReplacement(); | |
491 | 425 |
492 // Create an edit entry of "[keyword] [remaining input]". This is helpful | 426 // Create an edit entry of "[keyword] [remaining input]". This is helpful |
493 // even when [remaining input] is empty, as the user can select the popup | 427 // even when [remaining input] is empty, as the user can select the popup |
494 // choice and immediately begin typing in query input. | 428 // choice and immediately begin typing in query input. |
495 const bool keyword_complete = (prefix_length == keyword.length()); | 429 const bool keyword_complete = |
| 430 (prefix_length == template_url->keyword().length()); |
496 if (relevance < 0) { | 431 if (relevance < 0) { |
497 relevance = | 432 relevance = |
498 CalculateRelevance(input.type(), keyword_complete, | 433 CalculateRelevance(input.type(), keyword_complete, |
499 // When the user wants keyword matches to take | 434 // When the user wants keyword matches to take |
500 // preference, score them highly regardless of | 435 // preference, score them highly regardless of |
501 // whether the input provides query text. | 436 // whether the input provides query text. |
502 supports_replacement, input.prefer_keyword(), | 437 supports_replacement, input.prefer_keyword(), |
503 input.allow_exact_keyword_match()); | 438 input.allow_exact_keyword_match()); |
504 } | 439 } |
505 AutocompleteMatch match(this, relevance, false, | 440 AutocompleteMatch match(this, relevance, false, |
506 supports_replacement ? AutocompleteMatchType::SEARCH_OTHER_ENGINE : | 441 supports_replacement ? AutocompleteMatchType::SEARCH_OTHER_ENGINE : |
507 AutocompleteMatchType::HISTORY_KEYWORD); | 442 AutocompleteMatchType::HISTORY_KEYWORD); |
508 match.fill_into_edit.assign(keyword); | 443 match.fill_into_edit = template_url->keyword(); |
509 if (!remaining_input.empty() || !keyword_complete || supports_replacement) | 444 if (!remaining_input.empty() || !keyword_complete || supports_replacement) |
510 match.fill_into_edit.push_back(L' '); | 445 match.fill_into_edit.push_back(L' '); |
511 match.fill_into_edit.append(remaining_input); | 446 match.fill_into_edit.append(remaining_input); |
512 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd | 447 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd |
513 // need CleanUserInputKeyword() to return the amount of adjustment it's made | 448 // need CleanUserInputKeyword() to return the amount of adjustment it's made |
514 // to the user's input. Because right now inexact keyword matches can't score | 449 // to the user's input. Because right now inexact keyword matches can't score |
515 // more highly than a "what you typed" match from one of the other providers, | 450 // more highly than a "what you typed" match from one of the other providers, |
516 // we just don't bother to do this, and leave inline autocompletion off. | 451 // we just don't bother to do this, and leave inline autocompletion off. |
517 match.inline_autocomplete_offset = string16::npos; | 452 match.inline_autocomplete_offset = string16::npos; |
518 | 453 |
519 // Create destination URL and popup entry content by substituting user input | 454 // Create destination URL and popup entry content by substituting user input |
520 // into keyword templates. | 455 // into keyword templates. |
521 FillInURLAndContents(remaining_input, element, &match); | 456 FillInURLAndContents(remaining_input, template_url, &match); |
522 | 457 |
523 match.keyword = keyword; | 458 match.keyword = template_url->keyword(); |
524 match.transition = content::PAGE_TRANSITION_KEYWORD; | 459 match.transition = content::PAGE_TRANSITION_KEYWORD; |
525 | 460 |
526 return match; | 461 return match; |
527 } | 462 } |
528 | 463 |
| 464 void KeywordProvider::FillInURLAndContents(const string16& remaining_input, |
| 465 const TemplateURL* element, |
| 466 AutocompleteMatch* match) const { |
| 467 DCHECK(!element->short_name().empty()); |
| 468 const TemplateURLRef& element_ref = element->url_ref(); |
| 469 DCHECK(element_ref.IsValid()); |
| 470 int message_id = element->IsExtensionKeyword() ? |
| 471 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; |
| 472 if (remaining_input.empty()) { |
| 473 // Allow extension keyword providers to accept empty string input. This is |
| 474 // useful to allow extensions to do something in the case where no input is |
| 475 // entered. |
| 476 if (element_ref.SupportsReplacement() && !element->IsExtensionKeyword()) { |
| 477 // No query input; return a generic, no-destination placeholder. |
| 478 match->contents.assign( |
| 479 l10n_util::GetStringFUTF16(message_id, |
| 480 element->AdjustedShortNameForLocaleDirection(), |
| 481 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE))); |
| 482 match->contents_class.push_back( |
| 483 ACMatchClassification(0, ACMatchClassification::DIM)); |
| 484 } else { |
| 485 // Keyword that has no replacement text (aka a shorthand for a URL). |
| 486 match->destination_url = GURL(element->url()); |
| 487 match->contents.assign(element->short_name()); |
| 488 AutocompleteMatch::ClassifyLocationInString(0, match->contents.length(), |
| 489 match->contents.length(), ACMatchClassification::NONE, |
| 490 &match->contents_class); |
| 491 } |
| 492 } else { |
| 493 // Create destination URL by escaping user input and substituting into |
| 494 // keyword template URL. The escaping here handles whitespace in user |
| 495 // input, but we rely on later canonicalization functions to do more |
| 496 // fixup to make the URL valid if necessary. |
| 497 DCHECK(element_ref.SupportsReplacement()); |
| 498 TemplateURLRef::SearchTermsArgs search_terms_args(remaining_input); |
| 499 search_terms_args.append_extra_query_params = |
| 500 element == GetTemplateURLService()->GetDefaultSearchProvider(); |
| 501 match->destination_url = |
| 502 GURL(element_ref.ReplaceSearchTerms(search_terms_args)); |
| 503 std::vector<size_t> content_param_offsets; |
| 504 match->contents.assign(l10n_util::GetStringFUTF16(message_id, |
| 505 element->short_name(), |
| 506 remaining_input, |
| 507 &content_param_offsets)); |
| 508 if (content_param_offsets.size() == 2) { |
| 509 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], |
| 510 remaining_input.length(), match->contents.length(), |
| 511 ACMatchClassification::NONE, &match->contents_class); |
| 512 } else { |
| 513 // See comments on an identical NOTREACHED() in search_provider.cc. |
| 514 NOTREACHED(); |
| 515 } |
| 516 } |
| 517 } |
| 518 |
529 void KeywordProvider::Observe(int type, | 519 void KeywordProvider::Observe(int type, |
530 const content::NotificationSource& source, | 520 const content::NotificationSource& source, |
531 const content::NotificationDetails& details) { | 521 const content::NotificationDetails& details) { |
532 TemplateURLService* model = GetTemplateURLService(); | 522 TemplateURLService* model = GetTemplateURLService(); |
533 const AutocompleteInput& input = extension_suggest_last_input_; | 523 const AutocompleteInput& input = extension_suggest_last_input_; |
534 | 524 |
535 switch (type) { | 525 switch (type) { |
536 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: | 526 case chrome::NOTIFICATION_EXTENSION_OMNIBOX_INPUT_ENTERED: |
537 // Input has been accepted, so we're done with this input session. Ensure | 527 // Input has been accepted, so we're done with this input session. Ensure |
538 // we don't send the OnInputCancelled event, or handle any more stray | 528 // we don't send the OnInputCancelled event, or handle any more stray |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
578 const omnibox_api::SuggestResult& suggestion = | 568 const omnibox_api::SuggestResult& suggestion = |
579 *suggestions.suggest_results[i]; | 569 *suggestions.suggest_results[i]; |
580 // We want to order these suggestions in descending order, so start with | 570 // We want to order these suggestions in descending order, so start with |
581 // the relevance of the first result (added synchronously in Start()), | 571 // the relevance of the first result (added synchronously in Start()), |
582 // and subtract 1 for each subsequent suggestion from the extension. | 572 // and subtract 1 for each subsequent suggestion from the extension. |
583 // We know that |complete| is true, because we wouldn't get results from | 573 // We know that |complete| is true, because we wouldn't get results from |
584 // the extension unless the full keyword had been typed. | 574 // the extension unless the full keyword had been typed. |
585 int first_relevance = CalculateRelevance(input.type(), true, true, | 575 int first_relevance = CalculateRelevance(input.type(), true, true, |
586 input.prefer_keyword(), input.allow_exact_keyword_match()); | 576 input.prefer_keyword(), input.allow_exact_keyword_match()); |
587 extension_suggest_matches_.push_back(CreateAutocompleteMatch( | 577 extension_suggest_matches_.push_back(CreateAutocompleteMatch( |
588 model, keyword, input, keyword.length(), | 578 model->GetTemplateURLForKeyword(keyword), input, keyword.length(), |
589 UTF8ToUTF16(suggestion.content), first_relevance - (i + 1))); | 579 UTF8ToUTF16(suggestion.content), first_relevance - (i + 1))); |
590 | 580 |
591 AutocompleteMatch* match = &extension_suggest_matches_.back(); | 581 AutocompleteMatch* match = &extension_suggest_matches_.back(); |
592 match->contents.assign(UTF8ToUTF16(suggestion.description)); | 582 match->contents.assign(UTF8ToUTF16(suggestion.description)); |
593 match->contents_class = | 583 match->contents_class = |
594 extensions::StyleTypesToACMatchClassifications(suggestion); | 584 extensions::StyleTypesToACMatchClassifications(suggestion); |
595 match->description.clear(); | 585 match->description.clear(); |
596 match->description_class.clear(); | 586 match->description_class.clear(); |
597 } | 587 } |
598 | 588 |
(...skipping 30 matching lines...) Expand all Loading... |
629 } | 619 } |
630 | 620 |
631 void KeywordProvider::MaybeEndExtensionKeywordMode() { | 621 void KeywordProvider::MaybeEndExtensionKeywordMode() { |
632 if (!current_keyword_extension_id_.empty()) { | 622 if (!current_keyword_extension_id_.empty()) { |
633 extensions::ExtensionOmniboxEventRouter::OnInputCancelled( | 623 extensions::ExtensionOmniboxEventRouter::OnInputCancelled( |
634 profile_, current_keyword_extension_id_); | 624 profile_, current_keyword_extension_id_); |
635 | 625 |
636 current_keyword_extension_id_.clear(); | 626 current_keyword_extension_id_.clear(); |
637 } | 627 } |
638 } | 628 } |
OLD | NEW |