OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/ntp_snippets/ntp_snippets_fetcher.h" | 5 #include "components/ntp_snippets/ntp_snippets_fetcher.h" |
6 | 6 |
7 #include <stdlib.h> | 7 #include <stdlib.h> |
8 | 8 |
9 #include "base/command_line.h" | 9 #include "base/command_line.h" |
10 #include "base/files/file_path.h" | 10 #include "base/files/file_path.h" |
11 #include "base/files/file_util.h" | 11 #include "base/files/file_util.h" |
| 12 #include "base/json/json_writer.h" |
| 13 #include "base/memory/ptr_util.h" |
12 #include "base/metrics/histogram_macros.h" | 14 #include "base/metrics/histogram_macros.h" |
13 #include "base/metrics/sparse_histogram.h" | 15 #include "base/metrics/sparse_histogram.h" |
14 #include "base/path_service.h" | 16 #include "base/path_service.h" |
15 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
16 #include "base/strings/string_util.h" | 18 #include "base/strings/string_util.h" |
17 #include "base/strings/stringprintf.h" | 19 #include "base/strings/stringprintf.h" |
18 #include "base/time/default_tick_clock.h" | 20 #include "base/time/default_tick_clock.h" |
19 #include "base/values.h" | 21 #include "base/values.h" |
20 #include "components/data_use_measurement/core/data_use_user_data.h" | 22 #include "components/data_use_measurement/core/data_use_user_data.h" |
21 #include "components/ntp_snippets/ntp_snippets_constants.h" | 23 #include "components/ntp_snippets/ntp_snippets_constants.h" |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
55 | 57 |
56 // Constants for possible values of the "fetching_personalization" parameter. | 58 // Constants for possible values of the "fetching_personalization" parameter. |
57 const char kPersonalizationPersonalString[] = "personal"; | 59 const char kPersonalizationPersonalString[] = "personal"; |
58 const char kPersonalizationNonPersonalString[] = "non_personal"; | 60 const char kPersonalizationNonPersonalString[] = "non_personal"; |
59 const char kPersonalizationBothString[] = "both"; // the default value | 61 const char kPersonalizationBothString[] = "both"; // the default value |
60 | 62 |
61 // Constants for possible values of the "fetching_host_restrict" parameter. | 63 // Constants for possible values of the "fetching_host_restrict" parameter. |
62 const char kHostRestrictionOnString[] = "on"; // the default value | 64 const char kHostRestrictionOnString[] = "on"; // the default value |
63 const char kHostRestrictionOffString[] = "off"; | 65 const char kHostRestrictionOffString[] = "off"; |
64 | 66 |
65 const char kRequestFormat[] = | |
66 "{" | |
67 " \"response_detail_level\": \"STANDARD\"," | |
68 "%s" // If authenticated - an obfuscated Gaia ID will be inserted here. | |
69 " \"advanced_options\": {" | |
70 " \"local_scoring_params\": {" | |
71 " \"content_params\": {" | |
72 " \"only_return_personalized_results\": %s" | |
73 "%s" // If authenticated - user segment (lang code) will be inserted here. | |
74 " }," | |
75 " \"content_restricts\": [" | |
76 " {" | |
77 " \"type\": \"METADATA\"," | |
78 " \"value\": \"TITLE\"" | |
79 " }," | |
80 " {" | |
81 " \"type\": \"METADATA\"," | |
82 " \"value\": \"SNIPPET\"" | |
83 " }," | |
84 " {" | |
85 " \"type\": \"METADATA\"," | |
86 " \"value\": \"THUMBNAIL\"" | |
87 " }" | |
88 " ]," | |
89 " \"content_selectors\": [%s]" | |
90 " }," | |
91 " \"global_scoring_params\": {" | |
92 " \"num_to_return\": %i," | |
93 " \"sort_type\": 1" | |
94 " }" | |
95 " }" | |
96 "}"; | |
97 | |
98 const char kGaiaIdFormat[] = " \"obfuscated_gaia_id\": \"%s\","; | |
99 const char kUserSegmentFormat[] = " ,\"user_segment\": \"%s\""; | |
100 const char kHostRestrictFormat[] = | |
101 " {" | |
102 " \"type\": \"HOST_RESTRICT\"," | |
103 " \"value\": \"%s\"" | |
104 " }"; | |
105 const char kTrueString[] = "true"; | |
106 const char kFalseString[] = "false"; | |
107 | |
108 std::string FetchResultToString(NTPSnippetsFetcher::FetchResult result) { | 67 std::string FetchResultToString(NTPSnippetsFetcher::FetchResult result) { |
109 switch (result) { | 68 switch (result) { |
110 case NTPSnippetsFetcher::FetchResult::SUCCESS: | 69 case NTPSnippetsFetcher::FetchResult::SUCCESS: |
111 return "OK"; | 70 return "OK"; |
112 case NTPSnippetsFetcher::FetchResult::EMPTY_HOSTS: | 71 case NTPSnippetsFetcher::FetchResult::EMPTY_HOSTS: |
113 return "Cannot fetch for empty hosts list."; | 72 return "Cannot fetch for empty hosts list."; |
114 case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR: | 73 case NTPSnippetsFetcher::FetchResult::URL_REQUEST_STATUS_ERROR: |
115 return "URLRequestStatus error"; | 74 return "URLRequestStatus error"; |
116 case NTPSnippetsFetcher::FetchResult::HTTP_ERROR: | 75 case NTPSnippetsFetcher::FetchResult::HTTP_ERROR: |
117 return "HTTP error"; | 76 return "HTTP error"; |
118 case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR: | 77 case NTPSnippetsFetcher::FetchResult::JSON_PARSE_ERROR: |
119 return "Received invalid JSON"; | 78 return "Received invalid JSON"; |
120 case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR: | 79 case NTPSnippetsFetcher::FetchResult::INVALID_SNIPPET_CONTENT_ERROR: |
121 return "Invalid / empty list."; | 80 return "Invalid / empty list."; |
122 case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR: | 81 case NTPSnippetsFetcher::FetchResult::OAUTH_TOKEN_ERROR: |
123 return "Error in obtaining an OAuth2 access token."; | 82 return "Error in obtaining an OAuth2 access token."; |
124 case NTPSnippetsFetcher::FetchResult::RESULT_MAX: | 83 case NTPSnippetsFetcher::FetchResult::RESULT_MAX: |
125 break; | 84 break; |
126 } | 85 } |
127 NOTREACHED(); | 86 NOTREACHED(); |
128 return "Unknown error"; | 87 return "Unknown error"; |
129 } | 88 } |
130 | 89 |
131 std::string BuildRequest(const std::string& obfuscated_gaia_id, | |
132 bool only_return_personalized_results, | |
133 const std::string& user_segment, | |
134 const std::string& host_restricts, | |
135 int count_to_fetch) { | |
136 return base::StringPrintf( | |
137 kRequestFormat, obfuscated_gaia_id.c_str(), | |
138 only_return_personalized_results ? kTrueString : kFalseString, | |
139 user_segment.c_str(), host_restricts.c_str(), count_to_fetch); | |
140 } | |
141 | |
142 } // namespace | 90 } // namespace |
143 | 91 |
144 NTPSnippetsFetcher::NTPSnippetsFetcher( | 92 NTPSnippetsFetcher::NTPSnippetsFetcher( |
145 SigninManagerBase* signin_manager, | 93 SigninManagerBase* signin_manager, |
146 OAuth2TokenService* token_service, | 94 OAuth2TokenService* token_service, |
147 scoped_refptr<URLRequestContextGetter> url_request_context_getter, | 95 scoped_refptr<URLRequestContextGetter> url_request_context_getter, |
148 const ParseJSONCallback& parse_json_callback, | 96 const ParseJSONCallback& parse_json_callback, |
149 bool is_stable_channel) | 97 bool is_stable_channel) |
150 : OAuth2TokenService::Consumer("ntp_snippets"), | 98 : OAuth2TokenService::Consumer("ntp_snippets"), |
151 signin_manager_(signin_manager), | 99 signin_manager_(signin_manager), |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
230 // Wait until we get a refresh token. | 178 // Wait until we get a refresh token. |
231 waiting_for_refresh_token_ = true; | 179 waiting_for_refresh_token_ = true; |
232 token_service_->AddObserver(this); | 180 token_service_->AddObserver(this); |
233 } | 181 } |
234 } else { | 182 } else { |
235 // Not signed in: fetch snippets (without authentication). | 183 // Not signed in: fetch snippets (without authentication). |
236 FetchSnippetsNonAuthenticated(); | 184 FetchSnippetsNonAuthenticated(); |
237 } | 185 } |
238 } | 186 } |
239 | 187 |
| 188 // static |
| 189 std::string NTPSnippetsFetcher::BuildRequest( |
| 190 const std::string& obfuscated_gaia_id, |
| 191 bool only_return_personalized_results, |
| 192 const std::string& user_segment, |
| 193 const std::set<std::string>& host_restricts, |
| 194 int count_to_fetch) { |
| 195 auto content_params = base::MakeUnique<base::DictionaryValue>(); |
| 196 content_params->SetBoolean("only_return_personalized_results", |
| 197 only_return_personalized_results); |
| 198 if (!user_segment.empty()) { |
| 199 content_params->SetString("user_segment", user_segment); |
| 200 } |
| 201 |
| 202 auto content_restricts = base::MakeUnique<base::ListValue>(); |
| 203 for (const auto& metadata : {"TITLE", "SNIPPET", "THUMBNAIL"}) { |
| 204 auto entry = base::MakeUnique<base::DictionaryValue>(); |
| 205 entry->SetString("type", "METADATA"); |
| 206 entry->SetString("value", metadata); |
| 207 content_restricts->Append(std::move(entry)); |
| 208 } |
| 209 |
| 210 auto content_selectors = base::MakeUnique<base::ListValue>(); |
| 211 for (const auto& host : host_restricts) { |
| 212 auto entry = base::MakeUnique<base::DictionaryValue>(); |
| 213 entry->SetString("type", "HOST_RESTRICT"); |
| 214 entry->SetString("value", host); |
| 215 content_selectors->Append(std::move(entry)); |
| 216 } |
| 217 |
| 218 auto local_scoring_params = base::MakeUnique<base::DictionaryValue>(); |
| 219 local_scoring_params->Set("content_params", std::move(content_params)); |
| 220 local_scoring_params->Set("content_restricts", std::move(content_restricts)); |
| 221 local_scoring_params->Set("content_selectors", std::move(content_selectors)); |
| 222 |
| 223 auto global_scoring_params = base::MakeUnique<base::DictionaryValue>(); |
| 224 global_scoring_params->SetInteger("num_to_return", count_to_fetch); |
| 225 global_scoring_params->SetInteger("sort_type", 1); |
| 226 |
| 227 auto advanced = base::MakeUnique<base::DictionaryValue>(); |
| 228 advanced->Set("local_scoring_params", std::move(local_scoring_params)); |
| 229 advanced->Set("global_scoring_params", std::move(global_scoring_params)); |
| 230 |
| 231 auto request = base::MakeUnique<base::DictionaryValue>(); |
| 232 request->SetString("response_detail_level", "STANDARD"); |
| 233 request->Set("advanced_options", std::move(advanced)); |
| 234 if (!obfuscated_gaia_id.empty()) { |
| 235 request->SetString("obfuscated_gaia_id", obfuscated_gaia_id); |
| 236 } |
| 237 |
| 238 std::string request_json; |
| 239 bool success = base::JSONWriter::WriteWithOptions( |
| 240 *request, base::JSONWriter::OPTIONS_PRETTY_PRINT, &request_json); |
| 241 DCHECK(success); |
| 242 return request_json; |
| 243 } |
| 244 |
240 void NTPSnippetsFetcher::FetchSnippetsImpl(const GURL& url, | 245 void NTPSnippetsFetcher::FetchSnippetsImpl(const GURL& url, |
241 const std::string& auth_header, | 246 const std::string& auth_header, |
242 const std::string& request) { | 247 const std::string& request) { |
243 url_fetcher_ = URLFetcher::Create(url, URLFetcher::POST, this); | 248 url_fetcher_ = URLFetcher::Create(url, URLFetcher::POST, this); |
244 | 249 |
245 url_fetcher_->SetRequestContext(url_request_context_getter_.get()); | 250 url_fetcher_->SetRequestContext(url_request_context_getter_.get()); |
246 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | | 251 url_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES | |
247 net::LOAD_DO_NOT_SAVE_COOKIES); | 252 net::LOAD_DO_NOT_SAVE_COOKIES); |
248 | 253 |
249 data_use_measurement::DataUseUserData::AttachToFetcher( | 254 data_use_measurement::DataUseUserData::AttachToFetcher( |
250 url_fetcher_.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS); | 255 url_fetcher_.get(), data_use_measurement::DataUseUserData::NTP_SNIPPETS); |
251 | 256 |
252 HttpRequestHeaders headers; | 257 HttpRequestHeaders headers; |
253 if (!auth_header.empty()) | 258 if (!auth_header.empty()) |
254 headers.SetHeader("Authorization", auth_header); | 259 headers.SetHeader("Authorization", auth_header); |
255 headers.SetHeader("Content-Type", "application/json; charset=UTF-8"); | 260 headers.SetHeader("Content-Type", "application/json; charset=UTF-8"); |
256 url_fetcher_->SetExtraRequestHeaders(headers.ToString()); | 261 url_fetcher_->SetExtraRequestHeaders(headers.ToString()); |
257 url_fetcher_->SetUploadData("application/json", request); | 262 url_fetcher_->SetUploadData("application/json", request); |
258 // Fetchers are sometimes cancelled because a network change was detected. | 263 // Fetchers are sometimes cancelled because a network change was detected. |
259 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); | 264 url_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3); |
260 // Try to make fetching the files bit more robust even with poor connection. | 265 // Try to make fetching the files bit more robust even with poor connection. |
261 url_fetcher_->SetMaxRetriesOn5xx(3); | 266 url_fetcher_->SetMaxRetriesOn5xx(3); |
262 url_fetcher_->Start(); | 267 url_fetcher_->Start(); |
263 } | 268 } |
264 | 269 |
265 std::string NTPSnippetsFetcher::GetHostRestricts() const { | |
266 std::string host_restricts; | |
267 if (UsesHostRestrictions()) { | |
268 for (const std::string& host : hosts_) { | |
269 if (!host_restricts.empty()) | |
270 host_restricts.push_back(','); | |
271 host_restricts += base::StringPrintf(kHostRestrictFormat, host.c_str()); | |
272 } | |
273 } | |
274 return host_restricts; | |
275 } | |
276 | |
277 bool NTPSnippetsFetcher::UsesHostRestrictions() const { | 270 bool NTPSnippetsFetcher::UsesHostRestrictions() const { |
278 return use_host_restriction_ && | 271 return use_host_restriction_ && |
279 !base::CommandLine::ForCurrentProcess()->HasSwitch( | 272 !base::CommandLine::ForCurrentProcess()->HasSwitch( |
280 switches::kDontRestrict); | 273 switches::kDontRestrict); |
281 } | 274 } |
282 | 275 |
283 bool NTPSnippetsFetcher::UsesAuthentication() const { | 276 bool NTPSnippetsFetcher::UsesAuthentication() const { |
284 return (personalization_ == Personalization::kPersonal || | 277 return (personalization_ == Personalization::kPersonal || |
285 personalization_ == Personalization::kBoth); | 278 personalization_ == Personalization::kBoth); |
286 } | 279 } |
287 | 280 |
288 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated() { | 281 void NTPSnippetsFetcher::FetchSnippetsNonAuthenticated() { |
289 // When not providing OAuth token, we need to pass the Google API key. | 282 // When not providing OAuth token, we need to pass the Google API key. |
290 const std::string& key = is_stable_channel_ | 283 const std::string& key = is_stable_channel_ |
291 ? google_apis::GetAPIKey() | 284 ? google_apis::GetAPIKey() |
292 : google_apis::GetNonStableAPIKey(); | 285 : google_apis::GetNonStableAPIKey(); |
293 GURL url(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, | 286 GURL url(base::StringPrintf(kSnippetsServerNonAuthorizedFormat, |
294 kSnippetsServer, key.c_str())); | 287 kSnippetsServer, key.c_str())); |
295 | 288 |
296 FetchSnippetsImpl(url, std::string(), | 289 FetchSnippetsImpl( |
297 BuildRequest(/*obfuscated_gaia_id=*/std::string(), | 290 url, std::string(), |
298 /*only_return_personalized_results=*/false, | 291 BuildRequest(/*obfuscated_gaia_id=*/std::string(), |
299 /*user_segment=*/std::string(), | 292 /*only_return_personalized_results=*/false, |
300 GetHostRestricts(), count_to_fetch_)); | 293 /*user_segment=*/std::string(), |
| 294 UsesHostRestrictions() ? hosts_ : std::set<std::string>(), |
| 295 count_to_fetch_)); |
301 } | 296 } |
302 | 297 |
303 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( | 298 void NTPSnippetsFetcher::FetchSnippetsAuthenticated( |
304 const std::string& account_id, | 299 const std::string& account_id, |
305 const std::string& oauth_access_token) { | 300 const std::string& oauth_access_token) { |
306 std::string gaia_id = base::StringPrintf(kGaiaIdFormat, account_id.c_str()); | |
307 std::string user_segment = | |
308 base::StringPrintf(kUserSegmentFormat, locale_.c_str()); | |
309 | |
310 FetchSnippetsImpl( | 301 FetchSnippetsImpl( |
311 GURL(kSnippetsServer), | 302 GURL(kSnippetsServer), |
312 base::StringPrintf(kAuthorizationRequestHeaderFormat, | 303 base::StringPrintf(kAuthorizationRequestHeaderFormat, |
313 oauth_access_token.c_str()), | 304 oauth_access_token.c_str()), |
314 BuildRequest(gaia_id, personalization_ == Personalization::kPersonal, | 305 BuildRequest(account_id, personalization_ == Personalization::kPersonal, |
315 user_segment, GetHostRestricts(), count_to_fetch_)); | 306 locale_, |
| 307 UsesHostRestrictions() ? hosts_ : std::set<std::string>(), |
| 308 count_to_fetch_)); |
316 } | 309 } |
317 | 310 |
318 void NTPSnippetsFetcher::StartTokenRequest() { | 311 void NTPSnippetsFetcher::StartTokenRequest() { |
319 OAuth2TokenService::ScopeSet scopes; | 312 OAuth2TokenService::ScopeSet scopes; |
320 scopes.insert(kApiScope); | 313 scopes.insert(kApiScope); |
321 oauth_request_ = token_service_->StartRequest( | 314 oauth_request_ = token_service_->StartRequest( |
322 signin_manager_->GetAuthenticatedAccountId(), scopes, this); | 315 signin_manager_->GetAuthenticatedAccountId(), scopes, this); |
323 } | 316 } |
324 | 317 |
325 //////////////////////////////////////////////////////////////////////////////// | 318 //////////////////////////////////////////////////////////////////////////////// |
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
433 tick_clock_->NowTicks() - fetch_start_time_); | 426 tick_clock_->NowTicks() - fetch_start_time_); |
434 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.FetchResult", | 427 UMA_HISTOGRAM_ENUMERATION("NewTabPage.Snippets.FetchResult", |
435 static_cast<int>(result), | 428 static_cast<int>(result), |
436 static_cast<int>(FetchResult::RESULT_MAX)); | 429 static_cast<int>(FetchResult::RESULT_MAX)); |
437 | 430 |
438 if (!snippets_available_callback_.is_null()) | 431 if (!snippets_available_callback_.is_null()) |
439 snippets_available_callback_.Run(std::move(snippets)); | 432 snippets_available_callback_.Run(std::move(snippets)); |
440 } | 433 } |
441 | 434 |
442 } // namespace ntp_snippets | 435 } // namespace ntp_snippets |
OLD | NEW |