Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(351)

Side by Side Diff: components/suggestions/suggestions_service.cc

Issue 1770843002: SuggestionsService: implement SyncServiceObserver (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix2 Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/suggestions/suggestions_service.h" 5 #include "components/suggestions/suggestions_service.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/feature_list.h" 9 #include "base/feature_list.h"
10 #include "base/location.h" 10 #include "base/location.h"
11 #include "base/metrics/histogram_macros.h" 11 #include "base/metrics/histogram_macros.h"
12 #include "base/metrics/sparse_histogram.h" 12 #include "base/metrics/sparse_histogram.h"
13 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_util.h" 14 #include "base/strings/string_util.h"
15 #include "base/strings/stringprintf.h" 15 #include "base/strings/stringprintf.h"
16 #include "base/thread_task_runner_handle.h" 16 #include "base/thread_task_runner_handle.h"
17 #include "build/build_config.h" 17 #include "build/build_config.h"
18 #include "components/data_use_measurement/core/data_use_user_data.h" 18 #include "components/data_use_measurement/core/data_use_user_data.h"
19 #include "components/google/core/browser/google_util.h" 19 #include "components/google/core/browser/google_util.h"
20 #include "components/pref_registry/pref_registry_syncable.h" 20 #include "components/pref_registry/pref_registry_syncable.h"
21 #include "components/signin/core/browser/signin_manager_base.h" 21 #include "components/signin/core/browser/signin_manager_base.h"
22 #include "components/suggestions/blacklist_store.h" 22 #include "components/suggestions/blacklist_store.h"
23 #include "components/suggestions/image_manager.h" 23 #include "components/suggestions/image_manager.h"
24 #include "components/suggestions/suggestions_store.h" 24 #include "components/suggestions/suggestions_store.h"
25 #include "components/sync_driver/sync_service.h"
25 #include "components/variations/net/variations_http_headers.h" 26 #include "components/variations/net/variations_http_headers.h"
26 #include "google_apis/gaia/gaia_constants.h" 27 #include "google_apis/gaia/gaia_constants.h"
27 #include "google_apis/gaia/oauth2_token_service.h" 28 #include "google_apis/gaia/oauth2_token_service.h"
28 #include "net/base/escape.h" 29 #include "net/base/escape.h"
29 #include "net/base/load_flags.h" 30 #include "net/base/load_flags.h"
30 #include "net/base/net_errors.h" 31 #include "net/base/net_errors.h"
31 #include "net/base/url_util.h" 32 #include "net/base/url_util.h"
32 #include "net/http/http_response_headers.h" 33 #include "net/http/http_response_headers.h"
33 #include "net/http/http_status_code.h" 34 #include "net/http/http_status_code.h"
34 #include "net/http/http_util.h" 35 #include "net/http/http_util.h"
35 #include "net/url_request/url_fetcher.h" 36 #include "net/url_request/url_fetcher.h"
36 #include "net/url_request/url_request_status.h" 37 #include "net/url_request/url_request_status.h"
37 38
38 using base::TimeDelta; 39 using base::TimeDelta;
39 using base::TimeTicks; 40 using base::TimeTicks;
40 41
41 namespace suggestions { 42 namespace suggestions {
42 43
43 namespace { 44 namespace {
44 45
46 // Establishes the different sync states that matter to SuggestionsService.
47 // There are three different concepts in the sync service: initialized, sync
48 // enabled and history sync enabled.
49 enum SyncState {
50 // State: Sync service is not initialized, yet not disabled. History sync
51 // state is unknown (since not initialized).
52 // Behavior: Does not issue a server request, but serves from cache if
53 // available.
54 NOT_INITIALIZED_ENABLED,
55
56 // State: Sync service is initialized, sync is enabled and history sync is
57 // enabled.
58 // Behavior: Update suggestions from the server. Serve from cache on timeout.
59 INITIALIZED_ENABLED_HISTORY,
60
61 // State: Sync service is disabled or history sync is disabled.
62 // Behavior: Do not issue a server request. Clear the cache. Serve empty
63 // suggestions.
64 SYNC_OR_HISTORY_SYNC_DISABLED,
65 };
66
67 SyncState GetSyncState(sync_driver::SyncService* sync) {
68 if (!sync || !sync->CanSyncStart())
69 return SYNC_OR_HISTORY_SYNC_DISABLED;
70 if (!sync->IsSyncActive() || !sync->ConfigurationDone())
71 return NOT_INITIALIZED_ENABLED;
72 return sync->GetActiveDataTypes().Has(syncer::HISTORY_DELETE_DIRECTIVES)
73 ? INITIALIZED_ENABLED_HISTORY
74 : SYNC_OR_HISTORY_SYNC_DISABLED;
75 }
76
45 // Used to UMA log the state of the last response from the server. 77 // Used to UMA log the state of the last response from the server.
46 enum SuggestionsResponseState { 78 enum SuggestionsResponseState {
47 RESPONSE_EMPTY, 79 RESPONSE_EMPTY,
48 RESPONSE_INVALID, 80 RESPONSE_INVALID,
49 RESPONSE_VALID, 81 RESPONSE_VALID,
50 RESPONSE_STATE_SIZE 82 RESPONSE_STATE_SIZE
51 }; 83 };
52 84
53 // Will log the supplied response |state|. 85 // Will log the supplied response |state|.
54 void LogResponseState(SuggestionsResponseState state) { 86 void LogResponseState(SuggestionsResponseState state) {
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after
160 const SigninManagerBase* signin_manager_; 192 const SigninManagerBase* signin_manager_;
161 OAuth2TokenService* token_service_; 193 OAuth2TokenService* token_service_;
162 194
163 TokenCallback callback_; 195 TokenCallback callback_;
164 scoped_ptr<OAuth2TokenService::Request> token_request_; 196 scoped_ptr<OAuth2TokenService::Request> token_request_;
165 }; 197 };
166 198
167 SuggestionsService::SuggestionsService( 199 SuggestionsService::SuggestionsService(
168 const SigninManagerBase* signin_manager, 200 const SigninManagerBase* signin_manager,
169 OAuth2TokenService* token_service, 201 OAuth2TokenService* token_service,
202 sync_driver::SyncService* sync_service,
170 net::URLRequestContextGetter* url_request_context, 203 net::URLRequestContextGetter* url_request_context,
171 scoped_ptr<SuggestionsStore> suggestions_store, 204 scoped_ptr<SuggestionsStore> suggestions_store,
172 scoped_ptr<ImageManager> thumbnail_manager, 205 scoped_ptr<ImageManager> thumbnail_manager,
173 scoped_ptr<BlacklistStore> blacklist_store) 206 scoped_ptr<BlacklistStore> blacklist_store)
174 : url_request_context_(url_request_context), 207 : sync_service_(sync_service),
208 sync_service_observer_(this),
209 url_request_context_(url_request_context),
175 suggestions_store_(std::move(suggestions_store)), 210 suggestions_store_(std::move(suggestions_store)),
176 thumbnail_manager_(std::move(thumbnail_manager)), 211 thumbnail_manager_(std::move(thumbnail_manager)),
177 blacklist_store_(std::move(blacklist_store)), 212 blacklist_store_(std::move(blacklist_store)),
178 scheduling_delay_(TimeDelta::FromSeconds(kDefaultSchedulingDelaySec)), 213 scheduling_delay_(TimeDelta::FromSeconds(kDefaultSchedulingDelaySec)),
179 token_fetcher_(new AccessTokenFetcher(signin_manager, token_service)), 214 token_fetcher_(new AccessTokenFetcher(signin_manager, token_service)),
180 weak_ptr_factory_(this) {} 215 weak_ptr_factory_(this) {
216 // |sync_service_| is null if switches::kDisableSync is set (tests use that).
217 if (sync_service_)
218 sync_service_observer_.Add(sync_service_);
219 // Immediately get the current sync state, so we'll flush the cache if
220 // necessary.
221 OnStateChanged();
222 }
181 223
182 SuggestionsService::~SuggestionsService() {} 224 SuggestionsService::~SuggestionsService() {}
183 225
184 void SuggestionsService::FetchSuggestionsData( 226 bool SuggestionsService::FetchSuggestionsData() {
185 SyncState sync_state,
186 const ResponseCallback& callback) {
187 DCHECK(thread_checker_.CalledOnValidThread()); 227 DCHECK(thread_checker_.CalledOnValidThread());
188 switch (sync_state) { 228 // If sync state allows, issue a network request to refresh the suggestions.
189 case SYNC_OR_HISTORY_SYNC_DISABLED: 229 if (GetSyncState(sync_service_) != INITIALIZED_ENABLED_HISTORY)
190 // Cancel any ongoing request, to stop interacting with the server. 230 return false;
191 pending_request_.reset(nullptr); 231 IssueRequestIfNoneOngoing(BuildSuggestionsURL());
192 suggestions_store_->ClearSuggestions(); 232 return true;
193 if (!callback.is_null()) 233 }
194 callback.Run(SuggestionsProfile());
195 break;
196 case INITIALIZED_ENABLED_HISTORY:
197 case NOT_INITIALIZED_ENABLED:
198 // TODO(treib): For NOT_INITIALIZED_ENABLED, we shouldn't issue a network
199 // request. Verify that that won't break anything.
200 // Sync is enabled. Serve previously cached suggestions if available, else
201 // an empty set of suggestions.
202 ServeFromCache(callback);
203 234
204 // Issue a network request to refresh the suggestions in the cache. 235 SuggestionsProfile SuggestionsService::GetSuggestionsDataFromCache() const {
205 IssueRequestIfNoneOngoing(BuildSuggestionsURL()); 236 SuggestionsProfile suggestions;
206 break; 237 // In case of empty cache or error, |suggestions| stays empty.
207 } 238 suggestions_store_->LoadSuggestions(&suggestions);
239 thumbnail_manager_->Initialize(suggestions);
240 blacklist_store_->FilterSuggestions(&suggestions);
241 return suggestions;
242 }
243
244 scoped_ptr<SuggestionsService::ResponseCallbackList::Subscription>
245 SuggestionsService::AddCallback(const ResponseCallback& callback) {
246 return callback_list_.Add(callback);
208 } 247 }
209 248
210 void SuggestionsService::GetPageThumbnail(const GURL& url, 249 void SuggestionsService::GetPageThumbnail(const GURL& url,
211 const BitmapCallback& callback) { 250 const BitmapCallback& callback) {
212 thumbnail_manager_->GetImageForURL(url, callback); 251 thumbnail_manager_->GetImageForURL(url, callback);
213 } 252 }
214 253
215 void SuggestionsService::GetPageThumbnailWithURL( 254 void SuggestionsService::GetPageThumbnailWithURL(
216 const GURL& url, 255 const GURL& url,
217 const GURL& thumbnail_url, 256 const GURL& thumbnail_url,
218 const BitmapCallback& callback) { 257 const BitmapCallback& callback) {
219 thumbnail_manager_->AddImageURL(url, thumbnail_url); 258 thumbnail_manager_->AddImageURL(url, thumbnail_url);
220 GetPageThumbnail(url, callback); 259 GetPageThumbnail(url, callback);
221 } 260 }
222 261
223 void SuggestionsService::BlacklistURL(const GURL& candidate_url, 262 bool SuggestionsService::BlacklistURL(const GURL& candidate_url) {
224 const ResponseCallback& callback,
225 const base::Closure& fail_callback) {
226 DCHECK(thread_checker_.CalledOnValidThread()); 263 DCHECK(thread_checker_.CalledOnValidThread());
227 264
228 if (!blacklist_store_->BlacklistUrl(candidate_url)) { 265 if (!blacklist_store_->BlacklistUrl(candidate_url))
229 if (!fail_callback.is_null()) 266 return false;
230 fail_callback.Run();
231 return;
232 }
233 267
234 ServeFromCache(callback); 268 callback_list_.Notify(GetSuggestionsDataFromCache());
269
235 // Blacklist uploads are scheduled on any request completion, so only schedule 270 // Blacklist uploads are scheduled on any request completion, so only schedule
236 // an upload if there is no ongoing request. 271 // an upload if there is no ongoing request.
237 if (!pending_request_.get()) 272 if (!pending_request_.get())
238 ScheduleBlacklistUpload(); 273 ScheduleBlacklistUpload();
274
275 return true;
239 } 276 }
240 277
241 void SuggestionsService::UndoBlacklistURL(const GURL& url, 278 bool SuggestionsService::UndoBlacklistURL(const GURL& url) {
242 const ResponseCallback& callback,
243 const base::Closure& fail_callback) {
244 DCHECK(thread_checker_.CalledOnValidThread()); 279 DCHECK(thread_checker_.CalledOnValidThread());
245 TimeDelta time_delta; 280 TimeDelta time_delta;
246 if (blacklist_store_->GetTimeUntilURLReadyForUpload(url, &time_delta) && 281 if (blacklist_store_->GetTimeUntilURLReadyForUpload(url, &time_delta) &&
247 time_delta > TimeDelta::FromSeconds(0) && 282 time_delta > TimeDelta::FromSeconds(0) &&
248 blacklist_store_->RemoveUrl(url)) { 283 blacklist_store_->RemoveUrl(url)) {
249 // The URL was not yet candidate for upload to the server and could be 284 // The URL was not yet candidate for upload to the server and could be
250 // removed from the blacklist. 285 // removed from the blacklist.
251 ServeFromCache(callback); 286 callback_list_.Notify(GetSuggestionsDataFromCache());
252 return; 287 return true;
253 } 288 }
254 if (!fail_callback.is_null()) 289 return false;
255 fail_callback.Run();
256 } 290 }
257 291
258 void SuggestionsService::ClearBlacklist(const ResponseCallback& callback) { 292 void SuggestionsService::ClearBlacklist() {
259 DCHECK(thread_checker_.CalledOnValidThread()); 293 DCHECK(thread_checker_.CalledOnValidThread());
260 blacklist_store_->ClearBlacklist(); 294 blacklist_store_->ClearBlacklist();
295 callback_list_.Notify(GetSuggestionsDataFromCache());
261 IssueRequestIfNoneOngoing(BuildSuggestionsBlacklistClearURL()); 296 IssueRequestIfNoneOngoing(BuildSuggestionsBlacklistClearURL());
262 ServeFromCache(callback);
263 } 297 }
264 298
265 // static 299 // static
266 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request, 300 bool SuggestionsService::GetBlacklistedUrl(const net::URLFetcher& request,
267 GURL* url) { 301 GURL* url) {
268 bool is_blacklist_request = base::StartsWith( 302 bool is_blacklist_request = base::StartsWith(
269 request.GetOriginalURL().spec(), BuildSuggestionsBlacklistURLPrefix(), 303 request.GetOriginalURL().spec(), BuildSuggestionsBlacklistURLPrefix(),
270 base::CompareCase::SENSITIVE); 304 base::CompareCase::SENSITIVE);
271 if (!is_blacklist_request) return false; 305 if (!is_blacklist_request) return false;
272 306
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 net::EscapeQueryParamValue(candidate_url.spec(), true)); 350 net::EscapeQueryParamValue(candidate_url.spec(), true));
317 } 351 }
318 352
319 // static 353 // static
320 GURL SuggestionsService::BuildSuggestionsBlacklistClearURL() { 354 GURL SuggestionsService::BuildSuggestionsBlacklistClearURL() {
321 return GURL(base::StringPrintf(kSuggestionsBlacklistClearURLFormat, 355 return GURL(base::StringPrintf(kSuggestionsBlacklistClearURLFormat,
322 GetGoogleBaseURL().spec().c_str(), 356 GetGoogleBaseURL().spec().c_str(),
323 kDeviceType)); 357 kDeviceType));
324 } 358 }
325 359
360 void SuggestionsService::OnStateChanged() {
361 switch (GetSyncState(sync_service_)) {
362 case SYNC_OR_HISTORY_SYNC_DISABLED:
363 // Cancel any ongoing request, to stop interacting with the server.
364 pending_request_.reset(nullptr);
365 suggestions_store_->ClearSuggestions();
366 callback_list_.Notify(SuggestionsProfile());
367 break;
368 case NOT_INITIALIZED_ENABLED:
369 // Keep the cache (if any), but don't refresh.
370 break;
371 case INITIALIZED_ENABLED_HISTORY:
372 // If we have any observers, issue a network request to refresh the
373 // suggestions in the cache.
374 if (!callback_list_.empty())
375 IssueRequestIfNoneOngoing(BuildSuggestionsURL());
Mathieu 2016/03/08 14:52:34 So the first OnStateChanged that is called in the
Marc Treib 2016/03/08 15:31:17 The main reason I put this in was to avoid lots of
376 break;
377 }
378 }
379
326 void SuggestionsService::SetDefaultExpiryTimestamp( 380 void SuggestionsService::SetDefaultExpiryTimestamp(
327 SuggestionsProfile* suggestions, 381 SuggestionsProfile* suggestions,
328 int64_t default_timestamp_usec) { 382 int64_t default_timestamp_usec) {
329 for (int i = 0; i < suggestions->suggestions_size(); ++i) { 383 for (int i = 0; i < suggestions->suggestions_size(); ++i) {
330 ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i); 384 ChromeSuggestion* suggestion = suggestions->mutable_suggestions(i);
331 // Do not set expiry if the server has already provided a more specific 385 // Do not set expiry if the server has already provided a more specific
332 // expiry time for this suggestion. 386 // expiry time for this suggestion.
333 if (!suggestion->has_expiry_ts()) { 387 if (!suggestion->has_expiry_ts()) {
334 suggestion->set_expiry_ts(default_timestamp_usec); 388 suggestion->set_expiry_ts(default_timestamp_usec);
335 } 389 }
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after
448 int64_t now_usec = 502 int64_t now_usec =
449 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch()) 503 (base::Time::NowFromSystemTime() - base::Time::UnixEpoch())
450 .ToInternalValue(); 504 .ToInternalValue();
451 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec); 505 SetDefaultExpiryTimestamp(&suggestions, now_usec + kDefaultExpiryUsec);
452 PopulateExtraData(&suggestions); 506 PopulateExtraData(&suggestions);
453 suggestions_store_->StoreSuggestions(suggestions); 507 suggestions_store_->StoreSuggestions(suggestions);
454 } else { 508 } else {
455 LogResponseState(RESPONSE_INVALID); 509 LogResponseState(RESPONSE_INVALID);
456 } 510 }
457 511
512 callback_list_.Notify(GetSuggestionsDataFromCache());
513
458 UpdateBlacklistDelay(true); 514 UpdateBlacklistDelay(true);
459 ScheduleBlacklistUpload(); 515 ScheduleBlacklistUpload();
460 } 516 }
461 517
462 void SuggestionsService::PopulateExtraData(SuggestionsProfile* suggestions) { 518 void SuggestionsService::PopulateExtraData(SuggestionsProfile* suggestions) {
463 for (int i = 0; i < suggestions->suggestions_size(); ++i) { 519 for (int i = 0; i < suggestions->suggestions_size(); ++i) {
464 suggestions::ChromeSuggestion* s = suggestions->mutable_suggestions(i); 520 suggestions::ChromeSuggestion* s = suggestions->mutable_suggestions(i);
465 if (!s->has_favicon_url() || s->favicon_url().empty()) { 521 if (!s->has_favicon_url() || s->favicon_url().empty()) {
466 s->set_favicon_url(base::StringPrintf(kFaviconURL, s->url().c_str())); 522 s->set_favicon_url(base::StringPrintf(kFaviconURL, s->url().c_str()));
467 } 523 }
468 if (!s->has_impression_url() || s->impression_url().empty()) { 524 if (!s->has_impression_url() || s->impression_url().empty()) {
469 s->set_impression_url( 525 s->set_impression_url(
470 base::StringPrintf( 526 base::StringPrintf(
471 kPingURL, static_cast<long long>(suggestions->timestamp()), -1)); 527 kPingURL, static_cast<long long>(suggestions->timestamp()), -1));
472 } 528 }
473 529
474 if (!s->has_click_url() || s->click_url().empty()) { 530 if (!s->has_click_url() || s->click_url().empty()) {
475 s->set_click_url(base::StringPrintf( 531 s->set_click_url(base::StringPrintf(
476 kPingURL, static_cast<long long>(suggestions->timestamp()), i)); 532 kPingURL, static_cast<long long>(suggestions->timestamp()), i));
477 } 533 }
478 } 534 }
479 } 535 }
480 536
481 void SuggestionsService::Shutdown() { 537 void SuggestionsService::Shutdown() {
482 // Cancel pending request. 538 // Cancel pending request.
483 pending_request_.reset(nullptr); 539 pending_request_.reset(nullptr);
484 } 540 }
485 541
486 void SuggestionsService::ServeFromCache(const ResponseCallback& callback) {
487 SuggestionsProfile suggestions;
488 // In case of empty cache or error, |suggestions| stays empty.
489 suggestions_store_->LoadSuggestions(&suggestions);
490 thumbnail_manager_->Initialize(suggestions);
491 blacklist_store_->FilterSuggestions(&suggestions);
492 if (!callback.is_null())
493 callback.Run(suggestions);
494 }
495
496 void SuggestionsService::ScheduleBlacklistUpload() { 542 void SuggestionsService::ScheduleBlacklistUpload() {
497 DCHECK(thread_checker_.CalledOnValidThread()); 543 DCHECK(thread_checker_.CalledOnValidThread());
498 TimeDelta time_delta; 544 TimeDelta time_delta;
499 if (blacklist_store_->GetTimeUntilReadyForUpload(&time_delta)) { 545 if (blacklist_store_->GetTimeUntilReadyForUpload(&time_delta)) {
500 // Blacklist cache is not empty: schedule. 546 // Blacklist cache is not empty: schedule.
501 base::Closure blacklist_cb = 547 base::Closure blacklist_cb =
502 base::Bind(&SuggestionsService::UploadOneFromBlacklist, 548 base::Bind(&SuggestionsService::UploadOneFromBlacklist,
503 weak_ptr_factory_.GetWeakPtr()); 549 weak_ptr_factory_.GetWeakPtr());
504 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( 550 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
505 FROM_HERE, blacklist_cb, time_delta + scheduling_delay_); 551 FROM_HERE, blacklist_cb, time_delta + scheduling_delay_);
(...skipping 23 matching lines...) Expand all
529 scheduling_delay_ = TimeDelta::FromSeconds(kDefaultSchedulingDelaySec); 575 scheduling_delay_ = TimeDelta::FromSeconds(kDefaultSchedulingDelaySec);
530 } else { 576 } else {
531 TimeDelta candidate_delay = 577 TimeDelta candidate_delay =
532 scheduling_delay_ * kSchedulingBackoffMultiplier; 578 scheduling_delay_ * kSchedulingBackoffMultiplier;
533 if (candidate_delay < TimeDelta::FromSeconds(kSchedulingMaxDelaySec)) 579 if (candidate_delay < TimeDelta::FromSeconds(kSchedulingMaxDelaySec))
534 scheduling_delay_ = candidate_delay; 580 scheduling_delay_ = candidate_delay;
535 } 581 }
536 } 582 }
537 583
538 } // namespace suggestions 584 } // namespace suggestions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698