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

Unified Diff: components/ntp_snippets/ntp_snippets_service.cc

Issue 2000233002: [NTP Snippets] Unschedule fetches when the service should be disabled (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@SnippetsDB
Patch Set: Please only look at ntp_snippets_service.*, the rest is because of the merge gone bad. Created 4 years, 7 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 side-by-side diff with in-line comments
Download patch
Index: components/ntp_snippets/ntp_snippets_service.cc
diff --git a/components/ntp_snippets/ntp_snippets_service.cc b/components/ntp_snippets/ntp_snippets_service.cc
index 4da4e50a3ddeb8950cafb65e3d8ba89a28e7d58b..0bf12b545ae3361094d5a3ecf99922df80d8569e 100644
--- a/components/ntp_snippets/ntp_snippets_service.cc
+++ b/components/ntp_snippets/ntp_snippets_service.cc
@@ -21,6 +21,7 @@
#include "base/values.h"
#include "components/image_fetcher/image_fetcher.h"
#include "components/ntp_snippets/ntp_snippets_constants.h"
+#include "components/ntp_snippets/ntp_snippets_database.h"
#include "components/ntp_snippets/pref_names.h"
#include "components/ntp_snippets/switches.h"
#include "components/prefs/pref_registry_simple.h"
@@ -157,16 +158,6 @@ std::set<std::string> GetSuggestionsHostsImpl(
return hosts;
}
-std::unique_ptr<base::ListValue> SnippetsToListValue(
- const NTPSnippet::PtrVector& snippets) {
- std::unique_ptr<base::ListValue> list(new base::ListValue);
- for (const auto& snippet : snippets) {
- std::unique_ptr<base::DictionaryValue> dict = snippet->ToDictionary();
- list->Append(std::move(dict));
- }
- return list;
-}
-
void InsertAllIDs(const NTPSnippet::PtrVector& snippets,
std::set<std::string>* ids) {
for (const std::unique_ptr<NTPSnippet>& snippet : snippets) {
@@ -176,6 +167,14 @@ void InsertAllIDs(const NTPSnippet::PtrVector& snippets,
}
}
+void EraseNullptrs(NTPSnippet::PtrVector* snippets) {
+ snippets->erase(
+ std::remove_if(
+ snippets->begin(), snippets->end(),
+ [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }),
+ snippets->end());
+}
+
void WrapImageFetchedCallback(
const NTPSnippetsService::ImageFetchedCallback& callback,
const GURL& snippet_id_url,
@@ -189,22 +188,22 @@ NTPSnippetsService::NTPSnippetsService(
PrefService* pref_service,
sync_driver::SyncService* sync_service,
SuggestionsService* suggestions_service,
- scoped_refptr<base::SequencedTaskRunner> file_task_runner,
const std::string& application_language_code,
NTPSnippetsScheduler* scheduler,
std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher,
- std::unique_ptr<ImageFetcher> image_fetcher)
+ std::unique_ptr<ImageFetcher> image_fetcher,
+ std::unique_ptr<NTPSnippetsDatabase> database)
: state_(State::NOT_INITED),
- enabled_(false),
+ explicitly_disabled_(false),
pref_service_(pref_service),
sync_service_(sync_service),
sync_service_observer_(this),
suggestions_service_(suggestions_service),
- file_task_runner_(file_task_runner),
application_language_code_(application_language_code),
scheduler_(scheduler),
snippets_fetcher_(std::move(snippets_fetcher)),
- image_fetcher_(std::move(image_fetcher)) {
+ image_fetcher_(std::move(image_fetcher)),
+ database_(std::move(database)) {
snippets_fetcher_->SetCallback(base::Bind(
&NTPSnippetsService::OnFetchFinished, base::Unretained(this)));
@@ -225,38 +224,18 @@ void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) {
}
void NTPSnippetsService::Init(bool enabled) {
- DCHECK(state_ == State::NOT_INITED);
- state_ = State::INITED;
-
- enabled_ = enabled;
- if (enabled_) {
- // |suggestions_service_| can be null in tests.
- if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) {
- suggestions_service_subscription_ = suggestions_service_->AddCallback(
- base::Bind(&NTPSnippetsService::OnSuggestionsChanged,
- base::Unretained(this)));
- }
-
- // Get any existing snippets immediately from prefs.
- LoadDiscardedSnippetsFromPrefs();
- LoadSnippetsFromPrefs();
-
- // If we don't have any snippets yet, start a fetch.
- if (snippets_.empty())
- FetchSnippets();
- }
-
- RescheduleFetching();
+ explicitly_disabled_ = !enabled;
+ EnterState(!explicitly_disabled_ ? State::INITED : State::DISABLED);
}
+// Inherited from KeyedService.
void NTPSnippetsService::Shutdown() {
- DCHECK(state_ == State::INITED);
- state_ = State::SHUT_DOWN;
+ EnterState(State::SHUT_DOWN);
FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
NTPSnippetsServiceShutdown());
+ expiry_timer_.Stop();
suggestions_service_subscription_.reset();
- enabled_ = false;
}
void NTPSnippetsService::FetchSnippets() {
@@ -265,6 +244,8 @@ void NTPSnippetsService::FetchSnippets() {
void NTPSnippetsService::FetchSnippetsFromHosts(
const std::set<std::string>& hosts) {
+ if (!ready())
+ return;
snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_,
kMaxSnippetCount);
}
@@ -274,7 +255,7 @@ void NTPSnippetsService::RescheduleFetching() {
if (!scheduler_)
return;
- if (enabled_) {
+ if (ready()) {
base::Time now = base::Time::Now();
scheduler_->Schedule(
GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now),
@@ -308,10 +289,17 @@ void NTPSnippetsService::FetchSnippetImage(
}
void NTPSnippetsService::ClearSnippets() {
- snippets_.clear();
+ // snippets-internals can call ClearSnippets while the service is ready.
Marc Treib 2016/05/25 14:47:32 Er, isn't it acceptable for anyone to call ClearSn
dgn 2016/06/03 19:02:24 True, removed that comment.
+ if (!ready() && state_ != State::DISABLED)
+ return;
- StoreSnippetsToPrefs();
+ if (snippets_.empty())
+ return;
+ database_->Delete(snippets_);
+ snippets_.clear();
+
+ DVLOG(1) << "[ClearSnippets] NTPSnippetsServiceLoaded";
FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
NTPSnippetsServiceLoaded());
}
@@ -321,12 +309,15 @@ std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const {
if (!suggestions_service_)
return std::set<std::string>();
- // TODO(treib) this should just call GetSnippetHostsFromPrefs
+ // TODO(treib): This should just call GetSnippetHostsFromPrefs.
return GetSuggestionsHostsImpl(
suggestions_service_->GetSuggestionsDataFromCache());
}
bool NTPSnippetsService::DiscardSnippet(const std::string& snippet_id) {
+ if (!ready())
+ return false;
+
auto it =
std::find_if(snippets_.begin(), snippets_.end(),
[&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) {
@@ -334,18 +325,26 @@ bool NTPSnippetsService::DiscardSnippet(const std::string& snippet_id) {
});
if (it == snippets_.end())
return false;
+
+ (*it)->set_discarded(true);
+
+ database_->Save(**it);
+
discarded_snippets_.push_back(std::move(*it));
snippets_.erase(it);
- StoreDiscardedSnippetsToPrefs();
- StoreSnippetsToPrefs();
+
FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
NTPSnippetsServiceLoaded());
return true;
}
void NTPSnippetsService::ClearDiscardedSnippets() {
+ if (!ready())
+ return;
+
+ database_->Delete(discarded_snippets_);
discarded_snippets_.clear();
- StoreDiscardedSnippetsToPrefs();
+
FetchSnippets();
}
@@ -357,6 +356,30 @@ void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) {
observers_.RemoveObserver(observer);
}
+DisabledReason NTPSnippetsService::GetDisabledReason() {
+ if (explicitly_disabled_)
+ return DisabledReason::EXPLICITLY_DISABLED;
+
+ if (!sync_service_ || !sync_service_->CanSyncStart()) {
+ DVLOG(1) << "[GetDisabledReason] Sync disabled";
+ return DisabledReason::HISTORY_SYNC_DISABLED;
+ }
+
+ if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone()) {
Marc Treib 2016/05/25 14:47:32 I think the ConfigurationDone check is redundant -
dgn 2016/06/03 19:02:24 When the sync options are modified, IsSyncActive()
+ DVLOG(1) << "[GetDisabledReason] config not done";
+ return DisabledReason::HISTORY_SYNC_STATE_UNKNOWN;
+ }
+
+ if (!sync_service_->GetActiveDataTypes().Has(
+ syncer::HISTORY_DELETE_DIRECTIVES)) {
+ DVLOG(1) << "[GetDisabledReason] History sync disabled";
+ return DisabledReason::HISTORY_SYNC_DISABLED;
+ }
+
+ DVLOG(1) << "[GetDisabledReason] Enabled!";
+ return DisabledReason::NONE;
+}
+
// static
int NTPSnippetsService::GetMaxSnippetCountForTesting() {
return kMaxSnippetCount;
@@ -367,21 +390,40 @@ int NTPSnippetsService::GetMaxSnippetCountForTesting() {
// sync_driver::SyncServiceObserver implementation.
void NTPSnippetsService::OnStateChanged() {
- if (IsSyncStateIncompatible()) {
- ClearSnippets();
- FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
- NTPSnippetsServiceDisabled());
+ DVLOG(1) << "[OnStateChanged]";
+ EnterState(GetStateForDependenciesStatus());
Marc Treib 2016/05/25 14:47:32 Can we get here before the DB is loaded? If so, pl
dgn 2016/06/03 19:02:24 Done.
+}
+
+void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) {
+ if (state_ == State::SHUT_DOWN)
return;
+
+ DCHECK(snippets_.empty());
+ DCHECK(discarded_snippets_.empty());
+ for (std::unique_ptr<NTPSnippet>& snippet : snippets) {
+ if (snippet->is_discarded())
+ discarded_snippets_.emplace_back(std::move(snippet));
+ else
+ snippets_.emplace_back(std::move(snippet));
+ }
+ std::sort(snippets_.begin(), snippets_.end(),
+ [](const std::unique_ptr<NTPSnippet>& lhs,
+ const std::unique_ptr<NTPSnippet>& rhs) {
+ return lhs->score() > rhs->score();
+ });
+
+ if (!snippets_.empty()) {
+ DVLOG(1) << "[OnDatabaseLoaded] got snippets";
+ LoadingSnippetsFinished();
}
- // TODO(dgn): When the data sources change, we may want to not fetch here,
- // as we will get notified of changes from the snippet sources as well, and
- // start multiple fetches.
- FetchSnippets();
+ EnterState(GetStateForDependenciesStatus());
}
void NTPSnippetsService::OnSuggestionsChanged(
const SuggestionsProfile& suggestions) {
+ DCHECK(ready() || state_ == State::DISABLED);
+
std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions);
if (hosts == GetSnippetHostsFromPrefs())
return;
@@ -389,16 +431,19 @@ void NTPSnippetsService::OnSuggestionsChanged(
// Remove existing snippets that aren't in the suggestions anymore.
// TODO(treib,maybelle): If there is another source with an allowed host,
// then we should fall back to that.
- snippets_.erase(
- std::remove_if(snippets_.begin(), snippets_.end(),
- [&hosts](const std::unique_ptr<NTPSnippet>& snippet) {
- return !hosts.count(snippet->best_source().url.host());
- }),
- snippets_.end());
-
- StoreSnippetsToPrefs();
+ // First, move them over into |to_delete|.
+ NTPSnippet::PtrVector to_delete;
+ for (std::unique_ptr<NTPSnippet>& snippet : snippets_) {
+ if (!hosts.count(snippet->best_source().url.host()))
+ to_delete.emplace_back(std::move(snippet));
+ }
+ EraseNullptrs(&snippets_);
+ // Then delete the removed snippets from the database.
+ database_->Delete(to_delete);
+
StoreSnippetHostsToPrefs(hosts);
+ DVLOG(1) << "[OnSuggestionsChanged] NTPSnippetsServiceLoaded";
FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
NTPSnippetsServiceLoaded());
@@ -407,18 +452,26 @@ void NTPSnippetsService::OnSuggestionsChanged(
void NTPSnippetsService::OnFetchFinished(
NTPSnippetsFetcher::OptionalSnippets snippets) {
+ if (!ready())
+ return;
+
if (snippets) {
// Sparse histogram used because the number of snippets is small (bound by
// kMaxSnippetCount).
DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount));
+ DVLOG(1) << "[OnFetchFinished] histogram -> NumArticlesFetched";
UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched",
snippets->size());
MergeSnippets(std::move(*snippets));
}
+
+ DVLOG(1) << "[OnFetchFinished]";
LoadingSnippetsFinished();
}
void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) {
+ DCHECK(ready());
+
// Remove new snippets that we already have, or that have been discarded.
std::set<std::string> old_snippet_ids;
InsertAllIDs(discarded_snippets_, &old_snippet_ids);
@@ -462,44 +515,25 @@ void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) {
}),
new_snippets.end());
int num_snippets_discarded = num_new_snippets - new_snippets.size();
+ DVLOG(1) << "[MergeSnippets] histogram -> IncompleteSnippetsAfterFetch";
UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch",
num_snippets_discarded > 0);
if (num_snippets_discarded > 0) {
+ DVLOG(1) << "[MergeSnippets] histogram -> NumIncompleteSnippets";
UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets",
num_snippets_discarded);
}
}
+
+ // Save the new snippets to the DB.
+ database_->Save(new_snippets);
+
// Insert the new snippets at the front.
snippets_.insert(snippets_.begin(),
std::make_move_iterator(new_snippets.begin()),
std::make_move_iterator(new_snippets.end()));
}
-void NTPSnippetsService::LoadSnippetsFromPrefs() {
- NTPSnippet::PtrVector prefs_snippets;
- bool success = NTPSnippet::AddFromListValue(
- *pref_service_->GetList(prefs::kSnippets), &prefs_snippets);
- DCHECK(success) << "Failed to parse snippets from prefs";
- MergeSnippets(std::move(prefs_snippets));
- LoadingSnippetsFinished();
-}
-
-void NTPSnippetsService::StoreSnippetsToPrefs() {
- pref_service_->Set(prefs::kSnippets, *SnippetsToListValue(snippets_));
-}
-
-void NTPSnippetsService::LoadDiscardedSnippetsFromPrefs() {
- discarded_snippets_.clear();
- bool success = NTPSnippet::AddFromListValue(
- *pref_service_->GetList(prefs::kDiscardedSnippets), &discarded_snippets_);
- DCHECK(success) << "Failed to parse discarded snippets from prefs";
-}
-
-void NTPSnippetsService::StoreDiscardedSnippetsToPrefs() {
- pref_service_->Set(prefs::kDiscardedSnippets,
- *SnippetsToListValue(discarded_snippets_));
-}
-
std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const {
std::set<std::string> hosts;
const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts);
@@ -521,34 +555,46 @@ void NTPSnippetsService::StoreSnippetHostsToPrefs(
}
void NTPSnippetsService::LoadingSnippetsFinished() {
+ DCHECK(ready() || state_ == State::INITED);
+
// Remove expired snippets.
base::Time expiry = base::Time::Now();
- snippets_.erase(
- std::remove_if(snippets_.begin(), snippets_.end(),
- [&expiry](const std::unique_ptr<NTPSnippet>& snippet) {
- return snippet->expiry_date() <= expiry;
- }),
- snippets_.end());
-
- // If there are more snippets now than we want to show, drop the extra ones
- // from the end of the list.
- if (snippets_.size() > kMaxSnippetCount)
+ // Move expired snippets over into |to_delete|.
+ NTPSnippet::PtrVector to_delete;
+ for (std::unique_ptr<NTPSnippet>& snippet : snippets_) {
+ if (snippet->expiry_date() <= expiry)
+ to_delete.emplace_back(std::move(snippet));
+ }
+ EraseNullptrs(&snippets_);
+
+ // If there are still more snippets than we want to show, move the extra ones
+ // over into |to_delete| as well.
+ if (snippets_.size() > kMaxSnippetCount) {
+ to_delete.insert(
+ to_delete.end(),
+ std::make_move_iterator(snippets_.begin() + kMaxSnippetCount),
+ std::make_move_iterator(snippets_.end()));
snippets_.resize(kMaxSnippetCount);
+ }
- StoreSnippetsToPrefs();
+ // Move expired discarded snippets over into |to_delete| as well.
+ for (std::unique_ptr<NTPSnippet>& snippet : discarded_snippets_) {
+ if (snippet->expiry_date() <= expiry)
+ to_delete.emplace_back(std::move(snippet));
+ }
+ EraseNullptrs(&discarded_snippets_);
- discarded_snippets_.erase(
- std::remove_if(discarded_snippets_.begin(), discarded_snippets_.end(),
- [&expiry](const std::unique_ptr<NTPSnippet>& snippet) {
- return snippet->expiry_date() <= expiry;
- }),
- discarded_snippets_.end());
- StoreDiscardedSnippetsToPrefs();
+ // Finally, actually delete the removed snippets from the DB.
+ database_->Delete(to_delete);
+ DVLOG(1) << "[LoadingSnippetsFinished] NTPSnippetsServiceLoaded -> Histogram "
+ "NumArticles";
UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles",
snippets_.size());
if (snippets_.empty() && !discarded_snippets_.empty()) {
+ DVLOG(1) << "[LoadingSnippetsFinished] NTPSnippetsServiceLoaded -> "
+ "Histogram NumArticlesZeroDueToDiscarded";
UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded",
discarded_snippets_.size());
}
@@ -575,13 +621,99 @@ void NTPSnippetsService::LoadingSnippetsFinished() {
base::Unretained(this)));
}
-bool NTPSnippetsService::IsSyncStateIncompatible() {
- if (!sync_service_ || !sync_service_->CanSyncStart())
- return true;
- if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone())
- return false; // Sync service is not initialized, yet not disabled.
- return !sync_service_->GetActiveDataTypes().Has(
- syncer::HISTORY_DELETE_DIRECTIVES);
+void NTPSnippetsService::LoadFromDB() {
+ database_->Load(base::Bind(&NTPSnippetsService::OnDatabaseLoaded,
+ base::Unretained(this)));
+}
+
+void NTPSnippetsService::Enable() {
+ // We might already have snippets from loading the DB and want to avoid a
+ // double load.
+ if (snippets_.empty())
+ FetchSnippets();
+
+ // If host restrictions are enabled, register for host list updates.
+ // |suggestions_service_| can be null in tests.
+ if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) {
+ suggestions_service_subscription_ =
+ suggestions_service_->AddCallback(base::Bind(
+ &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this)));
+ }
+ RescheduleFetching();
+}
+
+void NTPSnippetsService::Disable() {
+ ClearSnippets();
+
+ suggestions_service_subscription_.reset();
+ RescheduleFetching();
+ FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_,
+ NTPSnippetsServiceDisabled());
+}
+
+State NTPSnippetsService::GetStateForDependenciesStatus() {
+ DisabledReason dr = GetDisabledReason();
+
+ if (dr == DisabledReason::NONE)
+ return State::READY;
+
+ // HistorySync is not initialized yet, so we don't know what the actual state
+ // is. However, because we retreived the previous snippets from the database,
+ // if we got something, we know that the service was previously enabled, so
+ // we just restore that state. If things changed, |OnStateChanged| will call
+ // this function again to fix the state.
+ if (dr == DisabledReason::HISTORY_SYNC_STATE_UNKNOWN) {
+ DVLOG(1) << "Sync configuration not done, continuing based on the current "
+ "state.";
+ return snippets_.empty() ? State::DISABLED : State::READY;
+ }
+
+ return State::DISABLED;
+}
+
+void NTPSnippetsService::EnterState(State state) {
+ switch (state) {
+ case State::NOT_INITED:
+ // Initial state, it should not be possible to get back there.
+ NOTREACHED();
+ return;
+
+ case State::INITED:
+ DCHECK(state_ == State::NOT_INITED);
+ state_ = State::INITED;
+ LoadFromDB();
+ DVLOG(1) << "Entering state: INITED";
+ return;
+
+ case State::READY:
+ DCHECK(state_ == State::INITED || state_ == State::READY ||
+ state_ == State::DISABLED);
+ if (state_ == State::READY)
+ return;
+
+ DVLOG(1) << "Entering state: READY";
+ state_ = State::READY;
+ Enable();
+ return;
+
+ case State::DISABLED:
+ DCHECK(state_ == State::NOT_INITED || state_ == State::READY ||
+ state_ == State::DISABLED || state_ == State::INITED);
+ if (state_ == State::DISABLED)
+ return;
+
+ DVLOG(1) << "Entering state: DISABLED";
+ state_ = State::DISABLED;
+ Disable();
+ return;
+
+ case State::SHUT_DOWN:
+ DCHECK(state_ == State::INITED || state_ == State::DISABLED ||
+ state_ == State::READY);
+ DVLOG(1) << "Entering state: SHUT_DOWN";
+ state_ = State::SHUT_DOWN;
+ return;
+ }
}
} // namespace ntp_snippets

Powered by Google App Engine
This is Rietveld 408576698