OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/base_search_provider.h" | 5 #include "chrome/browser/autocomplete/base_search_provider.h" |
6 | 6 |
| 7 #include "base/i18n/case_conversion.h" |
7 #include "base/json/json_string_value_serializer.h" | 8 #include "base/json/json_string_value_serializer.h" |
8 #include "base/prefs/pref_service.h" | 9 #include "base/prefs/pref_service.h" |
9 #include "base/strings/string_util.h" | 10 #include "base/strings/string_util.h" |
10 #include "base/strings/utf_string_conversions.h" | 11 #include "base/strings/utf_string_conversions.h" |
11 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" | 12 #include "chrome/browser/autocomplete/autocomplete_provider_listener.h" |
12 #include "chrome/browser/autocomplete/url_prefix.h" | 13 #include "chrome/browser/autocomplete/url_prefix.h" |
13 #include "chrome/browser/omnibox/omnibox_field_trial.h" | 14 #include "chrome/browser/omnibox/omnibox_field_trial.h" |
14 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/search/instant_service.h" |
| 17 #include "chrome/browser/search/instant_service_factory.h" |
| 18 #include "chrome/browser/search/search.h" |
15 #include "chrome/browser/search_engines/template_url.h" | 19 #include "chrome/browser/search_engines/template_url.h" |
16 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" | 20 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" |
17 #include "chrome/browser/sync/profile_sync_service.h" | 21 #include "chrome/browser/sync/profile_sync_service.h" |
18 #include "chrome/browser/sync/profile_sync_service_factory.h" | 22 #include "chrome/browser/sync/profile_sync_service_factory.h" |
19 #include "chrome/common/pref_names.h" | 23 #include "chrome/common/pref_names.h" |
20 #include "content/public/common/url_constants.h" | 24 #include "content/public/common/url_constants.h" |
21 #include "net/base/escape.h" | 25 #include "net/base/escape.h" |
22 #include "net/base/net_util.h" | 26 #include "net/base/net_util.h" |
23 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 27 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
24 #include "net/url_request/url_fetcher_delegate.h" | 28 #include "net/url_request/url_fetcher_delegate.h" |
25 #include "url/gurl.h" | 29 #include "url/gurl.h" |
26 | 30 |
27 // BaseSearchProvider --------------------------------------------------------- | 31 // BaseSearchProvider --------------------------------------------------------- |
28 | 32 |
29 BaseSearchProvider::BaseSearchProvider(AutocompleteProviderListener* listener, | 33 BaseSearchProvider::BaseSearchProvider(AutocompleteProviderListener* listener, |
30 Profile* profile, | 34 Profile* profile, |
31 AutocompleteProvider::Type type) | 35 AutocompleteProvider::Type type) |
32 : AutocompleteProvider(listener, profile, type), | 36 : AutocompleteProvider(listener, profile, type), |
33 field_trial_triggered_(false), | 37 field_trial_triggered_(false), |
34 field_trial_triggered_in_session_(false) {} | 38 field_trial_triggered_in_session_(false) {} |
35 | 39 |
| 40 // static |
| 41 bool BaseSearchProvider::ShouldPrefetch(const AutocompleteMatch& match) { |
| 42 return match.GetAdditionalInfo(kShouldPrefetchKey) == kTrue; |
| 43 } |
| 44 |
36 void BaseSearchProvider::AddProviderInfo(ProvidersInfo* provider_info) const { | 45 void BaseSearchProvider::AddProviderInfo(ProvidersInfo* provider_info) const { |
37 provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo()); | 46 provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo()); |
38 metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back(); | 47 metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back(); |
39 new_entry.set_provider(AsOmniboxEventProviderType()); | 48 new_entry.set_provider(AsOmniboxEventProviderType()); |
40 new_entry.set_provider_done(done_); | 49 new_entry.set_provider_done(done_); |
41 std::vector<uint32> field_trial_hashes; | 50 std::vector<uint32> field_trial_hashes; |
42 OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes); | 51 OmniboxFieldTrial::GetActiveSuggestFieldTrialHashes(&field_trial_hashes); |
43 for (size_t i = 0; i < field_trial_hashes.size(); ++i) { | 52 for (size_t i = 0; i < field_trial_hashes.size(); ++i) { |
44 if (field_trial_triggered_) | 53 if (field_trial_triggered_) |
45 new_entry.mutable_field_trial_triggered()->Add(field_trial_hashes[i]); | 54 new_entry.mutable_field_trial_triggered()->Add(field_trial_hashes[i]); |
46 if (field_trial_triggered_in_session_) { | 55 if (field_trial_triggered_in_session_) { |
47 new_entry.mutable_field_trial_triggered_in_session()->Add( | 56 new_entry.mutable_field_trial_triggered_in_session()->Add( |
48 field_trial_hashes[i]); | 57 field_trial_hashes[i]); |
49 } | 58 } |
50 } | 59 } |
51 } | 60 } |
52 | 61 |
| 62 // static |
| 63 const char BaseSearchProvider::kRelevanceFromServerKey[] = |
| 64 "relevance_from_server"; |
| 65 const char BaseSearchProvider::kShouldPrefetchKey[] = "should_prefetch"; |
| 66 const char BaseSearchProvider::kSuggestMetadataKey[] = "suggest_metadata"; |
| 67 const char BaseSearchProvider::kDeletionUrlKey[] = "deletion_url"; |
| 68 const char BaseSearchProvider::kTrue[] = "true"; |
| 69 const char BaseSearchProvider::kFalse[] = "false"; |
| 70 |
53 BaseSearchProvider::~BaseSearchProvider() {} | 71 BaseSearchProvider::~BaseSearchProvider() {} |
54 | 72 |
55 // BaseSearchProvider::Result -------------------------------------------------- | 73 // BaseSearchProvider::Result -------------------------------------------------- |
56 | 74 |
57 BaseSearchProvider::Result::Result(bool from_keyword_provider, | 75 BaseSearchProvider::Result::Result(bool from_keyword_provider, |
58 int relevance, | 76 int relevance, |
59 bool relevance_from_server) | 77 bool relevance_from_server) |
60 : from_keyword_provider_(from_keyword_provider), | 78 : from_keyword_provider_(from_keyword_provider), |
61 relevance_(relevance), | 79 relevance_(relevance), |
62 relevance_from_server_(relevance_from_server) {} | 80 relevance_from_server_(relevance_from_server) {} |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
251 | 269 |
252 return false; | 270 return false; |
253 } | 271 } |
254 | 272 |
255 // BaseSearchProvider --------------------------------------------------------- | 273 // BaseSearchProvider --------------------------------------------------------- |
256 | 274 |
257 // static | 275 // static |
258 AutocompleteMatch BaseSearchProvider::CreateSearchSuggestion( | 276 AutocompleteMatch BaseSearchProvider::CreateSearchSuggestion( |
259 AutocompleteProvider* autocomplete_provider, | 277 AutocompleteProvider* autocomplete_provider, |
260 const AutocompleteInput& input, | 278 const AutocompleteInput& input, |
261 const base::string16& input_text, | |
262 const SuggestResult& suggestion, | 279 const SuggestResult& suggestion, |
263 const TemplateURL* template_url, | 280 const TemplateURL* template_url, |
264 int accepted_suggestion, | 281 int accepted_suggestion, |
265 int omnibox_start_margin, | 282 int omnibox_start_margin, |
266 bool append_extra_query_params) { | 283 bool append_extra_query_params) { |
267 AutocompleteMatch match(autocomplete_provider, suggestion.relevance(), false, | 284 AutocompleteMatch match(autocomplete_provider, suggestion.relevance(), false, |
268 suggestion.type()); | 285 suggestion.type()); |
269 | 286 |
270 if (!template_url) | 287 if (!template_url) |
271 return match; | 288 return match; |
272 match.keyword = template_url->keyword(); | 289 match.keyword = template_url->keyword(); |
273 match.contents = suggestion.match_contents(); | 290 match.contents = suggestion.match_contents(); |
274 match.contents_class = suggestion.match_contents_class(); | 291 match.contents_class = suggestion.match_contents_class(); |
275 | 292 |
276 if (!suggestion.annotation().empty()) | 293 if (!suggestion.annotation().empty()) |
277 match.description = suggestion.annotation(); | 294 match.description = suggestion.annotation(); |
278 | 295 |
279 match.allowed_to_be_default_match = | 296 match.allowed_to_be_default_match = |
280 (input_text == suggestion.match_contents()); | 297 (input.text() == suggestion.match_contents()); |
281 | 298 |
282 // When the user forced a query, we need to make sure all the fill_into_edit | 299 // When the user forced a query, we need to make sure all the fill_into_edit |
283 // values preserve that property. Otherwise, if the user starts editing a | 300 // values preserve that property. Otherwise, if the user starts editing a |
284 // suggestion, non-Search results will suddenly appear. | 301 // suggestion, non-Search results will suddenly appear. |
285 if (input.type() == AutocompleteInput::FORCED_QUERY) | 302 if (input.type() == AutocompleteInput::FORCED_QUERY) |
286 match.fill_into_edit.assign(base::ASCIIToUTF16("?")); | 303 match.fill_into_edit.assign(base::ASCIIToUTF16("?")); |
287 if (suggestion.from_keyword_provider()) | 304 if (suggestion.from_keyword_provider()) |
288 match.fill_into_edit.append(match.keyword + base::char16(' ')); | 305 match.fill_into_edit.append(match.keyword + base::char16(' ')); |
289 if (!input.prevent_inline_autocomplete() && | 306 if (!input.prevent_inline_autocomplete() && |
290 StartsWith(suggestion.suggestion(), input_text, false)) { | 307 StartsWith(suggestion.suggestion(), input.text(), false)) { |
291 match.inline_autocompletion = | 308 match.inline_autocompletion = |
292 suggestion.suggestion().substr(input_text.length()); | 309 suggestion.suggestion().substr(input.text().length()); |
293 match.allowed_to_be_default_match = true; | 310 match.allowed_to_be_default_match = true; |
294 } | 311 } |
295 match.fill_into_edit.append(suggestion.suggestion()); | 312 match.fill_into_edit.append(suggestion.suggestion()); |
296 | 313 |
297 const TemplateURLRef& search_url = template_url->url_ref(); | 314 const TemplateURLRef& search_url = template_url->url_ref(); |
298 DCHECK(search_url.SupportsReplacement()); | 315 DCHECK(search_url.SupportsReplacement()); |
299 match.search_terms_args.reset( | 316 match.search_terms_args.reset( |
300 new TemplateURLRef::SearchTermsArgs(suggestion.suggestion())); | 317 new TemplateURLRef::SearchTermsArgs(suggestion.suggestion())); |
301 match.search_terms_args->original_query = input_text; | 318 match.search_terms_args->original_query = input.text(); |
302 match.search_terms_args->accepted_suggestion = accepted_suggestion; | 319 match.search_terms_args->accepted_suggestion = accepted_suggestion; |
303 match.search_terms_args->omnibox_start_margin = omnibox_start_margin; | 320 match.search_terms_args->omnibox_start_margin = omnibox_start_margin; |
304 match.search_terms_args->suggest_query_params = | 321 match.search_terms_args->suggest_query_params = |
305 suggestion.suggest_query_params(); | 322 suggestion.suggest_query_params(); |
306 match.search_terms_args->append_extra_query_params = | 323 match.search_terms_args->append_extra_query_params = |
307 append_extra_query_params; | 324 append_extra_query_params; |
308 // This is the destination URL sans assisted query stats. This must be set | 325 // This is the destination URL sans assisted query stats. This must be set |
309 // so the AutocompleteController can properly de-dupe; the controller will | 326 // so the AutocompleteController can properly de-dupe; the controller will |
310 // eventually overwrite it before it reaches the user. | 327 // eventually overwrite it before it reaches the user. |
311 match.destination_url = | 328 match.destination_url = |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
396 service == NULL || | 413 service == NULL || |
397 !service->IsSyncEnabledAndLoggedIn() || | 414 !service->IsSyncEnabledAndLoggedIn() || |
398 !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( | 415 !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( |
399 syncer::PROXY_TABS) || | 416 syncer::PROXY_TABS) || |
400 service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) | 417 service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) |
401 return false; | 418 return false; |
402 | 419 |
403 return true; | 420 return true; |
404 } | 421 } |
405 | 422 |
| 423 void BaseSearchProvider::AddMatchToMap(const SuggestResult& result, |
| 424 const std::string& metadata, |
| 425 int accepted_suggestion, |
| 426 MatchMap* map) { |
| 427 InstantService* instant_service = |
| 428 InstantServiceFactory::GetForProfile(profile_); |
| 429 // Android and iOS have no InstantService. |
| 430 const int omnibox_start_margin = instant_service ? |
| 431 instant_service->omnibox_start_margin() : chrome::kDisableStartMargin; |
| 432 |
| 433 AutocompleteMatch match = CreateSearchSuggestion( |
| 434 this, GetInput(result), result, GetTemplateURL(result), |
| 435 accepted_suggestion, omnibox_start_margin, |
| 436 ShouldAppendExtraParams(result)); |
| 437 if (!match.destination_url.is_valid()) |
| 438 return; |
| 439 match.search_terms_args->bookmark_bar_pinned = |
| 440 profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar); |
| 441 match.RecordAdditionalInfo(kRelevanceFromServerKey, |
| 442 result.relevance_from_server() ? kTrue : kFalse); |
| 443 match.RecordAdditionalInfo(kShouldPrefetchKey, |
| 444 result.should_prefetch() ? kTrue : kFalse); |
| 445 |
| 446 if (!result.deletion_url().empty()) { |
| 447 GURL url(match.destination_url.GetOrigin().Resolve(result.deletion_url())); |
| 448 if (url.is_valid()) { |
| 449 match.RecordAdditionalInfo(kDeletionUrlKey, url.spec()); |
| 450 match.deletable = true; |
| 451 } |
| 452 } |
| 453 |
| 454 // Metadata is needed only for prefetching queries. |
| 455 if (result.should_prefetch()) |
| 456 match.RecordAdditionalInfo(kSuggestMetadataKey, metadata); |
| 457 |
| 458 // Try to add |match| to |map|. If a match for this suggestion is |
| 459 // already in |map|, replace it if |match| is more relevant. |
| 460 // NOTE: Keep this ToLower() call in sync with url_database.cc. |
| 461 MatchKey match_key( |
| 462 std::make_pair(base::i18n::ToLower(result.suggestion()), |
| 463 match.search_terms_args->suggest_query_params)); |
| 464 const std::pair<MatchMap::iterator, bool> i( |
| 465 map->insert(std::make_pair(match_key, match))); |
| 466 |
| 467 bool should_prefetch = result.should_prefetch(); |
| 468 if (!i.second) { |
| 469 // NOTE: We purposefully do a direct relevance comparison here instead of |
| 470 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items |
| 471 // added first" rather than "items alphabetically first" when the scores |
| 472 // are equal. The only case this matters is when a user has results with |
| 473 // the same score that differ only by capitalization; because the history |
| 474 // system returns results sorted by recency, this means we'll pick the most |
| 475 // recent such result even if the precision of our relevance score is too |
| 476 // low to distinguish the two. |
| 477 if (match.relevance > i.first->second.relevance) { |
| 478 i.first->second = match; |
| 479 } else if (match.keyword == i.first->second.keyword) { |
| 480 // Old and new matches are from the same search provider. It is okay to |
| 481 // record one match's prefetch data onto a different match (for the same |
| 482 // query string) for the following reasons: |
| 483 // 1. Because the suggest server only sends down a query string from |
| 484 // which we construct a URL, rather than sending a full URL, and because |
| 485 // we construct URLs from query strings in the same way every time, the |
| 486 // URLs for the two matches will be the same. Therefore, we won't end up |
| 487 // prefetching something the server didn't intend. |
| 488 // 2. Presumably the server sets the prefetch bit on a match it things is |
| 489 // sufficiently relevant that the user is likely to choose it. Surely |
| 490 // setting the prefetch bit on a match of even higher relevance won't |
| 491 // violate this assumption. |
| 492 should_prefetch |= ShouldPrefetch(i.first->second); |
| 493 i.first->second.RecordAdditionalInfo(kShouldPrefetchKey, |
| 494 should_prefetch ? kTrue : kFalse); |
| 495 if (should_prefetch) |
| 496 i.first->second.RecordAdditionalInfo(kSuggestMetadataKey, metadata); |
| 497 } |
| 498 } |
| 499 } |
OLD | NEW |