Chromium Code Reviews| 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 |