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 |
| 11 #include "base/command_line.h" | 11 #include "base/command_line.h" |
| 12 #include "base/files/file_path.h" | 12 #include "base/files/file_path.h" |
| 13 #include "base/files/file_util.h" | 13 #include "base/files/file_util.h" |
| 14 #include "base/location.h" | 14 #include "base/location.h" |
| 15 #include "base/metrics/histogram_macros.h" | 15 #include "base/metrics/histogram_macros.h" |
| 16 #include "base/metrics/sparse_histogram.h" | 16 #include "base/metrics/sparse_histogram.h" |
| 17 #include "base/path_service.h" | 17 #include "base/path_service.h" |
| 18 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/task_runner_util.h" | 19 #include "base/task_runner_util.h" |
| 20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
| 21 #include "base/values.h" | 21 #include "base/values.h" |
| 22 #include "components/image_fetcher/image_fetcher.h" | 22 #include "components/image_fetcher/image_fetcher.h" |
| 23 #include "components/ntp_snippets/ntp_snippets_constants.h" | 23 #include "components/ntp_snippets/ntp_snippets_constants.h" |
| 24 #include "components/ntp_snippets/ntp_snippets_database.h" | |
| 24 #include "components/ntp_snippets/pref_names.h" | 25 #include "components/ntp_snippets/pref_names.h" |
| 25 #include "components/ntp_snippets/switches.h" | 26 #include "components/ntp_snippets/switches.h" |
| 26 #include "components/prefs/pref_registry_simple.h" | 27 #include "components/prefs/pref_registry_simple.h" |
| 27 #include "components/prefs/pref_service.h" | 28 #include "components/prefs/pref_service.h" |
| 28 #include "components/suggestions/proto/suggestions.pb.h" | 29 #include "components/suggestions/proto/suggestions.pb.h" |
| 29 #include "components/sync_driver/sync_service.h" | 30 #include "components/sync_driver/sync_service.h" |
| 30 #include "components/variations/variations_associated_data.h" | 31 #include "components/variations/variations_associated_data.h" |
| 31 #include "ui/gfx/image/image.h" | 32 #include "ui/gfx/image/image.h" |
| 32 | 33 |
| 33 using image_fetcher::ImageFetcher; | 34 using image_fetcher::ImageFetcher; |
| (...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 150 std::set<std::string> hosts; | 151 std::set<std::string> hosts; |
| 151 for (int i = 0; i < suggestions.suggestions_size(); ++i) { | 152 for (int i = 0; i < suggestions.suggestions_size(); ++i) { |
| 152 const ChromeSuggestion& suggestion = suggestions.suggestions(i); | 153 const ChromeSuggestion& suggestion = suggestions.suggestions(i); |
| 153 GURL url(suggestion.url()); | 154 GURL url(suggestion.url()); |
| 154 if (url.is_valid()) | 155 if (url.is_valid()) |
| 155 hosts.insert(url.host()); | 156 hosts.insert(url.host()); |
| 156 } | 157 } |
| 157 return hosts; | 158 return hosts; |
| 158 } | 159 } |
| 159 | 160 |
| 160 std::unique_ptr<base::ListValue> SnippetsToListValue( | |
| 161 const NTPSnippet::PtrVector& snippets) { | |
| 162 std::unique_ptr<base::ListValue> list(new base::ListValue); | |
| 163 for (const auto& snippet : snippets) { | |
| 164 std::unique_ptr<base::DictionaryValue> dict = snippet->ToDictionary(); | |
| 165 list->Append(std::move(dict)); | |
| 166 } | |
| 167 return list; | |
| 168 } | |
| 169 | |
| 170 void InsertAllIDs(const NTPSnippet::PtrVector& snippets, | 161 void InsertAllIDs(const NTPSnippet::PtrVector& snippets, |
| 171 std::set<std::string>* ids) { | 162 std::set<std::string>* ids) { |
| 172 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { | 163 for (const std::unique_ptr<NTPSnippet>& snippet : snippets) { |
| 173 ids->insert(snippet->id()); | 164 ids->insert(snippet->id()); |
| 174 for (const SnippetSource& source : snippet->sources()) | 165 for (const SnippetSource& source : snippet->sources()) |
| 175 ids->insert(source.url.spec()); | 166 ids->insert(source.url.spec()); |
| 176 } | 167 } |
| 177 } | 168 } |
| 178 | 169 |
| 170 void EraseNullptrs(NTPSnippet::PtrVector* snippets) { | |
| 171 snippets->erase( | |
| 172 std::remove_if( | |
| 173 snippets->begin(), snippets->end(), | |
| 174 [](const std::unique_ptr<NTPSnippet>& snippet) { return !snippet; }), | |
| 175 snippets->end()); | |
| 176 } | |
| 177 | |
| 179 void WrapImageFetchedCallback( | 178 void WrapImageFetchedCallback( |
| 180 const NTPSnippetsService::ImageFetchedCallback& callback, | 179 const NTPSnippetsService::ImageFetchedCallback& callback, |
| 181 const GURL& snippet_id_url, | 180 const GURL& snippet_id_url, |
| 182 const gfx::Image& image) { | 181 const gfx::Image& image) { |
| 183 callback.Run(snippet_id_url.spec(), image); | 182 callback.Run(snippet_id_url.spec(), image); |
| 184 } | 183 } |
| 185 | 184 |
| 186 } // namespace | 185 } // namespace |
| 187 | 186 |
| 188 NTPSnippetsService::NTPSnippetsService( | 187 NTPSnippetsService::NTPSnippetsService( |
| 189 PrefService* pref_service, | 188 PrefService* pref_service, |
| 190 sync_driver::SyncService* sync_service, | 189 sync_driver::SyncService* sync_service, |
| 191 SuggestionsService* suggestions_service, | 190 SuggestionsService* suggestions_service, |
| 192 scoped_refptr<base::SequencedTaskRunner> file_task_runner, | |
| 193 const std::string& application_language_code, | 191 const std::string& application_language_code, |
| 194 NTPSnippetsScheduler* scheduler, | 192 NTPSnippetsScheduler* scheduler, |
| 195 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, | 193 std::unique_ptr<NTPSnippetsFetcher> snippets_fetcher, |
| 196 std::unique_ptr<ImageFetcher> image_fetcher) | 194 std::unique_ptr<ImageFetcher> image_fetcher, |
| 195 std::unique_ptr<NTPSnippetsDatabase> database) | |
| 197 : state_(State::NOT_INITED), | 196 : state_(State::NOT_INITED), |
| 198 enabled_(false), | 197 explicitly_disabled_(false), |
| 199 pref_service_(pref_service), | 198 pref_service_(pref_service), |
| 200 sync_service_(sync_service), | 199 sync_service_(sync_service), |
| 201 sync_service_observer_(this), | 200 sync_service_observer_(this), |
| 202 suggestions_service_(suggestions_service), | 201 suggestions_service_(suggestions_service), |
| 203 file_task_runner_(file_task_runner), | |
| 204 application_language_code_(application_language_code), | 202 application_language_code_(application_language_code), |
| 205 scheduler_(scheduler), | 203 scheduler_(scheduler), |
| 206 snippets_fetcher_(std::move(snippets_fetcher)), | 204 snippets_fetcher_(std::move(snippets_fetcher)), |
| 207 image_fetcher_(std::move(image_fetcher)) { | 205 image_fetcher_(std::move(image_fetcher)), |
| 206 database_(std::move(database)) { | |
| 208 snippets_fetcher_->SetCallback(base::Bind( | 207 snippets_fetcher_->SetCallback(base::Bind( |
| 209 &NTPSnippetsService::OnFetchFinished, base::Unretained(this))); | 208 &NTPSnippetsService::OnFetchFinished, base::Unretained(this))); |
| 210 | 209 |
| 211 // |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. |
| 212 if (sync_service_) | 211 if (sync_service_) |
| 213 sync_service_observer_.Add(sync_service_); | 212 sync_service_observer_.Add(sync_service_); |
| 214 } | 213 } |
| 215 | 214 |
| 216 NTPSnippetsService::~NTPSnippetsService() { | 215 NTPSnippetsService::~NTPSnippetsService() { |
| 217 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); | 216 DCHECK(state_ == State::NOT_INITED || state_ == State::SHUT_DOWN); |
| 218 } | 217 } |
| 219 | 218 |
| 220 // static | 219 // static |
| 221 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { | 220 void NTPSnippetsService::RegisterProfilePrefs(PrefRegistrySimple* registry) { |
| 222 registry->RegisterListPref(prefs::kSnippets); | 221 registry->RegisterListPref(prefs::kSnippets); |
| 223 registry->RegisterListPref(prefs::kDiscardedSnippets); | 222 registry->RegisterListPref(prefs::kDiscardedSnippets); |
| 224 registry->RegisterListPref(prefs::kSnippetHosts); | 223 registry->RegisterListPref(prefs::kSnippetHosts); |
| 225 } | 224 } |
| 226 | 225 |
| 227 void NTPSnippetsService::Init(bool enabled) { | 226 void NTPSnippetsService::Init(bool enabled) { |
| 228 DCHECK(state_ == State::NOT_INITED); | 227 explicitly_disabled_ = !enabled; |
| 229 state_ = State::INITED; | 228 EnterState(!explicitly_disabled_ ? State::INITED : State::DISABLED); |
| 230 | |
| 231 enabled_ = enabled; | |
| 232 if (enabled_) { | |
| 233 // |suggestions_service_| can be null in tests. | |
| 234 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { | |
| 235 suggestions_service_subscription_ = suggestions_service_->AddCallback( | |
| 236 base::Bind(&NTPSnippetsService::OnSuggestionsChanged, | |
| 237 base::Unretained(this))); | |
| 238 } | |
| 239 | |
| 240 // Get any existing snippets immediately from prefs. | |
| 241 LoadDiscardedSnippetsFromPrefs(); | |
| 242 LoadSnippetsFromPrefs(); | |
| 243 | |
| 244 // If we don't have any snippets yet, start a fetch. | |
| 245 if (snippets_.empty()) | |
| 246 FetchSnippets(); | |
| 247 } | |
| 248 | |
| 249 RescheduleFetching(); | |
| 250 } | 229 } |
| 251 | 230 |
| 231 // Inherited from KeyedService. | |
| 252 void NTPSnippetsService::Shutdown() { | 232 void NTPSnippetsService::Shutdown() { |
| 253 DCHECK(state_ == State::INITED); | 233 EnterState(State::SHUT_DOWN); |
| 254 state_ = State::SHUT_DOWN; | |
| 255 | 234 |
| 256 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 235 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 257 NTPSnippetsServiceShutdown()); | 236 NTPSnippetsServiceShutdown()); |
| 237 expiry_timer_.Stop(); | |
| 258 suggestions_service_subscription_.reset(); | 238 suggestions_service_subscription_.reset(); |
| 259 enabled_ = false; | |
| 260 } | 239 } |
| 261 | 240 |
| 262 void NTPSnippetsService::FetchSnippets() { | 241 void NTPSnippetsService::FetchSnippets() { |
| 263 FetchSnippetsFromHosts(GetSuggestionsHosts()); | 242 FetchSnippetsFromHosts(GetSuggestionsHosts()); |
| 264 } | 243 } |
| 265 | 244 |
| 266 void NTPSnippetsService::FetchSnippetsFromHosts( | 245 void NTPSnippetsService::FetchSnippetsFromHosts( |
| 267 const std::set<std::string>& hosts) { | 246 const std::set<std::string>& hosts) { |
| 247 if (!ready()) | |
| 248 return; | |
| 268 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, | 249 snippets_fetcher_->FetchSnippetsFromHosts(hosts, application_language_code_, |
| 269 kMaxSnippetCount); | 250 kMaxSnippetCount); |
| 270 } | 251 } |
| 271 | 252 |
| 272 void NTPSnippetsService::RescheduleFetching() { | 253 void NTPSnippetsService::RescheduleFetching() { |
| 273 // The scheduler only exists on Android so far, it's null on other platforms. | 254 // The scheduler only exists on Android so far, it's null on other platforms. |
| 274 if (!scheduler_) | 255 if (!scheduler_) |
| 275 return; | 256 return; |
| 276 | 257 |
| 277 if (enabled_) { | 258 if (ready()) { |
| 278 base::Time now = base::Time::Now(); | 259 base::Time now = base::Time::Now(); |
| 279 scheduler_->Schedule( | 260 scheduler_->Schedule( |
| 280 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), | 261 GetFetchingIntervalWifiCharging(), GetFetchingIntervalWifi(now), |
| 281 GetFetchingIntervalFallback(), GetRescheduleTime(now)); | 262 GetFetchingIntervalFallback(), GetRescheduleTime(now)); |
| 282 } else { | 263 } else { |
| 283 scheduler_->Unschedule(); | 264 scheduler_->Unschedule(); |
| 284 } | 265 } |
| 285 } | 266 } |
| 286 | 267 |
| 287 void NTPSnippetsService::FetchSnippetImage( | 268 void NTPSnippetsService::FetchSnippetImage( |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 301 const NTPSnippet& snippet = *it->get(); | 282 const NTPSnippet& snippet = *it->get(); |
| 302 // TODO(treib): Make ImageFetcher take a string instead of a GURL as an | 283 // TODO(treib): Make ImageFetcher take a string instead of a GURL as an |
| 303 // identifier. | 284 // identifier. |
| 304 image_fetcher_->StartOrQueueNetworkRequest( | 285 image_fetcher_->StartOrQueueNetworkRequest( |
| 305 GURL(snippet.id()), snippet.salient_image_url(), | 286 GURL(snippet.id()), snippet.salient_image_url(), |
| 306 base::Bind(WrapImageFetchedCallback, callback)); | 287 base::Bind(WrapImageFetchedCallback, callback)); |
| 307 // TODO(treib): Cache/persist the snippet image. | 288 // TODO(treib): Cache/persist the snippet image. |
| 308 } | 289 } |
| 309 | 290 |
| 310 void NTPSnippetsService::ClearSnippets() { | 291 void NTPSnippetsService::ClearSnippets() { |
| 292 // 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.
| |
| 293 if (!ready() && state_ != State::DISABLED) | |
| 294 return; | |
| 295 | |
| 296 if (snippets_.empty()) | |
| 297 return; | |
| 298 | |
| 299 database_->Delete(snippets_); | |
| 311 snippets_.clear(); | 300 snippets_.clear(); |
| 312 | 301 |
| 313 StoreSnippetsToPrefs(); | 302 DVLOG(1) << "[ClearSnippets] NTPSnippetsServiceLoaded"; |
| 314 | |
| 315 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 303 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 316 NTPSnippetsServiceLoaded()); | 304 NTPSnippetsServiceLoaded()); |
| 317 } | 305 } |
| 318 | 306 |
| 319 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { | 307 std::set<std::string> NTPSnippetsService::GetSuggestionsHosts() const { |
| 320 // |suggestions_service_| can be null in tests. | 308 // |suggestions_service_| can be null in tests. |
| 321 if (!suggestions_service_) | 309 if (!suggestions_service_) |
| 322 return std::set<std::string>(); | 310 return std::set<std::string>(); |
| 323 | 311 |
| 324 // TODO(treib) this should just call GetSnippetHostsFromPrefs | 312 // TODO(treib): This should just call GetSnippetHostsFromPrefs. |
| 325 return GetSuggestionsHostsImpl( | 313 return GetSuggestionsHostsImpl( |
| 326 suggestions_service_->GetSuggestionsDataFromCache()); | 314 suggestions_service_->GetSuggestionsDataFromCache()); |
| 327 } | 315 } |
| 328 | 316 |
| 329 bool NTPSnippetsService::DiscardSnippet(const std::string& snippet_id) { | 317 bool NTPSnippetsService::DiscardSnippet(const std::string& snippet_id) { |
| 318 if (!ready()) | |
| 319 return false; | |
| 320 | |
| 330 auto it = | 321 auto it = |
| 331 std::find_if(snippets_.begin(), snippets_.end(), | 322 std::find_if(snippets_.begin(), snippets_.end(), |
| 332 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { | 323 [&snippet_id](const std::unique_ptr<NTPSnippet>& snippet) { |
| 333 return snippet->id() == snippet_id; | 324 return snippet->id() == snippet_id; |
| 334 }); | 325 }); |
| 335 if (it == snippets_.end()) | 326 if (it == snippets_.end()) |
| 336 return false; | 327 return false; |
| 328 | |
| 329 (*it)->set_discarded(true); | |
| 330 | |
| 331 database_->Save(**it); | |
| 332 | |
| 337 discarded_snippets_.push_back(std::move(*it)); | 333 discarded_snippets_.push_back(std::move(*it)); |
| 338 snippets_.erase(it); | 334 snippets_.erase(it); |
| 339 StoreDiscardedSnippetsToPrefs(); | 335 |
| 340 StoreSnippetsToPrefs(); | |
| 341 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 336 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 342 NTPSnippetsServiceLoaded()); | 337 NTPSnippetsServiceLoaded()); |
| 343 return true; | 338 return true; |
| 344 } | 339 } |
| 345 | 340 |
| 346 void NTPSnippetsService::ClearDiscardedSnippets() { | 341 void NTPSnippetsService::ClearDiscardedSnippets() { |
| 342 if (!ready()) | |
| 343 return; | |
| 344 | |
| 345 database_->Delete(discarded_snippets_); | |
| 347 discarded_snippets_.clear(); | 346 discarded_snippets_.clear(); |
| 348 StoreDiscardedSnippetsToPrefs(); | 347 |
| 349 FetchSnippets(); | 348 FetchSnippets(); |
| 350 } | 349 } |
| 351 | 350 |
| 352 void NTPSnippetsService::AddObserver(NTPSnippetsServiceObserver* observer) { | 351 void NTPSnippetsService::AddObserver(NTPSnippetsServiceObserver* observer) { |
| 353 observers_.AddObserver(observer); | 352 observers_.AddObserver(observer); |
| 354 } | 353 } |
| 355 | 354 |
| 356 void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) { | 355 void NTPSnippetsService::RemoveObserver(NTPSnippetsServiceObserver* observer) { |
| 357 observers_.RemoveObserver(observer); | 356 observers_.RemoveObserver(observer); |
| 358 } | 357 } |
| 359 | 358 |
| 359 DisabledReason NTPSnippetsService::GetDisabledReason() { | |
| 360 if (explicitly_disabled_) | |
| 361 return DisabledReason::EXPLICITLY_DISABLED; | |
| 362 | |
| 363 if (!sync_service_ || !sync_service_->CanSyncStart()) { | |
| 364 DVLOG(1) << "[GetDisabledReason] Sync disabled"; | |
| 365 return DisabledReason::HISTORY_SYNC_DISABLED; | |
| 366 } | |
| 367 | |
| 368 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()
| |
| 369 DVLOG(1) << "[GetDisabledReason] config not done"; | |
| 370 return DisabledReason::HISTORY_SYNC_STATE_UNKNOWN; | |
| 371 } | |
| 372 | |
| 373 if (!sync_service_->GetActiveDataTypes().Has( | |
| 374 syncer::HISTORY_DELETE_DIRECTIVES)) { | |
| 375 DVLOG(1) << "[GetDisabledReason] History sync disabled"; | |
| 376 return DisabledReason::HISTORY_SYNC_DISABLED; | |
| 377 } | |
| 378 | |
| 379 DVLOG(1) << "[GetDisabledReason] Enabled!"; | |
| 380 return DisabledReason::NONE; | |
| 381 } | |
| 382 | |
| 360 // static | 383 // static |
| 361 int NTPSnippetsService::GetMaxSnippetCountForTesting() { | 384 int NTPSnippetsService::GetMaxSnippetCountForTesting() { |
| 362 return kMaxSnippetCount; | 385 return kMaxSnippetCount; |
| 363 } | 386 } |
| 364 | 387 |
| 365 //////////////////////////////////////////////////////////////////////////////// | 388 //////////////////////////////////////////////////////////////////////////////// |
| 366 // Private methods | 389 // Private methods |
| 367 | 390 |
| 368 // sync_driver::SyncServiceObserver implementation. | 391 // sync_driver::SyncServiceObserver implementation. |
| 369 void NTPSnippetsService::OnStateChanged() { | 392 void NTPSnippetsService::OnStateChanged() { |
| 370 if (IsSyncStateIncompatible()) { | 393 DVLOG(1) << "[OnStateChanged]"; |
| 371 ClearSnippets(); | 394 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.
| |
| 372 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 395 } |
| 373 NTPSnippetsServiceDisabled()); | 396 |
| 397 void NTPSnippetsService::OnDatabaseLoaded(NTPSnippet::PtrVector snippets) { | |
| 398 if (state_ == State::SHUT_DOWN) | |
| 374 return; | 399 return; |
| 400 | |
| 401 DCHECK(snippets_.empty()); | |
| 402 DCHECK(discarded_snippets_.empty()); | |
| 403 for (std::unique_ptr<NTPSnippet>& snippet : snippets) { | |
| 404 if (snippet->is_discarded()) | |
| 405 discarded_snippets_.emplace_back(std::move(snippet)); | |
| 406 else | |
| 407 snippets_.emplace_back(std::move(snippet)); | |
| 408 } | |
| 409 std::sort(snippets_.begin(), snippets_.end(), | |
| 410 [](const std::unique_ptr<NTPSnippet>& lhs, | |
| 411 const std::unique_ptr<NTPSnippet>& rhs) { | |
| 412 return lhs->score() > rhs->score(); | |
| 413 }); | |
| 414 | |
| 415 if (!snippets_.empty()) { | |
| 416 DVLOG(1) << "[OnDatabaseLoaded] got snippets"; | |
| 417 LoadingSnippetsFinished(); | |
| 375 } | 418 } |
| 376 | 419 |
| 377 // TODO(dgn): When the data sources change, we may want to not fetch here, | 420 EnterState(GetStateForDependenciesStatus()); |
| 378 // as we will get notified of changes from the snippet sources as well, and | |
| 379 // start multiple fetches. | |
| 380 FetchSnippets(); | |
| 381 } | 421 } |
| 382 | 422 |
| 383 void NTPSnippetsService::OnSuggestionsChanged( | 423 void NTPSnippetsService::OnSuggestionsChanged( |
| 384 const SuggestionsProfile& suggestions) { | 424 const SuggestionsProfile& suggestions) { |
| 425 DCHECK(ready() || state_ == State::DISABLED); | |
| 426 | |
| 385 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); | 427 std::set<std::string> hosts = GetSuggestionsHostsImpl(suggestions); |
| 386 if (hosts == GetSnippetHostsFromPrefs()) | 428 if (hosts == GetSnippetHostsFromPrefs()) |
| 387 return; | 429 return; |
| 388 | 430 |
| 389 // Remove existing snippets that aren't in the suggestions anymore. | 431 // Remove existing snippets that aren't in the suggestions anymore. |
| 390 // TODO(treib,maybelle): If there is another source with an allowed host, | 432 // TODO(treib,maybelle): If there is another source with an allowed host, |
| 391 // then we should fall back to that. | 433 // then we should fall back to that. |
| 392 snippets_.erase( | 434 // First, move them over into |to_delete|. |
| 393 std::remove_if(snippets_.begin(), snippets_.end(), | 435 NTPSnippet::PtrVector to_delete; |
| 394 [&hosts](const std::unique_ptr<NTPSnippet>& snippet) { | 436 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { |
| 395 return !hosts.count(snippet->best_source().url.host()); | 437 if (!hosts.count(snippet->best_source().url.host())) |
| 396 }), | 438 to_delete.emplace_back(std::move(snippet)); |
| 397 snippets_.end()); | 439 } |
| 440 EraseNullptrs(&snippets_); | |
| 441 // Then delete the removed snippets from the database. | |
| 442 database_->Delete(to_delete); | |
| 398 | 443 |
| 399 StoreSnippetsToPrefs(); | |
| 400 StoreSnippetHostsToPrefs(hosts); | 444 StoreSnippetHostsToPrefs(hosts); |
| 401 | 445 |
| 446 DVLOG(1) << "[OnSuggestionsChanged] NTPSnippetsServiceLoaded"; | |
| 402 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 447 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 403 NTPSnippetsServiceLoaded()); | 448 NTPSnippetsServiceLoaded()); |
| 404 | 449 |
| 405 FetchSnippetsFromHosts(hosts); | 450 FetchSnippetsFromHosts(hosts); |
| 406 } | 451 } |
| 407 | 452 |
| 408 void NTPSnippetsService::OnFetchFinished( | 453 void NTPSnippetsService::OnFetchFinished( |
| 409 NTPSnippetsFetcher::OptionalSnippets snippets) { | 454 NTPSnippetsFetcher::OptionalSnippets snippets) { |
| 455 if (!ready()) | |
| 456 return; | |
| 457 | |
| 410 if (snippets) { | 458 if (snippets) { |
| 411 // Sparse histogram used because the number of snippets is small (bound by | 459 // Sparse histogram used because the number of snippets is small (bound by |
| 412 // kMaxSnippetCount). | 460 // kMaxSnippetCount). |
| 413 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); | 461 DCHECK_LE(snippets->size(), static_cast<size_t>(kMaxSnippetCount)); |
| 462 DVLOG(1) << "[OnFetchFinished] histogram -> NumArticlesFetched"; | |
| 414 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", | 463 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticlesFetched", |
| 415 snippets->size()); | 464 snippets->size()); |
| 416 MergeSnippets(std::move(*snippets)); | 465 MergeSnippets(std::move(*snippets)); |
| 417 } | 466 } |
| 467 | |
| 468 DVLOG(1) << "[OnFetchFinished]"; | |
| 418 LoadingSnippetsFinished(); | 469 LoadingSnippetsFinished(); |
| 419 } | 470 } |
| 420 | 471 |
| 421 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { | 472 void NTPSnippetsService::MergeSnippets(NTPSnippet::PtrVector new_snippets) { |
| 473 DCHECK(ready()); | |
| 474 | |
| 422 // Remove new snippets that we already have, or that have been discarded. | 475 // Remove new snippets that we already have, or that have been discarded. |
| 423 std::set<std::string> old_snippet_ids; | 476 std::set<std::string> old_snippet_ids; |
| 424 InsertAllIDs(discarded_snippets_, &old_snippet_ids); | 477 InsertAllIDs(discarded_snippets_, &old_snippet_ids); |
| 425 InsertAllIDs(snippets_, &old_snippet_ids); | 478 InsertAllIDs(snippets_, &old_snippet_ids); |
| 426 new_snippets.erase( | 479 new_snippets.erase( |
| 427 std::remove_if( | 480 std::remove_if( |
| 428 new_snippets.begin(), new_snippets.end(), | 481 new_snippets.begin(), new_snippets.end(), |
| 429 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { | 482 [&old_snippet_ids](const std::unique_ptr<NTPSnippet>& snippet) { |
| 430 if (old_snippet_ids.count(snippet->id())) | 483 if (old_snippet_ids.count(snippet->id())) |
| 431 return true; | 484 return true; |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 455 int num_new_snippets = new_snippets.size(); | 508 int num_new_snippets = new_snippets.size(); |
| 456 // Remove snippets that do not have all the info we need to display it to | 509 // Remove snippets that do not have all the info we need to display it to |
| 457 // the user. | 510 // the user. |
| 458 new_snippets.erase( | 511 new_snippets.erase( |
| 459 std::remove_if(new_snippets.begin(), new_snippets.end(), | 512 std::remove_if(new_snippets.begin(), new_snippets.end(), |
| 460 [](const std::unique_ptr<NTPSnippet>& snippet) { | 513 [](const std::unique_ptr<NTPSnippet>& snippet) { |
| 461 return !snippet->is_complete(); | 514 return !snippet->is_complete(); |
| 462 }), | 515 }), |
| 463 new_snippets.end()); | 516 new_snippets.end()); |
| 464 int num_snippets_discarded = num_new_snippets - new_snippets.size(); | 517 int num_snippets_discarded = num_new_snippets - new_snippets.size(); |
| 518 DVLOG(1) << "[MergeSnippets] histogram -> IncompleteSnippetsAfterFetch"; | |
| 465 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", | 519 UMA_HISTOGRAM_BOOLEAN("NewTabPage.Snippets.IncompleteSnippetsAfterFetch", |
| 466 num_snippets_discarded > 0); | 520 num_snippets_discarded > 0); |
| 467 if (num_snippets_discarded > 0) { | 521 if (num_snippets_discarded > 0) { |
| 522 DVLOG(1) << "[MergeSnippets] histogram -> NumIncompleteSnippets"; | |
| 468 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", | 523 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumIncompleteSnippets", |
| 469 num_snippets_discarded); | 524 num_snippets_discarded); |
| 470 } | 525 } |
| 471 } | 526 } |
| 527 | |
| 528 // Save the new snippets to the DB. | |
| 529 database_->Save(new_snippets); | |
| 530 | |
| 472 // Insert the new snippets at the front. | 531 // Insert the new snippets at the front. |
| 473 snippets_.insert(snippets_.begin(), | 532 snippets_.insert(snippets_.begin(), |
| 474 std::make_move_iterator(new_snippets.begin()), | 533 std::make_move_iterator(new_snippets.begin()), |
| 475 std::make_move_iterator(new_snippets.end())); | 534 std::make_move_iterator(new_snippets.end())); |
| 476 } | 535 } |
| 477 | 536 |
| 478 void NTPSnippetsService::LoadSnippetsFromPrefs() { | |
| 479 NTPSnippet::PtrVector prefs_snippets; | |
| 480 bool success = NTPSnippet::AddFromListValue( | |
| 481 *pref_service_->GetList(prefs::kSnippets), &prefs_snippets); | |
| 482 DCHECK(success) << "Failed to parse snippets from prefs"; | |
| 483 MergeSnippets(std::move(prefs_snippets)); | |
| 484 LoadingSnippetsFinished(); | |
| 485 } | |
| 486 | |
| 487 void NTPSnippetsService::StoreSnippetsToPrefs() { | |
| 488 pref_service_->Set(prefs::kSnippets, *SnippetsToListValue(snippets_)); | |
| 489 } | |
| 490 | |
| 491 void NTPSnippetsService::LoadDiscardedSnippetsFromPrefs() { | |
| 492 discarded_snippets_.clear(); | |
| 493 bool success = NTPSnippet::AddFromListValue( | |
| 494 *pref_service_->GetList(prefs::kDiscardedSnippets), &discarded_snippets_); | |
| 495 DCHECK(success) << "Failed to parse discarded snippets from prefs"; | |
| 496 } | |
| 497 | |
| 498 void NTPSnippetsService::StoreDiscardedSnippetsToPrefs() { | |
| 499 pref_service_->Set(prefs::kDiscardedSnippets, | |
| 500 *SnippetsToListValue(discarded_snippets_)); | |
| 501 } | |
| 502 | |
| 503 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { | 537 std::set<std::string> NTPSnippetsService::GetSnippetHostsFromPrefs() const { |
| 504 std::set<std::string> hosts; | 538 std::set<std::string> hosts; |
| 505 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); | 539 const base::ListValue* list = pref_service_->GetList(prefs::kSnippetHosts); |
| 506 for (const base::Value* value : *list) { | 540 for (const base::Value* value : *list) { |
| 507 std::string str; | 541 std::string str; |
| 508 bool success = value->GetAsString(&str); | 542 bool success = value->GetAsString(&str); |
| 509 DCHECK(success) << "Failed to parse snippet host from prefs"; | 543 DCHECK(success) << "Failed to parse snippet host from prefs"; |
| 510 hosts.insert(std::move(str)); | 544 hosts.insert(std::move(str)); |
| 511 } | 545 } |
| 512 return hosts; | 546 return hosts; |
| 513 } | 547 } |
| 514 | 548 |
| 515 void NTPSnippetsService::StoreSnippetHostsToPrefs( | 549 void NTPSnippetsService::StoreSnippetHostsToPrefs( |
| 516 const std::set<std::string>& hosts) { | 550 const std::set<std::string>& hosts) { |
| 517 base::ListValue list; | 551 base::ListValue list; |
| 518 for (const std::string& host : hosts) | 552 for (const std::string& host : hosts) |
| 519 list.AppendString(host); | 553 list.AppendString(host); |
| 520 pref_service_->Set(prefs::kSnippetHosts, list); | 554 pref_service_->Set(prefs::kSnippetHosts, list); |
| 521 } | 555 } |
| 522 | 556 |
| 523 void NTPSnippetsService::LoadingSnippetsFinished() { | 557 void NTPSnippetsService::LoadingSnippetsFinished() { |
| 558 DCHECK(ready() || state_ == State::INITED); | |
| 559 | |
| 524 // Remove expired snippets. | 560 // Remove expired snippets. |
| 525 base::Time expiry = base::Time::Now(); | 561 base::Time expiry = base::Time::Now(); |
| 526 | 562 |
| 527 snippets_.erase( | 563 // Move expired snippets over into |to_delete|. |
| 528 std::remove_if(snippets_.begin(), snippets_.end(), | 564 NTPSnippet::PtrVector to_delete; |
| 529 [&expiry](const std::unique_ptr<NTPSnippet>& snippet) { | 565 for (std::unique_ptr<NTPSnippet>& snippet : snippets_) { |
| 530 return snippet->expiry_date() <= expiry; | 566 if (snippet->expiry_date() <= expiry) |
| 531 }), | 567 to_delete.emplace_back(std::move(snippet)); |
| 532 snippets_.end()); | 568 } |
| 569 EraseNullptrs(&snippets_); | |
| 533 | 570 |
| 534 // If there are more snippets now than we want to show, drop the extra ones | 571 // If there are still more snippets than we want to show, move the extra ones |
| 535 // from the end of the list. | 572 // over into |to_delete| as well. |
| 536 if (snippets_.size() > kMaxSnippetCount) | 573 if (snippets_.size() > kMaxSnippetCount) { |
| 574 to_delete.insert( | |
| 575 to_delete.end(), | |
| 576 std::make_move_iterator(snippets_.begin() + kMaxSnippetCount), | |
| 577 std::make_move_iterator(snippets_.end())); | |
| 537 snippets_.resize(kMaxSnippetCount); | 578 snippets_.resize(kMaxSnippetCount); |
| 579 } | |
| 538 | 580 |
| 539 StoreSnippetsToPrefs(); | 581 // Move expired discarded snippets over into |to_delete| as well. |
| 582 for (std::unique_ptr<NTPSnippet>& snippet : discarded_snippets_) { | |
| 583 if (snippet->expiry_date() <= expiry) | |
| 584 to_delete.emplace_back(std::move(snippet)); | |
| 585 } | |
| 586 EraseNullptrs(&discarded_snippets_); | |
| 540 | 587 |
| 541 discarded_snippets_.erase( | 588 // Finally, actually delete the removed snippets from the DB. |
| 542 std::remove_if(discarded_snippets_.begin(), discarded_snippets_.end(), | 589 database_->Delete(to_delete); |
| 543 [&expiry](const std::unique_ptr<NTPSnippet>& snippet) { | |
| 544 return snippet->expiry_date() <= expiry; | |
| 545 }), | |
| 546 discarded_snippets_.end()); | |
| 547 StoreDiscardedSnippetsToPrefs(); | |
| 548 | 590 |
| 591 DVLOG(1) << "[LoadingSnippetsFinished] NTPSnippetsServiceLoaded -> Histogram " | |
| 592 "NumArticles"; | |
| 549 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", | 593 UMA_HISTOGRAM_SPARSE_SLOWLY("NewTabPage.Snippets.NumArticles", |
| 550 snippets_.size()); | 594 snippets_.size()); |
| 551 if (snippets_.empty() && !discarded_snippets_.empty()) { | 595 if (snippets_.empty() && !discarded_snippets_.empty()) { |
| 596 DVLOG(1) << "[LoadingSnippetsFinished] NTPSnippetsServiceLoaded -> " | |
| 597 "Histogram NumArticlesZeroDueToDiscarded"; | |
| 552 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", | 598 UMA_HISTOGRAM_COUNTS("NewTabPage.Snippets.NumArticlesZeroDueToDiscarded", |
| 553 discarded_snippets_.size()); | 599 discarded_snippets_.size()); |
| 554 } | 600 } |
| 555 | 601 |
| 556 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | 602 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, |
| 557 NTPSnippetsServiceLoaded()); | 603 NTPSnippetsServiceLoaded()); |
| 558 | 604 |
| 559 // If there are any snippets left, schedule a timer for the next expiry. | 605 // If there are any snippets left, schedule a timer for the next expiry. |
| 560 if (snippets_.empty() && discarded_snippets_.empty()) | 606 if (snippets_.empty() && discarded_snippets_.empty()) |
| 561 return; | 607 return; |
| 562 | 608 |
| 563 base::Time next_expiry = base::Time::Max(); | 609 base::Time next_expiry = base::Time::Max(); |
| 564 for (const auto& snippet : snippets_) { | 610 for (const auto& snippet : snippets_) { |
| 565 if (snippet->expiry_date() < next_expiry) | 611 if (snippet->expiry_date() < next_expiry) |
| 566 next_expiry = snippet->expiry_date(); | 612 next_expiry = snippet->expiry_date(); |
| 567 } | 613 } |
| 568 for (const auto& snippet : discarded_snippets_) { | 614 for (const auto& snippet : discarded_snippets_) { |
| 569 if (snippet->expiry_date() < next_expiry) | 615 if (snippet->expiry_date() < next_expiry) |
| 570 next_expiry = snippet->expiry_date(); | 616 next_expiry = snippet->expiry_date(); |
| 571 } | 617 } |
| 572 DCHECK_GT(next_expiry, expiry); | 618 DCHECK_GT(next_expiry, expiry); |
| 573 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, | 619 expiry_timer_.Start(FROM_HERE, next_expiry - expiry, |
| 574 base::Bind(&NTPSnippetsService::LoadingSnippetsFinished, | 620 base::Bind(&NTPSnippetsService::LoadingSnippetsFinished, |
| 575 base::Unretained(this))); | 621 base::Unretained(this))); |
| 576 } | 622 } |
| 577 | 623 |
| 578 bool NTPSnippetsService::IsSyncStateIncompatible() { | 624 void NTPSnippetsService::LoadFromDB() { |
| 579 if (!sync_service_ || !sync_service_->CanSyncStart()) | 625 database_->Load(base::Bind(&NTPSnippetsService::OnDatabaseLoaded, |
| 580 return true; | 626 base::Unretained(this))); |
| 581 if (!sync_service_->IsSyncActive() || !sync_service_->ConfigurationDone()) | 627 } |
| 582 return false; // Sync service is not initialized, yet not disabled. | 628 |
| 583 return !sync_service_->GetActiveDataTypes().Has( | 629 void NTPSnippetsService::Enable() { |
| 584 syncer::HISTORY_DELETE_DIRECTIVES); | 630 // We might already have snippets from loading the DB and want to avoid a |
| 631 // double load. | |
| 632 if (snippets_.empty()) | |
| 633 FetchSnippets(); | |
| 634 | |
| 635 // If host restrictions are enabled, register for host list updates. | |
| 636 // |suggestions_service_| can be null in tests. | |
| 637 if (snippets_fetcher_->UsesHostRestrictions() && suggestions_service_) { | |
| 638 suggestions_service_subscription_ = | |
| 639 suggestions_service_->AddCallback(base::Bind( | |
| 640 &NTPSnippetsService::OnSuggestionsChanged, base::Unretained(this))); | |
| 641 } | |
| 642 RescheduleFetching(); | |
| 643 } | |
| 644 | |
| 645 void NTPSnippetsService::Disable() { | |
| 646 ClearSnippets(); | |
| 647 | |
| 648 suggestions_service_subscription_.reset(); | |
| 649 RescheduleFetching(); | |
| 650 FOR_EACH_OBSERVER(NTPSnippetsServiceObserver, observers_, | |
| 651 NTPSnippetsServiceDisabled()); | |
| 652 } | |
| 653 | |
| 654 State NTPSnippetsService::GetStateForDependenciesStatus() { | |
| 655 DisabledReason dr = GetDisabledReason(); | |
| 656 | |
| 657 if (dr == DisabledReason::NONE) | |
| 658 return State::READY; | |
| 659 | |
| 660 // HistorySync is not initialized yet, so we don't know what the actual state | |
| 661 // is. However, because we retreived the previous snippets from the database, | |
| 662 // if we got something, we know that the service was previously enabled, so | |
| 663 // we just restore that state. If things changed, |OnStateChanged| will call | |
| 664 // this function again to fix the state. | |
| 665 if (dr == DisabledReason::HISTORY_SYNC_STATE_UNKNOWN) { | |
| 666 DVLOG(1) << "Sync configuration not done, continuing based on the current " | |
| 667 "state."; | |
| 668 return snippets_.empty() ? State::DISABLED : State::READY; | |
| 669 } | |
| 670 | |
| 671 return State::DISABLED; | |
| 672 } | |
| 673 | |
| 674 void NTPSnippetsService::EnterState(State state) { | |
| 675 switch (state) { | |
| 676 case State::NOT_INITED: | |
| 677 // Initial state, it should not be possible to get back there. | |
| 678 NOTREACHED(); | |
| 679 return; | |
| 680 | |
| 681 case State::INITED: | |
| 682 DCHECK(state_ == State::NOT_INITED); | |
| 683 state_ = State::INITED; | |
| 684 LoadFromDB(); | |
| 685 DVLOG(1) << "Entering state: INITED"; | |
| 686 return; | |
| 687 | |
| 688 case State::READY: | |
| 689 DCHECK(state_ == State::INITED || state_ == State::READY || | |
| 690 state_ == State::DISABLED); | |
| 691 if (state_ == State::READY) | |
| 692 return; | |
| 693 | |
| 694 DVLOG(1) << "Entering state: READY"; | |
| 695 state_ = State::READY; | |
| 696 Enable(); | |
| 697 return; | |
| 698 | |
| 699 case State::DISABLED: | |
| 700 DCHECK(state_ == State::NOT_INITED || state_ == State::READY || | |
| 701 state_ == State::DISABLED || state_ == State::INITED); | |
| 702 if (state_ == State::DISABLED) | |
| 703 return; | |
| 704 | |
| 705 DVLOG(1) << "Entering state: DISABLED"; | |
| 706 state_ = State::DISABLED; | |
| 707 Disable(); | |
| 708 return; | |
| 709 | |
| 710 case State::SHUT_DOWN: | |
| 711 DCHECK(state_ == State::INITED || state_ == State::DISABLED || | |
| 712 state_ == State::READY); | |
| 713 DVLOG(1) << "Entering state: SHUT_DOWN"; | |
| 714 state_ = State::SHUT_DOWN; | |
| 715 return; | |
| 716 } | |
| 585 } | 717 } |
| 586 | 718 |
| 587 } // namespace ntp_snippets | 719 } // namespace ntp_snippets |
| OLD | NEW |