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 |