Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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_service.h" | 5 #include "components/ntp_snippets/ntp_snippets_service.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <iterator> | 8 #include <iterator> |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 187 NTPSnippetsService::NTPSnippetsService( | 187 NTPSnippetsService::NTPSnippetsService( |
| 188 PrefService* pref_service, | 188 PrefService* pref_service, |
| 189 sync_driver::SyncService* sync_service, | 189 sync_driver::SyncService* sync_service, |
| 190 SuggestionsService* suggestions_service, | 190 SuggestionsService* suggestions_service, |
| 191 const std::string& application_language_code, | 191 const std::string& application_language_code, |
| 192 NTPSnippetsScheduler* scheduler, | 192 NTPSnippetsScheduler* scheduler, |
| 193 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 193 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| 194 std::unique_ptr<ImageFetcher> image_fetcher, | 194 std::unique_ptr<ImageFetcher> image_fetcher, |
| 195 std::unique_ptr<NTPSnippetsDatabase> database) | 195 std::unique_ptr<NTPSnippetsDatabase> database) |
| 196 : state_(State::NOT_INITED), | 196 : state_(State::NOT_INITED), |
| 197 enabled_(false), | 197 explicitly_disabled_(false), |
| 198 pref_service_(pref_service), | 198 pref_service_(pref_service), |
| 199 sync_service_(sync_service), | 199 sync_service_(sync_service), |
| 200 sync_service_observer_(this), | 200 sync_service_observer_(this), |
| 201 suggestions_service_(suggestions_service), | 201 suggestions_service_(suggestions_service), |
| 202 application_language_code_(application_language_code), | 202 application_language_code_(application_language_code), |
| 203 scheduler_(scheduler), | 203 scheduler_(scheduler), |
| 204 snippets_fetcher_(std::move(snippets_fetcher)), | 204 snippets_fetcher_(std::move(snippets_fetcher)), |
| 205 image_fetcher_(std::move(image_fetcher)), | 205 image_fetcher_(std::move(image_fetcher)), |
| 206 database_(std::move(database)) { | 206 database_(std::move(database)) { |
| 207 snippets_fetcher_->SetCallback(base::Bind( | 207 snippets_fetcher_->SetCallback(base::Bind( |
| 208 &NTPSnippetsService::OnFetchFinished, base::Unretained(this))); | 208 &NTPSnippetsService::OnFetchFinished, base::Unretained(this))); |
| 209 | 209 |
| 210 // |sync_service_| can be null in tests or if sync is disabled. | 210 // |sync_service_| can be null in tests or if sync is disabled. |
| 211 if (sync_service_) | 211 if (sync_service_) |
| 212 sync_service_observer_.Add(sync_service_); | 212 sync_service_observer_.Add(sync_service_); |
| 213 } | 213 } |
| 214 | 214 |
| 215 NTPSnippetsService::~NTPSnippetsService() { | 215 NTPSnippetsService::~NTPSnippetsService() { |
| 216 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); | 216 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); |
| 217 } | 217 } |
| 218 | 218 |
| 219 // static | 219 // static |
| 220 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { | 220 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
| 221 registry->RegisterListPref(prefs::kSnippets); | 221 registry->RegisterListPref(prefs::kSnippets); |
| 222 registry->RegisterListPref(prefs::kDiscardedSnippets); | 222 registry->RegisterListPref(prefs::kDiscardedSnippets); |
| 223 registry->RegisterListPref(prefs::kSnippetHosts); | 223 registry->RegisterListPref(prefs::kSnippetHosts); |
| 224 } | 224 } |
| 225 | 225 |
| 226 void NTPSnippetsService::Init(bool enabled) { | 226 void NTPSnippetsService::Init(bool enabled) { |
|
Marc Treib
2016/05/24 09:04:46
Heads-up: Michael's https://codereview.chromium.or
dgn
2016/05/24 18:13:30
Acknowledged.
| |
| 227 DCHECK(state_ == State::NOT_INITED); | 227 DCHECK(state_ == State::NOT_INITED); |
| 228 state_ = State::INITED; | 228 explicitly_disabled_ = !enabled; |
| 229 | 229 EnterState(GetDisabledReason() == DisabledReason::NONE ? State::INITED |
| 230 enabled_ = enabled; | 230 : State::DISABLED); |
| 231 if (enabled_) { | |
| 232 database_->Load(base::Bind(&NTPSnippetsService::OnSnippetsLoaded, | |
| 233 base::Unretained(this))); | |
| 234 } | |
| 235 | |
| 236 RescheduleFetching(); | |
| 237 } | 231 } |
| 238 | 232 |
| 233 // Inherited from KeyedService. | |
| 239 void NTPSnippetsService::Shutdown() { | 234 void NTPSnippetsService::Shutdown() { |
| 240 DCHECK(state_ == State::INITED || state_ == State::LOADED); | 235 EnterState(State::SHUT_DOWN); |
| 241 state_ = State::SHUT_DOWN; | |
| 242 | 236 |
| 243 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 237 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 244 NTPSnippetsServiceShutdown()); | 238 NTPSnippetsServiceShutdown()); |
| 245 expiry_timer_.Stop(); | 239 expiry_timer_.Stop(); |
| 246 suggestions_service_subscription_.reset(); | 240 suggestions_service_subscription_.reset(); |
| 247 enabled_ = false; | |
| 248 } | 241 } |
| 249 | 242 |
| 250 void NTPSnippetsService::FetchSnippets() { | 243 void NTPSnippetsService::FetchSnippets() { |
| 251 FetchSnippetsFromHosts(GetSuggestionsHosts()); | 244 FetchSnippetsFromHosts(GetSuggestionsHosts()); |
| 252 } | 245 } |
| 253 | 246 |
| 254 void NTPSnippetsService::FetchSnippetsFromHosts( | 247 void NTPSnippetsService::FetchSnippetsFromHosts( |
| 255 const std::set<std::string>& hosts) { | 248 const std::set<std::string>& hosts) { |
| 256 if (!loaded()) | 249 if (!loaded()) |
| 257 return; | 250 return; |
| 258 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | 251 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, |
| 259 kMaxSnippetCount); | 252 kMaxSnippetCount); |
| 260 } | 253 } |
| 261 | 254 |
| 262 void NTPSnippetsService::RescheduleFetching() { | 255 void NTPSnippetsService::RescheduleFetching() { |
| 263 // The scheduler only exists on Android so far, it's null on other platforms. | 256 // The scheduler only exists on Android so far, it's null on other platforms. |
| 264 if (!scheduler_) | 257 if (!scheduler_) |
| 265 return; | 258 return; |
| 266 | 259 |
| 267 if (enabled_) { | 260 if (state_ == State::INITED || state_ == State::READY) { |
| 268 base::Time now = base::Time::Now(); | 261 base::Time now = base::Time::Now(); |
| 269 scheduler_->Schedule( | 262 scheduler_->Schedule( |
| 270 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), | 263 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), |
| 271 GetFetchingIntervalFallback(), GetRescheduleTime(now)); | 264 GetFetchingIntervalFallback(), GetRescheduleTime(now)); |
| 272 } else { | 265 } else { |
| 273 scheduler_->Unschedule(); | 266 scheduler_->Unschedule(); |
| 274 } | 267 } |
| 275 } | 268 } |
| 276 | 269 |
| 277 void NTPSnippetsService::FetchSnippetImage( | 270 void NTPSnippetsService::FetchSnippetImage( |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 291 const NTPSnippet& snippet = *it->get(); | 284 const NTPSnippet& snippet = *it->get(); |
| 292 // TODO(treib): Make ImageFetcher take a string instead of a GURL as an | 285 // TODO(treib): Make ImageFetcher take a string instead of a GURL as an |
| 293 // identifier. | 286 // identifier. |
| 294 image_fetcher_->StartOrQueueNetworkRequest( | 287 image_fetcher_->StartOrQueueNetworkRequest( |
| 295 GURL(snippet.id()), snippet.salient_image_url(), | 288 GURL(snippet.id()), snippet.salient_image_url(), |
| 296 base::Bind(WrapImageFetchedCallback, callback)); | 289 base::Bind(WrapImageFetchedCallback, callback)); |
| 297 // TODO(treib): Cache/persist the snippet image. | 290 // TODO(treib): Cache/persist the snippet image. |
| 298 } | 291 } |
| 299 | 292 |
| 300 void NTPSnippetsService::ClearSnippets() { | 293 void NTPSnippetsService::ClearSnippets() { |
| 301 if (!loaded()) | 294 if (!loaded() && state_ != State::DISABLED) |
| 302 return; | 295 return; |
| 303 | 296 |
| 304 database_->Delete(snippets_); | 297 database_->Delete(snippets_); |
| 305 snippets_.clear(); | 298 snippets_.clear(); |
| 306 | 299 |
| 307 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 300 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 308 NTPSnippetsServiceLoaded()); | 301 NTPSnippetsServiceLoaded()); |
| 309 } | 302 } |
| 310 | 303 |
| 311 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { | 304 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 353 } | 346 } |
| 354 | 347 |
| 355 void NTPSnippetsService::AddObserver(NTPSnippetsServiceObserver* observer) { | 348 void NTPSnippetsService::AddObserver(NTPSnippetsServiceObserver* observer) { |
| 356 observers_.AddObserver(observer); | 349 observers_.AddObserver(observer); |
| 357 } | 350 } |
| 358 | 351 |
| 359 void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) { | 352 void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) { |
| 360 observers_.RemoveObserver(observer); | 353 observers_.RemoveObserver(observer); |
| 361 } | 354 } |
| 362 | 355 |
| 356 DisabledReason NTPSnippetsService::GetDisabledReason() { | |
| 357 if (explicitly_disabled_) | |
| 358 return DisabledReason::EXPLICITLY_DISABLED; | |
| 359 | |
| 360 if (!sync_service_ || !sync_service_->CanSyncStart() || | |
| 361 !sync_service_->GetActiveDataTypes().Has( | |
| 362 syncer::HISTORY_DELETE_DIRECTIVES)) | |
| 363 return DisabledReason::HISTORY_SYNC_DISABLED; | |
| 364 | |
| 365 return DisabledReason::NONE; | |
| 366 } | |
| 367 | |
| 363 // static | 368 // static |
| 364 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 369 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 365 return kMaxSnippetCount; | 370 return kMaxSnippetCount; |
| 366 } | 371 } |
| 367 | 372 |
| 368 //////////////////////////////////////////////////////////////////////////////// | 373 //////////////////////////////////////////////////////////////////////////////// |
| 369 // Private methods | 374 // Private methods |
| 370 | 375 |
| 371 // sync_driver::SyncServiceObserver implementation. | 376 // sync_driver::SyncServiceObserver implementation. |
| 372 void NTPSnippetsService::OnStateChanged() { | 377 void NTPSnippetsService::OnStateChanged() { |
| 373 if (IsSyncStateIncompatible()) { | 378 bool enabled = GetDisabledReason() == DisabledReason::NONE; |
| 374 ClearSnippets(); | 379 |
| 375 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 380 if (enabled == (state_ != State::DISABLED)) |
| 376 NTPSnippetsServiceDisabled()); | 381 return; |
| 382 | |
| 383 // |GetDisabledReason| is strict and marks the state as disabled even in cases | |
| 384 // where we miss some data. During initialization or when the user is changing | |
| 385 // the sync settings, we can run in false negatives where we mark the state | |
| 386 // as disabled. We wait do nothing here until the configuration is completely | |
| 387 // loaded to avoid unnecessary state changes. | |
|
Marc Treib
2016/05/24 09:04:46
Hm, this kinda seems wrong. IMO we should have a c
dgn
2016/05/24 18:13:30
I explicitly handle the UNKNOWN state now. WDYT?
Marc Treib
2016/05/25 14:47:32
SGTM!
| |
| 388 if (sync_service_ && sync_service_->IsSyncActive() && | |
| 389 !sync_service_->ConfigurationDone()) { | |
| 390 DVLOG(1) << "Sync configuration not done, skipping a state change."; | |
| 377 return; | 391 return; |
| 378 } | 392 } |
| 379 | 393 |
| 380 // TODO(dgn): When the data sources change, we may want to not fetch here, | 394 EnterState(enabled ? State::INITED : State::DISABLED); |
|
Marc Treib
2016/05/24 09:04:46
What if we were already in the READY state?
dgn
2016/05/24 18:13:30
Handled same state transitions as noops in EnterSt
| |
| 381 // as we will get notified of changes from the snippet sources as well, and | |
| 382 // start multiple fetches. | |
| 383 FetchSnippets(); | |
| 384 } | 395 } |
| 385 | 396 |
| 386 void NTPSnippetsService::OnSnippetsLoaded(NTPSnippet::PtrVector snippets) { | 397 void NTPSnippetsService::OnSnippetsLoaded(NTPSnippet::PtrVector snippets) { |
| 387 DCHECK(state_ == State::INITED || state_ == State::SHUT_DOWN); | 398 DCHECK(state_ == State::INITED || state_ == State::SHUT_DOWN); |
| 388 if (state_ == State::SHUT_DOWN) | 399 if (state_ == State::SHUT_DOWN) |
| 389 return; | 400 return; // TODO(dgn) how can we get here? Just race conditons? |
|
Marc Treib
2016/05/24 09:04:46
I think it can happen if we started a fetch, then
dgn
2016/05/24 18:13:30
Acknowledged.
| |
| 390 state_ = State::LOADED; | 401 EnterState(State::LOADED); |
| 391 | 402 |
| 392 DCHECK(snippets_.empty()); | 403 DCHECK(snippets_.empty()); |
| 393 DCHECK(discarded_snippets_.empty()); | 404 DCHECK(discarded_snippets_.empty()); |
| 394 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | 405 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 395 if (snippet->is_discarded()) | 406 if (snippet->is_discarded()) |
| 396 discarded_snippets_.emplace_back(std::move(snippet)); | 407 discarded_snippets_.emplace_back(std::move(snippet)); |
| 397 else | 408 else |
| 398 snippets_.emplace_back(std::move(snippet)); | 409 snippets_.emplace_back(std::move(snippet)); |
| 399 } | 410 } |
| 400 std::sort(snippets_.begin(), snippets_.end(), | 411 std::sort(snippets_.begin(), snippets_.end(), |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 412 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); | 423 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); |
| 413 } | 424 } |
| 414 | 425 |
| 415 // If we don't have any snippets yet, start a fetch. | 426 // If we don't have any snippets yet, start a fetch. |
| 416 if (snippets_.empty()) | 427 if (snippets_.empty()) |
| 417 FetchSnippets(); | 428 FetchSnippets(); |
| 418 } | 429 } |
| 419 | 430 |
| 420 void NTPSnippetsService::OnSuggestionsChanged( | 431 void NTPSnippetsService::OnSuggestionsChanged( |
| 421 const SuggestionsProfile& suggestions) { | 432 const SuggestionsProfile& suggestions) { |
| 422 DCHECK(loaded()); | 433 DCHECK(loaded() || state_ == State::DISABLED); |
| 423 | 434 |
| 424 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); | 435 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); |
| 425 if (hosts == GetSnippetHostsFromPrefs()) | 436 if (hosts == GetSnippetHostsFromPrefs()) |
| 426 return; | 437 return; |
| 427 | 438 |
| 428 // Remove existing snippets that aren't in the suggestions anymore. | 439 // Remove existing snippets that aren't in the suggestions anymore. |
| 429 // TODO(treib,maybelle): If there is another source with an allowed host, | 440 // TODO(treib,maybelle): If there is another source with an allowed host, |
| 430 // then we should fall back to that. | 441 // then we should fall back to that. |
| 431 // First, move them over into |to_delete|. | 442 // First, move them over into |to_delete|. |
| 432 NTPSnippet::PtrVector to_delete; | 443 NTPSnippet::PtrVector to_delete; |
| (...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 601 for (const auto& snippet : discarded_snippets_) { | 612 for (const auto& snippet : discarded_snippets_) { |
| 602 if (snippet->expiry_date() < next_expiry) | 613 if (snippet->expiry_date() < next_expiry) |
| 603 next_expiry = snippet->expiry_date(); | 614 next_expiry = snippet->expiry_date(); |
| 604 } | 615 } |
| 605 DCHECK_GT(next_expiry, expiry); | 616 DCHECK_GT(next_expiry, expiry); |
| 606 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, | 617 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, |
| 607 base::Bind(&NTPSnippetsService::LoadingSnippetsFinished, | 618 base::Bind(&NTPSnippetsService::LoadingSnippetsFinished, |
| 608 base::Unretained(this))); | 619 base::Unretained(this))); |
| 609 } | 620 } |
| 610 | 621 |
| 611 bool NTPSnippetsService::IsSyncStateIncompatible() { | 622 void NTPSnippetsService::Enable() { |
| 612 if (!sync_service_ || !sync_service_->CanSyncStart()) | 623 database_->Load(base::Bind(&NTPSnippetsService::OnSnippetsLoaded, |
| 613 return true; | 624 base::Unretained(this))); |
| 614 if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone()) | 625 RescheduleFetching(); |
| 615 return false; // Sync service is not initialized, yet not disabled. | 626 } |
| 616 return !sync_service_->GetActiveDataTypes().Has( | 627 |
| 617 syncer::HISTORY_DELETE_DIRECTIVES); | 628 void NTPSnippetsService::Disable() { |
| 629 state_ = State::DISABLED; | |
|
Marc Treib
2016/05/24 09:04:46
IMO only EnterState should change state_.
I think
dgn
2016/05/24 18:13:30
Done.
| |
| 630 ClearSnippets(); | |
| 631 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | |
| 632 NTPSnippetsServiceDisabled()); | |
| 633 RescheduleFetching(); | |
| 634 } | |
| 635 | |
| 636 void NTPSnippetsService::EnterState(State state) { | |
| 637 switch (state) { | |
| 638 case State::NOT_INITED: | |
| 639 // Initial state, should not be possible to get back there. | |
| 640 NOTREACHED(); | |
| 641 break; | |
| 642 case State::INITED: | |
| 643 DCHECK(state_ == State::NOT_INITED || state_ == State::DISABLED); | |
| 644 Enable(); | |
| 645 break; | |
| 646 case State::READY: | |
| 647 DCHECK(state_ == State::INITED); | |
| 648 // TODO | |
| 649 break; | |
| 650 case State::DISABLED: | |
| 651 DCHECK(state_ == State::NOT_INITED || state_ == State::READY); | |
| 652 Disable(); | |
| 653 break; | |
| 654 case State::SHUT_DOWN: | |
| 655 DCHECK(state_ == State::INITED || state_ == State::DISABLED || | |
| 656 state_ == State::READY); | |
| 657 break; | |
| 658 } | |
| 659 state_ = state; | |
| 618 } | 660 } |
| 619 | 661 |
| 620 } // namespace ntp_snippets | 662 } // namespace ntp_snippets |
| OLD | NEW |