OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/omnibox/browser/contextual_suggestions_service.h" | |
6 | |
7 #include "base/feature_list.h" | |
8 #include "base/memory/ptr_util.h" | |
9 #include "base/metrics/field_trial_params.h" | |
10 #include "base/strings/stringprintf.h" | |
11 #include "components/data_use_measurement/core/data_use_user_data.h" | |
12 #include "components/omnibox/browser/omnibox_field_trial.h" | |
13 #include "components/variations/net/variations_http_headers.h" | |
14 #include "net/base/escape.h" | |
15 #include "net/base/load_flags.h" | |
16 #include "net/http/http_response_headers.h" | |
17 #include "net/traffic_annotation/network_traffic_annotation.h" | |
18 #include "net/url_request/url_fetcher.h" | |
19 | |
20 namespace contextual_suggestions { | |
21 | |
22 namespace { | |
23 // Server address for the experimental suggestions service. | |
24 const char kExperimentalServerAddress[] = | |
25 "https://cuscochromeextension-pa.googleapis.com/v1/zerosuggestions"; | |
26 | |
27 // Parameter name used by the experiment redirecting Zero Suggestion requests | |
28 // to a service provided by the Chrome team. | |
29 const char kZeroSuggestRedirectToChromeExperimentIdParam[] = | |
30 "ZeroSuggestRedirectToChromeExperimentID"; | |
31 | |
32 // Scope for the authentication token attached to the request sent to the | |
33 // experimental service. | |
34 const char kScopeForExperimentalService[] = | |
35 "https://www.googleapis.com/auth/cusco-chrome-extension"; | |
36 | |
37 // Format string for OAuth2 authentication headers. | |
38 const char kAuthorizationHeaderFormat[] = "Authorization: Bearer %s"; | |
39 | |
40 } // namespace | |
41 | |
42 ContextualSuggestionsService::ContextualSuggestionsService( | |
43 SigninManagerBase* signin_manager, | |
44 OAuth2TokenService* token_service, | |
45 TemplateURLService* template_url_service, | |
46 net::URLRequestContextGetter* request_context) | |
47 : request_context_(request_context), | |
48 signin_manager_(signin_manager), | |
49 template_url_service_(template_url_service), | |
50 token_service_(token_service) {} | |
51 | |
52 ContextualSuggestionsService::~ContextualSuggestionsService() {} | |
53 | |
54 bool ContextualSuggestionsService::UseExperimentalZeroSuggestSuggestions( | |
55 const std::string& visited_url) const { | |
56 if (visited_url.empty()) { | |
57 return false; | |
58 } | |
59 | |
60 if (!base::FeatureList::IsEnabled(omnibox::kZeroSuggestRedirectToChrome) || | |
61 template_url_service_ == nullptr) { | |
62 return false; | |
63 } | |
64 | |
65 // Check that the default search engine is Google. | |
66 const TemplateURL& default_provider_url = | |
67 *template_url_service_->GetDefaultSearchProvider(); | |
68 const SearchTermsData& search_terms_data = | |
69 template_url_service_->search_terms_data(); | |
70 if (default_provider_url.GetEngineType(search_terms_data) != | |
71 SEARCH_ENGINE_GOOGLE) { | |
72 return false; | |
73 } | |
74 | |
75 // Check that the suggest URL for redirect to chrome field trial is valid. | |
76 const GURL suggest_url = | |
77 ExperimentalZeroSuggestURL(/*visited_url=*/std::string()); | |
Mark P
2017/07/18 19:11:28
Shouldn't you provide a real URL here? I wouldn't
Toyama
2017/07/18 20:28:47
I don't have the right context here, but everythin
| |
78 if (!suggest_url.is_valid()) { | |
79 return false; | |
80 } | |
81 | |
82 // Check that the suggest URL for redirect to chrome is HTTPS. | |
83 return suggest_url.SchemeIsCryptographic(); | |
84 } | |
85 | |
86 void ContextualSuggestionsService::CreateContextualSuggestionsRequest( | |
87 const std::string& visited_url, | |
88 net::URLFetcherDelegate* fetcher_delegate, | |
89 ContextualSuggestionsCallback callback) { | |
90 DCHECK(signin_manager_); | |
91 DCHECK(token_service_); | |
92 | |
93 const bool experimental = UseExperimentalZeroSuggestSuggestions(visited_url); | |
94 std::unique_ptr<net::URLFetcher> fetcher = | |
95 CreateRequest(visited_url, experimental, fetcher_delegate); | |
96 if (fetcher == nullptr) { | |
97 std::move(callback).Run(nullptr); | |
98 return; | |
99 } | |
100 | |
101 if (experimental) { | |
102 // Skip this request if still waiting for oauth2 token. | |
103 if (token_fetcher_) { | |
104 std::move(callback).Run(nullptr); | |
105 return; | |
106 } | |
107 // Create the oauth2 token fetcher. | |
108 OAuth2TokenService::ScopeSet scopes{kScopeForExperimentalService}; | |
109 token_fetcher_ = base::MakeUnique<AccessTokenFetcher>( | |
110 "contextual_suggestions_service", signin_manager_, token_service_, | |
111 scopes, | |
112 base::BindOnce(&ContextualSuggestionsService::AccessTokenAvailable, | |
113 base::Unretained(this), std::move(fetcher), | |
114 std::move(callback))); | |
115 return; | |
116 } | |
117 | |
118 std::move(callback).Run(std::move(fetcher)); | |
119 } | |
120 | |
121 // static | |
122 GURL ContextualSuggestionsService::ExperimentalZeroSuggestURL( | |
123 const std::string& visited_url) { | |
124 const std::string server_address(kExperimentalServerAddress); | |
125 const int experiment_id = base::GetFieldTrialParamByFeatureAsInt( | |
126 omnibox::kZeroSuggestRedirectToChrome, | |
127 kZeroSuggestRedirectToChromeExperimentIdParam, /*default_value=*/-1); | |
128 const std::string additional_parameters = | |
129 experiment_id >= 0 | |
130 ? base::StringPrintf("&experiment_id=%d", experiment_id) | |
131 : "&"; | |
132 return GURL(server_address + "/url=" + net::EscapePath(visited_url) + | |
133 additional_parameters); | |
134 } | |
135 | |
136 GURL ContextualSuggestionsService::ZeroSuggestURL( | |
137 const std::string& visited_url, | |
138 bool experimental) const { | |
139 if (experimental) { | |
140 return ExperimentalZeroSuggestURL(visited_url); | |
141 } | |
142 | |
143 const TemplateURLRef& suggestion_url_ref = | |
144 template_url_service_->GetDefaultSearchProvider()->suggestions_url_ref(); | |
145 const SearchTermsData& search_terms_data = | |
146 template_url_service_->search_terms_data(); | |
147 base::string16 prefix; | |
148 TemplateURLRef::SearchTermsArgs search_term_args(prefix); | |
149 if (!visited_url.empty()) { | |
150 search_term_args.current_page_url = visited_url; | |
151 } | |
152 return GURL(suggestion_url_ref.ReplaceSearchTerms(search_term_args, | |
153 search_terms_data)); | |
154 } | |
155 | |
156 std::unique_ptr<net::URLFetcher> ContextualSuggestionsService::CreateRequest( | |
157 const std::string& visited_url, | |
158 bool experimental, | |
159 net::URLFetcherDelegate* fetcher_delegate) const { | |
160 const GURL suggest_url = ZeroSuggestURL(visited_url, experimental); | |
161 DCHECK(suggest_url.is_valid()); | |
162 | |
163 if (visited_url.empty()) | |
164 experimental = false; | |
165 const int kFetcherID = 1; | |
166 net::NetworkTrafficAnnotationTag annotation_tag = | |
167 experimental | |
168 ? net::DefineNetworkTrafficAnnotation( | |
169 "omnibox_zerosuggest_experimental", R"( | |
170 semantics { | |
171 sender: "Omnibox" | |
172 description: | |
173 "When the user focuses the omnibox, Chrome can provide search or " | |
174 "navigation suggestions from the default search provider in the " | |
175 "omnibox dropdown, based on the current page URL.\n" | |
176 "This is limited to users whose default search engine is Google, " | |
177 "as no other search engines currently support this kind of " | |
178 "suggestion." | |
179 trigger: "The omnibox receives focus." | |
180 data: "The user's OAuth2 credentials and the URL of the current page." | |
181 destination: GOOGLE_OWNED_SERVICE | |
182 } | |
183 policy { | |
184 cookies_allowed: false | |
185 setting: | |
186 "Users can control this feature via the 'Use a prediction service " | |
187 "to help complete searches and URLs typed in the address bar' " | |
188 "settings under 'Privacy'. The feature is enabled by default." | |
189 chrome_policy { | |
190 SearchSuggestEnabled { | |
191 policy_options {mode: MANDATORY} | |
192 SearchSuggestEnabled: false | |
193 } | |
194 } | |
195 })") | |
196 : net::DefineNetworkTrafficAnnotation("omnibox_zerosuggest", R"( | |
197 semantics { | |
198 sender: "Omnibox" | |
199 description: | |
200 "When the user focuses the omnibox, Chrome can provide search or " | |
201 "navigation suggestions from the default search provider in the " | |
202 "omnibox dropdown, based on the current page URL.\n" | |
203 "This is limited to users whose default search engine is Google, " | |
204 "as no other search engines currently support this kind of " | |
205 "suggestion." | |
206 trigger: "The omnibox receives focus." | |
207 data: "The URL of the current page." | |
208 destination: GOOGLE_OWNED_SERVICE | |
209 } | |
210 policy { | |
211 cookies_allowed: true | |
212 cookies_store: "user" | |
213 setting: | |
214 "Users can control this feature via the 'Use a prediction service " | |
215 "to help complete searches and URLs typed in the address bar' " | |
216 "settings under 'Privacy'. The feature is enabled by default." | |
217 chrome_policy { | |
218 SearchSuggestEnabled { | |
219 policy_options {mode: MANDATORY} | |
220 SearchSuggestEnabled: false | |
221 } | |
222 } | |
223 })"); | |
224 | |
225 std::unique_ptr<net::URLFetcher> fetcher = | |
226 net::URLFetcher::Create(kFetcherID, suggest_url, net::URLFetcher::GET, | |
227 fetcher_delegate, annotation_tag); | |
228 fetcher->SetRequestContext(request_context_); | |
229 if (experimental) { | |
230 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | |
231 net::LOAD_DO_NOT_SAVE_COOKIES); | |
232 } else { | |
233 data_use_measurement::DataUseUserData::AttachToFetcher( | |
234 fetcher.get(), data_use_measurement::DataUseUserData::OMNIBOX); | |
235 fetcher->SetLoadFlags(net::LOAD_DO_NOT_SAVE_COOKIES); | |
236 // Add Chrome experiment state to the request headers. | |
237 net::HttpRequestHeaders headers; | |
238 // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does | |
239 // not affect transmission of experiments coming from the variations server. | |
240 variations::AppendVariationHeaders(fetcher->GetOriginalURL(), false, false, | |
241 /*is_signed_in=*/false, &headers); | |
242 fetcher->SetExtraRequestHeaders(headers.ToString()); | |
243 } | |
244 return fetcher; | |
245 } | |
246 | |
247 void ContextualSuggestionsService::AccessTokenAvailable( | |
248 std::unique_ptr<net::URLFetcher> fetcher, | |
249 ContextualSuggestionsCallback callback, | |
250 const GoogleServiceAuthError& error, | |
251 const std::string& access_token) { | |
252 DCHECK(token_fetcher_); | |
253 std::unique_ptr<AccessTokenFetcher> token_fetcher_deleter( | |
254 std::move(token_fetcher_)); | |
255 | |
256 if (error.state() != GoogleServiceAuthError::NONE) { | |
257 std::move(callback).Run(nullptr); | |
258 return; | |
259 } | |
260 DCHECK(!access_token.empty()); | |
261 fetcher->AddExtraRequestHeader( | |
262 base::StringPrintf(kAuthorizationHeaderFormat, access_token.c_str())); | |
263 std::move(callback).Run(std::move(fetcher)); | |
264 } | |
265 | |
266 } // namespace contextual_suggestions | |
OLD | NEW |