Chromium Code Reviews| 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 "components/omnibox/browser/zero_suggest_provider.h" | 5 #include "components/omnibox/browser/zero_suggest_provider.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include "base/callback.h" | 9 #include "base/callback.h" |
| 10 #include "base/feature_list.h" | 10 #include "base/feature_list.h" |
| 11 #include "base/i18n/case_conversion.h" | 11 #include "base/i18n/case_conversion.h" |
| 12 #include "base/json/json_string_value_serializer.h" | 12 #include "base/json/json_string_value_serializer.h" |
| 13 #include "base/metrics/histogram_macros.h" | 13 #include "base/metrics/histogram_macros.h" |
| 14 #include "base/metrics/user_metrics.h" | 14 #include "base/metrics/user_metrics.h" |
| 15 #include "base/strings/string16.h" | 15 #include "base/strings/string16.h" |
| 16 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
| 17 #include "base/strings/utf_string_conversions.h" | 17 #include "base/strings/utf_string_conversions.h" |
| 18 #include "base/time/time.h" | 18 #include "base/time/time.h" |
| 19 #include "base/trace_event/trace_event.h" | 19 #include "base/trace_event/trace_event.h" |
| 20 #include "components/data_use_measurement/core/data_use_user_data.h" | 20 #include "components/data_use_measurement/core/data_use_user_data.h" |
| 21 #include "components/history/core/browser/history_types.h" | 21 #include "components/history/core/browser/history_types.h" |
| 22 #include "components/history/core/browser/top_sites.h" | 22 #include "components/history/core/browser/top_sites.h" |
| 23 #include "components/metrics/proto/omnibox_event.pb.h" | 23 #include "components/metrics/proto/omnibox_event.pb.h" |
| 24 #include "components/metrics/proto/omnibox_input_type.pb.h" | 24 #include "components/metrics/proto/omnibox_input_type.pb.h" |
| 25 #include "components/omnibox/browser/autocomplete_classifier.h" | 25 #include "components/omnibox/browser/autocomplete_classifier.h" |
| 26 #include "components/omnibox/browser/autocomplete_input.h" | 26 #include "components/omnibox/browser/autocomplete_input.h" |
| 27 #include "components/omnibox/browser/autocomplete_match.h" | 27 #include "components/omnibox/browser/autocomplete_match.h" |
| 28 #include "components/omnibox/browser/autocomplete_provider_listener.h" | 28 #include "components/omnibox/browser/autocomplete_provider_listener.h" |
| 29 #include "components/omnibox/browser/contextual_suggestions_service.h" | |
| 29 #include "components/omnibox/browser/history_url_provider.h" | 30 #include "components/omnibox/browser/history_url_provider.h" |
| 30 #include "components/omnibox/browser/omnibox_field_trial.h" | 31 #include "components/omnibox/browser/omnibox_field_trial.h" |
| 31 #include "components/omnibox/browser/omnibox_pref_names.h" | 32 #include "components/omnibox/browser/omnibox_pref_names.h" |
| 32 #include "components/omnibox/browser/search_provider.h" | 33 #include "components/omnibox/browser/search_provider.h" |
| 33 #include "components/omnibox/browser/verbatim_match.h" | 34 #include "components/omnibox/browser/verbatim_match.h" |
| 34 #include "components/pref_registry/pref_registry_syncable.h" | 35 #include "components/pref_registry/pref_registry_syncable.h" |
| 35 #include "components/prefs/pref_service.h" | 36 #include "components/prefs/pref_service.h" |
| 36 #include "components/search_engines/template_url_service.h" | 37 #include "components/search_engines/template_url_service.h" |
| 37 #include "components/url_formatter/url_formatter.h" | 38 #include "components/url_formatter/url_formatter.h" |
| 38 #include "components/variations/net/variations_http_headers.h" | 39 #include "components/variations/net/variations_http_headers.h" |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 78 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value, | 79 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value, |
| 79 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE); | 80 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE); |
| 80 } | 81 } |
| 81 | 82 |
| 82 // Relevance value to use if it was not set explicitly by the server. | 83 // Relevance value to use if it was not set explicitly by the server. |
| 83 const int kDefaultZeroSuggestRelevance = 100; | 84 const int kDefaultZeroSuggestRelevance = 100; |
| 84 | 85 |
| 85 // Used for testing whether zero suggest is ever available. | 86 // Used for testing whether zero suggest is ever available. |
| 86 constexpr char kArbitraryInsecureUrlString[] = "http://www.google.com/"; | 87 constexpr char kArbitraryInsecureUrlString[] = "http://www.google.com/"; |
| 87 | 88 |
| 88 // Returns true if the folowing conditions are valid: | |
| 89 // * The |default_provider| is Google. | |
| 90 // * The user is in the ZeroSuggestRedirectToChrome field trial. | |
| 91 // * The field trial provides a valid server address where the suggest request | |
| 92 // is redirected. | |
| 93 // * The suggest request is over HTTPS. This avoids leaking the current page URL | |
| 94 // or personal data in unencrypted network traffic. | |
| 95 // Note: these checks are in addition to CanSendUrl() on the default contextual | |
| 96 // suggestion URL. | |
| 97 bool UseExperimentalSuggestService(const TemplateURLService& default_provider) { | |
| 98 const TemplateURL& default_provider_url = | |
| 99 *default_provider.GetDefaultSearchProvider(); | |
| 100 const SearchTermsData& search_terms_data = | |
| 101 default_provider.search_terms_data(); | |
| 102 if ((default_provider_url.GetEngineType(search_terms_data) != | |
| 103 SEARCH_ENGINE_GOOGLE) || | |
| 104 !OmniboxFieldTrial::InZeroSuggestRedirectToChromeFieldTrial()) | |
| 105 return false; | |
| 106 // Check that the suggest URL for redirect to chrome field trial is valid. | |
| 107 const GURL suggest_url( | |
| 108 OmniboxFieldTrial::ZeroSuggestRedirectToChromeServerAddress()); | |
| 109 if (!suggest_url.is_valid()) | |
| 110 return false; | |
| 111 return suggest_url.SchemeIsCryptographic(); | |
| 112 } | |
| 113 | |
| 114 } // namespace | 89 } // namespace |
| 115 | 90 |
| 116 // static | 91 // static |
| 117 ZeroSuggestProvider* ZeroSuggestProvider::Create( | 92 ZeroSuggestProvider* ZeroSuggestProvider::Create( |
| 118 AutocompleteProviderClient* client, | 93 AutocompleteProviderClient* client, |
| 119 HistoryURLProvider* history_url_provider, | 94 HistoryURLProvider* history_url_provider, |
| 120 AutocompleteProviderListener* listener) { | 95 AutocompleteProviderListener* listener) { |
| 121 return new ZeroSuggestProvider(client, history_url_provider, listener); | 96 return new ZeroSuggestProvider(client, history_url_provider, listener); |
| 122 } | 97 } |
| 123 | 98 |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 CanSendURL(arbitrary_insecure_url, suggest_url, default_provider, | 142 CanSendURL(arbitrary_insecure_url, suggest_url, default_provider, |
| 168 current_page_classification_, | 143 current_page_classification_, |
| 169 template_url_service->search_terms_data(), client()); | 144 template_url_service->search_terms_data(), client()); |
| 170 eligibility = can_send_ordinary_url | 145 eligibility = can_send_ordinary_url |
| 171 ? ZeroSuggestEligibility::URL_INELIGIBLE | 146 ? ZeroSuggestEligibility::URL_INELIGIBLE |
| 172 : ZeroSuggestEligibility::GENERALLY_INELIGIBLE; | 147 : ZeroSuggestEligibility::GENERALLY_INELIGIBLE; |
| 173 } | 148 } |
| 174 UMA_HISTOGRAM_ENUMERATION( | 149 UMA_HISTOGRAM_ENUMERATION( |
| 175 "Omnibox.ZeroSuggest.Eligible.OnFocus", static_cast<int>(eligibility), | 150 "Omnibox.ZeroSuggest.Eligible.OnFocus", static_cast<int>(eligibility), |
| 176 static_cast<int>(ZeroSuggestEligibility::ELIGIBLE_MAX_VALUE)); | 151 static_cast<int>(ZeroSuggestEligibility::ELIGIBLE_MAX_VALUE)); |
| 177 if (can_send_current_url && | 152 |
| 153 bool attach_current_url = | |
|
Mark P
2017/07/18 19:11:30
optional nit: how about can_attach_current_url?
| |
| 154 can_send_current_url && | |
| 178 !OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() && | 155 !OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial() && |
| 179 !OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) { | 156 !OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial(); |
| 180 // Update suggest_url to include the current_page_url. | 157 |
| 181 if (UseExperimentalSuggestService(*template_url_service)) { | 158 if (!attach_current_url && |
| 182 suggest_url = GURL( | 159 !ShouldShowNonContextualZeroSuggest(input.current_url())) { |
| 183 OmniboxFieldTrial::ZeroSuggestRedirectToChromeServerAddress() + | |
| 184 "/url=" + net::EscapePath(current_query_) + | |
| 185 OmniboxFieldTrial::ZeroSuggestRedirectToChromeAdditionalFields()); | |
| 186 } else { | |
| 187 base::string16 prefix; | |
| 188 TemplateURLRef::SearchTermsArgs search_term_args(prefix); | |
| 189 search_term_args.current_page_url = current_query_; | |
| 190 suggest_url = | |
| 191 GURL(default_provider->suggestions_url_ref().ReplaceSearchTerms( | |
| 192 search_term_args, template_url_service->search_terms_data())); | |
| 193 } | |
| 194 } else if (!ShouldShowNonContextualZeroSuggest(input.current_url())) { | |
| 195 return; | 160 return; |
| 196 } | 161 } |
| 197 | 162 |
| 198 done_ = false; | |
|
Mark P
2017/07/18 19:11:30
I think this line should probably stay. I wonder
| |
| 199 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. | 163 // TODO(jered): Consider adding locally-sourced zero-suggestions here too. |
| 200 // These may be useful on the NTP or more relevant to the user than server | 164 // These may be useful on the NTP or more relevant to the user than server |
| 201 // suggestions, if based on local browsing history. | 165 // suggestions, if based on local browsing history. |
| 202 MaybeUseCachedSuggestions(); | 166 MaybeUseCachedSuggestions(); |
| 203 Run(suggest_url); | 167 |
| 168 if (OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) { | |
| 169 most_visited_urls_.clear(); | |
| 170 scoped_refptr<history::TopSites> ts = client()->GetTopSites(); | |
| 171 if (ts) { | |
| 172 waiting_for_most_visited_urls_request_ = true; | |
| 173 ts->GetMostVisitedURLs( | |
| 174 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable, | |
| 175 weak_ptr_factory_.GetWeakPtr()), | |
| 176 false); | |
| 177 } | |
| 178 return; | |
| 179 } | |
| 180 | |
| 181 // Create a request for experimental suggestions with |this| as the | |
| 182 // fetcher delegate. | |
| 183 client() | |
| 184 ->GetContextualSuggestionsService() | |
|
Mark P
2017/07/18 19:11:30
Is GetContextualSuggestionsService() always going
| |
| 185 ->CreateContextualSuggestionsRequest( | |
| 186 attach_current_url ? current_query_ : std::string(), | |
| 187 /*fetcher_delegate=*/this, | |
| 188 base::BindOnce( | |
| 189 &ZeroSuggestProvider::OnContextualSuggestionsFetcherAvailable, | |
| 190 weak_ptr_factory_.GetWeakPtr())); | |
| 191 } | |
| 192 | |
| 193 void ZeroSuggestProvider::OnContextualSuggestionsFetcherAvailable( | |
|
Mark P
2017/07/18 19:11:30
nit: Definitions in the .cc file should be in the
| |
| 194 std::unique_ptr<net::URLFetcher> fetcher) { | |
| 195 if (fetcher == nullptr) { | |
|
Mark P
2017/07/18 19:11:30
Why / how can this happen?
Also, since we're going
| |
| 196 return; | |
| 197 } | |
| 198 fetcher_ = std::move(fetcher); | |
| 199 done_ = false; | |
|
Mark P
2017/07/18 19:11:30
I don't think this is the right place to set done_
| |
| 200 fetcher_->Start(); | |
| 201 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT); | |
| 204 } | 202 } |
| 205 | 203 |
| 206 void ZeroSuggestProvider::Stop(bool clear_cached_results, | 204 void ZeroSuggestProvider::Stop(bool clear_cached_results, |
| 207 bool due_to_user_inactivity) { | 205 bool due_to_user_inactivity) { |
| 208 if (fetcher_) | 206 if (fetcher_) |
| 209 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED); | 207 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED); |
| 210 fetcher_.reset(); | 208 fetcher_.reset(); |
| 211 waiting_for_most_visited_urls_request_ = false; | 209 waiting_for_most_visited_urls_request_ = false; |
| 212 done_ = true; | 210 done_ = true; |
| 213 | 211 |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 389 | 387 |
| 390 match.description = | 388 match.description = |
| 391 AutocompleteMatch::SanitizeString(navigation.description()); | 389 AutocompleteMatch::SanitizeString(navigation.description()); |
| 392 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, | 390 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0, |
| 393 match.description.length(), ACMatchClassification::NONE, | 391 match.description.length(), ACMatchClassification::NONE, |
| 394 &match.description_class); | 392 &match.description_class); |
| 395 match.subtype_identifier = navigation.subtype_identifier(); | 393 match.subtype_identifier = navigation.subtype_identifier(); |
| 396 return match; | 394 return match; |
| 397 } | 395 } |
| 398 | 396 |
| 399 void ZeroSuggestProvider::Run(const GURL& suggest_url) { | |
| 400 if (OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial()) { | |
| 401 most_visited_urls_.clear(); | |
| 402 scoped_refptr<history::TopSites> ts = client()->GetTopSites(); | |
| 403 if (ts) { | |
| 404 waiting_for_most_visited_urls_request_ = true; | |
| 405 ts->GetMostVisitedURLs( | |
| 406 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable, | |
| 407 weak_ptr_factory_.GetWeakPtr()), false); | |
| 408 } | |
| 409 } else { | |
| 410 net::NetworkTrafficAnnotationTag traffic_annotation = | |
| 411 net::DefineNetworkTrafficAnnotation("omnibox_zerosuggest", R"( | |
| 412 semantics { | |
| 413 sender: "Omnibox" | |
| 414 description: | |
| 415 "When the user focuses the omnibox, Chrome can provide search or " | |
| 416 "navigation suggestions from the default search provider in the " | |
| 417 "omnibox dropdown, based on the current page URL.\n" | |
| 418 "This is limited to users whose default search engine is Google, " | |
| 419 "as no other search engines currently support this kind of " | |
| 420 "suggestion." | |
| 421 trigger: "The omnibox receives focus." | |
| 422 data: "The URL of the current page." | |
| 423 destination: GOOGLE_OWNED_SERVICE | |
| 424 } | |
| 425 policy { | |
| 426 cookies_allowed: true | |
| 427 cookies_store: "user" | |
| 428 setting: | |
| 429 "Users can control this feature via the 'Use a prediction service " | |
| 430 "to help complete searches and URLs typed in the address bar' " | |
| 431 "settings under 'Privacy'. The feature is enabled by default." | |
| 432 chrome_policy { | |
| 433 SearchSuggestEnabled { | |
| 434 policy_options {mode: MANDATORY} | |
| 435 SearchSuggestEnabled: false | |
| 436 } | |
| 437 } | |
| 438 })"); | |
| 439 const int kFetcherID = 1; | |
| 440 fetcher_ = | |
| 441 net::URLFetcher::Create(kFetcherID, suggest_url, net::URLFetcher::GET, | |
| 442 this, traffic_annotation); | |
| 443 data_use_measurement::DataUseUserData::AttachToFetcher( | |
| 444 fetcher_.get(), data_use_measurement::DataUseUserData::OMNIBOX); | |
| 445 fetcher_->SetRequestContext(client()->GetRequestContext()); | |
| 446 fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); | |
| 447 // Add Chrome experiment state to the request headers. | |
| 448 net::HttpRequestHeaders headers; | |
| 449 // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does | |
| 450 // not affect transmission of experiments coming from the variations server. | |
| 451 bool is_signed_in = false; | |
| 452 variations::AppendVariationHeaders(fetcher_->GetOriginalURL(), | |
| 453 client()->IsOffTheRecord(), false, | |
| 454 is_signed_in, &headers); | |
| 455 fetcher_->SetExtraRequestHeaders(headers.ToString()); | |
| 456 fetcher_->Start(); | |
| 457 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT); | |
| 458 } | |
| 459 } | |
| 460 | |
| 461 void ZeroSuggestProvider::OnMostVisitedUrlsAvailable( | 397 void ZeroSuggestProvider::OnMostVisitedUrlsAvailable( |
| 462 const history::MostVisitedURLList& urls) { | 398 const history::MostVisitedURLList& urls) { |
| 463 if (!waiting_for_most_visited_urls_request_) return; | 399 if (!waiting_for_most_visited_urls_request_) return; |
| 464 most_visited_urls_ = urls; | 400 most_visited_urls_ = urls; |
| 465 waiting_for_most_visited_urls_request_ = false; | 401 waiting_for_most_visited_urls_request_ = false; |
| 466 done_ = true; | 402 done_ = true; |
| 467 ConvertResultsToAutocompleteMatches(); | 403 ConvertResultsToAutocompleteMatches(); |
| 468 listener_->OnProviderUpdate(true); | 404 listener_->OnProviderUpdate(true); |
| 469 } | 405 } |
| 470 | 406 |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 601 if (!json_data.empty()) { | 537 if (!json_data.empty()) { |
| 602 std::unique_ptr<base::Value> data( | 538 std::unique_ptr<base::Value> data( |
| 603 SearchSuggestionParser::DeserializeJsonData(json_data)); | 539 SearchSuggestionParser::DeserializeJsonData(json_data)); |
| 604 if (data && ParseSuggestResults( | 540 if (data && ParseSuggestResults( |
| 605 *data, kDefaultZeroSuggestRelevance, false, &results_)) { | 541 *data, kDefaultZeroSuggestRelevance, false, &results_)) { |
| 606 ConvertResultsToAutocompleteMatches(); | 542 ConvertResultsToAutocompleteMatches(); |
| 607 results_from_cache_ = !matches_.empty(); | 543 results_from_cache_ = !matches_.empty(); |
| 608 } | 544 } |
| 609 } | 545 } |
| 610 } | 546 } |
| OLD | NEW |