| // Copyright 2021 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/search/background/ntp_custom_background_service.h" |
| |
| #include <string> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/functional/bind.h" |
| #include "base/observer_list.h" |
| #include "base/task/thread_pool.h" |
| #include "base/time/clock.h" |
| #include "base/time/default_clock.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/image_fetcher/image_decoder_impl.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/search/background/ntp_background_service_factory.h" |
| #include "chrome/browser/search/background/ntp_custom_background_service_observer.h" |
| #include "chrome/browser/themes/theme_service_factory.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/search/instant_types.h" |
| #include "chrome/common/url_constants.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/search/ntp_features.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/storage_partition.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "ui/gfx/color_analysis.h" |
| #include "ui/gfx/image/image.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| const char kNtpCustomBackgroundURL[] = "background_url"; |
| const char kNtpCustomBackgroundAttributionLine1[] = "attribution_line_1"; |
| const char kNtpCustomBackgroundAttributionLine2[] = "attribution_line_2"; |
| const char kNtpCustomBackgroundAttributionActionURL[] = |
| "attribution_action_url"; |
| const char kNtpCustomBackgroundCollectionId[] = "collection_id"; |
| const char kNtpCustomBackgroundResumeToken[] = "resume_token"; |
| const char kNtpCustomBackgroundRefreshTimestamp[] = "refresh_timestamp"; |
| const char kNtpCustomBackgroundMainColor[] = "background_main_color"; |
| |
| constexpr char kSidePanelSnapshotImageOptions[] = "=w320-h180-p-k-no-nd-mv"; |
| |
| base::Value::Dict GetBackgroundInfoAsDict( |
| const GURL& background_url, |
| const std::string& attribution_line_1, |
| const std::string& attribution_line_2, |
| const GURL& action_url, |
| const absl::optional<std::string>& collection_id, |
| const absl::optional<std::string>& resume_token, |
| const absl::optional<int> refresh_timestamp) { |
| base::Value::Dict background_info; |
| background_info.Set(kNtpCustomBackgroundURL, |
| base::Value(background_url.spec())); |
| background_info.Set(kNtpCustomBackgroundAttributionLine1, |
| base::Value(attribution_line_1)); |
| background_info.Set(kNtpCustomBackgroundAttributionLine2, |
| base::Value(attribution_line_2)); |
| background_info.Set(kNtpCustomBackgroundAttributionActionURL, |
| base::Value(action_url.spec())); |
| background_info.Set(kNtpCustomBackgroundCollectionId, |
| base::Value(collection_id.value_or(""))); |
| background_info.Set(kNtpCustomBackgroundResumeToken, |
| base::Value(resume_token.value_or(""))); |
| background_info.Set(kNtpCustomBackgroundRefreshTimestamp, |
| base::Value(refresh_timestamp.value_or(0))); |
| |
| return background_info; |
| } |
| |
| base::Value::Dict GetBackgroundInfoWithColor( |
| const base::Value::Dict* background_info, |
| const SkColor color) { |
| base::Value::Dict new_background_info = background_info->Clone(); |
| new_background_info.Set(kNtpCustomBackgroundMainColor, |
| base::Value(static_cast<int>(color))); |
| return new_background_info; |
| } |
| |
| base::Value NtpCustomBackgroundDefaults() { |
| base::Value defaults(base::Value::Type::DICT); |
| defaults.SetKey(kNtpCustomBackgroundURL, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundAttributionLine1, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundAttributionLine2, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundAttributionActionURL, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundCollectionId, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundResumeToken, |
| base::Value(base::Value::Type::STRING)); |
| defaults.SetKey(kNtpCustomBackgroundRefreshTimestamp, |
| base::Value(base::Value::Type::INTEGER)); |
| return defaults; |
| } |
| |
| void CopyFileToProfilePath(const base::FilePath& from_path, |
| const base::FilePath& profile_path) { |
| base::CopyFile(from_path, |
| profile_path.AppendASCII( |
| chrome::kChromeUIUntrustedNewTabPageBackgroundFilename)); |
| } |
| |
| void RemoveLocalBackgroundImageCopy(Profile* profile) { |
| base::FilePath path = profile->GetPath().AppendASCII( |
| chrome::kChromeUIUntrustedNewTabPageBackgroundFilename); |
| base::ThreadPool::PostTask( |
| FROM_HERE, {base::TaskPriority::BEST_EFFORT, base::MayBlock()}, |
| base::GetDeleteFileCallback(path)); |
| } |
| |
| // |GetBitmapMainColor| just wraps |CalculateKMeanColorOfBitmap|. |
| // As |CalculateKMeanColorOfBitmap| is overloaded, it cannot be bind for async |
| // call. |
| SkColor GetBitmapMainColor(const SkBitmap& bitmap) { |
| return color_utils::CalculateKMeanColorOfBitmap(bitmap); |
| } |
| |
| } // namespace |
| |
| // static |
| void NtpCustomBackgroundService::RegisterProfilePrefs( |
| PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref( |
| prefs::kNtpCustomBackgroundDict, NtpCustomBackgroundDefaults(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); |
| registry->RegisterBooleanPref(prefs::kNtpCustomBackgroundLocalToDevice, |
| false); |
| } |
| |
| // static |
| void NtpCustomBackgroundService::ResetProfilePrefs(Profile* profile) { |
| profile->GetPrefs()->ClearPref(prefs::kNtpCustomBackgroundDict); |
| profile->GetPrefs()->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, |
| false); |
| RemoveLocalBackgroundImageCopy(profile); |
| } |
| |
| NtpCustomBackgroundService::NtpCustomBackgroundService(Profile* profile) |
| : profile_(profile), |
| pref_service_(profile_->GetPrefs()), |
| clock_(base::DefaultClock::GetInstance()), |
| background_updated_timestamp_(base::TimeTicks::Now()) { |
| background_service_ = NtpBackgroundServiceFactory::GetForProfile(profile_); |
| theme_service_ = ThemeServiceFactory::GetForProfile(profile_); |
| if (background_service_) |
| background_service_observation_.Observe(background_service_.get()); |
| |
| // Update theme info when the pref is changed via Sync. |
| pref_change_registrar_.Init(pref_service_); |
| pref_change_registrar_.Add( |
| prefs::kNtpCustomBackgroundDict, |
| base::BindRepeating(&NtpCustomBackgroundService::UpdateBackgroundFromSync, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| image_fetcher_ = std::make_unique<image_fetcher::ImageFetcherImpl>( |
| std::make_unique<ImageDecoderImpl>(), |
| profile_->GetDefaultStoragePartition() |
| ->GetURLLoaderFactoryForBrowserProcess()); |
| } |
| |
| NtpCustomBackgroundService::~NtpCustomBackgroundService() = default; |
| |
| void NtpCustomBackgroundService::Shutdown() { |
| for (NtpCustomBackgroundServiceObserver& observer : observers_) |
| observer.OnNtpCustomBackgroundServiceShuttingDown(); |
| } |
| |
| void NtpCustomBackgroundService::OnCollectionInfoAvailable() {} |
| |
| void NtpCustomBackgroundService::OnCollectionImagesAvailable() {} |
| |
| void NtpCustomBackgroundService::OnNextCollectionImageAvailable() { |
| auto image = background_service_->next_image(); |
| std::string attribution1; |
| std::string attribution2; |
| if (image.attribution.size() > 0) |
| attribution1 = image.attribution[0]; |
| if (image.attribution.size() > 1) |
| attribution2 = image.attribution[1]; |
| |
| std::string resume_token = background_service_->next_image_resume_token(); |
| int64_t timestamp = (clock_->Now() + base::Days(1)).ToTimeT(); |
| |
| if (base::FeatureList::IsEnabled( |
| ntp_features::kCustomizeChromeColorExtraction)) { |
| FetchCustomBackgroundAndExtractBackgroundColor(image.image_url, |
| image.thumbnail_image_url); |
| } |
| |
| base::Value::Dict background_info = GetBackgroundInfoAsDict( |
| image.image_url, attribution1, attribution2, image.attribution_action_url, |
| image.collection_id, resume_token, timestamp); |
| |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| pref_service_->SetDict(prefs::kNtpCustomBackgroundDict, |
| std::move(background_info)); |
| } |
| |
| void NtpCustomBackgroundService::OnNtpBackgroundServiceShuttingDown() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| background_service_observation_.Reset(); |
| background_service_ = nullptr; |
| } |
| |
| void NtpCustomBackgroundService::UpdateBackgroundFromSync() { |
| // Any incoming change to synced background data should clear the local image. |
| pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false); |
| RemoveLocalBackgroundImageCopy(profile_); |
| NotifyAboutBackgrounds(); |
| } |
| |
| void NtpCustomBackgroundService::ResetCustomBackgroundInfo() { |
| SetCustomBackgroundInfo(GURL(), GURL(), std::string(), std::string(), GURL(), |
| std::string()); |
| } |
| |
| void NtpCustomBackgroundService::UpdateCustomBackgroundColorAsync( |
| const GURL& image_url, |
| const gfx::Image& fetched_image, |
| const image_fetcher::RequestMetadata& metadata) { |
| // Calculate the bitmap color asynchronously as it is slow (1-2 seconds for |
| // the thumbnail). However, prefs should be updated on the main thread. |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, {base::TaskPriority::BEST_EFFORT}, |
| base::BindOnce(&GetBitmapMainColor, fetched_image.AsBitmap()), |
| base::BindOnce( |
| &NtpCustomBackgroundService::UpdateCustomBackgroundPrefsWithColor, |
| weak_ptr_factory_.GetWeakPtr(), image_url)); |
| } |
| |
| void NtpCustomBackgroundService::FetchCustomBackgroundAndExtractBackgroundColor( |
| const GURL& image_url, |
| const GURL& fetch_url) { |
| DCHECK(!fetch_url.is_empty()); |
| |
| net::NetworkTrafficAnnotationTag traffic_annotation = |
| net::DefineNetworkTrafficAnnotation("ntp_custom_background", |
| R"( |
| semantics { |
| sender: "Desktop Chrome background fetcher" |
| description: |
| "Fetch New Tab Page background image for color calculation." |
| trigger: |
| "User selects new collection image background on the New Tab " |
| "Page." |
| data: "The only data sent is the URL to an image." |
| destination: GOOGLE_OWNED_SERVICE |
| internal { |
| contacts { |
| email: "chrome-desktop-ntp@google.com" |
| } |
| } |
| user_data { |
| type: NONE |
| } |
| last_reviewed: "2023-01-09" |
| } |
| policy { |
| cookies_allowed: NO |
| setting: |
| "Users cannot disable this feature. The feature is enabled by " |
| "default." |
| chrome_policy { |
| NTPCustomBackgroundEnabled { |
| NTPCustomBackgroundEnabled: true |
| } |
| } |
| })"); |
| |
| image_fetcher::ImageFetcherParams params(traffic_annotation, |
| "NtpCustomBackgrounds"); |
| image_fetcher_->FetchImage( |
| fetch_url, |
| base::BindOnce( |
| &NtpCustomBackgroundService::UpdateCustomBackgroundColorAsync, |
| weak_ptr_factory_.GetWeakPtr(), image_url), |
| std::move(params)); |
| } |
| |
| void NtpCustomBackgroundService::UpdateCustomBackgroundPrefsWithColor( |
| const GURL& image_url, |
| SkColor color) { |
| // Update background color only if the selected background is still the same. |
| const base::Value::Dict& background_info = |
| pref_service_->GetDict(prefs::kNtpCustomBackgroundDict); |
| |
| GURL current_bg_url( |
| background_info.Find(kNtpCustomBackgroundURL)->GetString()); |
| if (current_bg_url == image_url) { |
| pref_service_->SetDict(prefs::kNtpCustomBackgroundDict, |
| GetBackgroundInfoWithColor(&background_info, color)); |
| theme_service_->BuildAutogeneratedThemeFromColor(color); |
| } |
| } |
| |
| void NtpCustomBackgroundService::SetCustomBackgroundInfo( |
| const GURL& background_url, |
| const GURL& thumbnail_url, |
| const std::string& attribution_line_1, |
| const std::string& attribution_line_2, |
| const GURL& action_url, |
| const std::string& collection_id) { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| if (IsCustomBackgroundDisabledByPolicy()) { |
| return; |
| } |
| // Store current background info before it is changed so it can be used if |
| // RevertBackgroundChanges is called. |
| if (previous_background_info_ == absl::nullopt) { |
| previous_background_info_ = absl::make_optional( |
| pref_service_->GetValue(prefs::kNtpCustomBackgroundDict).Clone()); |
| previous_local_background_ = false; |
| } |
| |
| bool is_backdrop_collection = |
| background_service_ && |
| background_service_->IsValidBackdropCollection(collection_id); |
| bool is_backdrop_url = |
| background_service_ && |
| background_service_->IsValidBackdropUrl(background_url); |
| |
| bool need_forced_refresh = |
| pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice) && |
| pref_service_->FindPreference(prefs::kNtpCustomBackgroundDict) |
| ->IsDefaultValue(); |
| pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, false); |
| RemoveLocalBackgroundImageCopy(profile_); |
| |
| background_updated_timestamp_ = base::TimeTicks::Now(); |
| |
| if (!background_url.is_valid() && !collection_id.empty() && |
| is_backdrop_collection) { |
| background_service_->FetchNextCollectionImage(collection_id, absl::nullopt); |
| } else if (background_url.is_valid() && is_backdrop_url) { |
| if (base::FeatureList::IsEnabled( |
| ntp_features::kCustomizeChromeColorExtraction) && |
| thumbnail_url.is_valid()) { |
| FetchCustomBackgroundAndExtractBackgroundColor(background_url, |
| thumbnail_url); |
| } |
| base::Value::Dict background_info = GetBackgroundInfoAsDict( |
| background_url, attribution_line_1, attribution_line_2, action_url, |
| collection_id, absl::nullopt, absl::nullopt); |
| pref_service_->SetDict(prefs::kNtpCustomBackgroundDict, |
| std::move(background_info)); |
| } else { |
| pref_service_->ClearPref(prefs::kNtpCustomBackgroundDict); |
| |
| // If this device was using a local image and did not have a non-local |
| // background saved, UpdateBackgroundFromSync will not fire. Therefore, we |
| // need to force a refresh here. |
| if (need_forced_refresh) { |
| NotifyAboutBackgrounds(); |
| } |
| } |
| } |
| |
| void NtpCustomBackgroundService::SelectLocalBackgroundImage( |
| const base::FilePath& path) { |
| if (IsCustomBackgroundDisabledByPolicy()) { |
| return; |
| } |
| previous_background_info_.reset(); |
| previous_local_background_ = true; |
| base::ThreadPool::PostTaskAndReply( |
| FROM_HERE, {base::TaskPriority::USER_VISIBLE, base::MayBlock()}, |
| base::BindOnce(&CopyFileToProfilePath, path, profile_->GetPath()), |
| base::BindOnce(&NtpCustomBackgroundService::SetBackgroundToLocalResource, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void NtpCustomBackgroundService::RefreshBackgroundIfNeeded() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| const base::Value::Dict& background_info = |
| profile_->GetPrefs()->GetDict(prefs::kNtpCustomBackgroundDict); |
| int64_t refresh_timestamp = 0; |
| const base::Value* timestamp_value = |
| background_info.Find(kNtpCustomBackgroundRefreshTimestamp); |
| if (timestamp_value) |
| refresh_timestamp = timestamp_value->GetInt(); |
| if (refresh_timestamp == 0) |
| return; |
| |
| if (clock_->Now().ToTimeT() > refresh_timestamp) { |
| std::string collection_id = |
| background_info.Find(kNtpCustomBackgroundCollectionId)->GetString(); |
| std::string resume_token = |
| background_info.Find(kNtpCustomBackgroundResumeToken)->GetString(); |
| background_service_->FetchNextCollectionImage(collection_id, resume_token); |
| } |
| } |
| |
| absl::optional<CustomBackground> |
| NtpCustomBackgroundService::GetCustomBackground() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| |
| if (pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice)) { |
| auto custom_background = absl::make_optional<CustomBackground>(); |
| // Add a timestamp to the url to prevent the browser from using a cached |
| // version when "Upload an image" is used multiple times. |
| std::string time_string = std::to_string(base::Time::Now().ToTimeT()); |
| std::string local_string(chrome::kChromeUIUntrustedNewTabPageBackgroundUrl); |
| GURL timestamped_url(local_string + "?ts=" + time_string); |
| custom_background->custom_background_url = timestamped_url; |
| custom_background->is_uploaded_image = true; |
| custom_background->custom_background_snapshot_url = GURL(); |
| custom_background->custom_background_attribution_line_1 = std::string(); |
| custom_background->custom_background_attribution_line_2 = std::string(); |
| custom_background->custom_background_attribution_action_url = GURL(); |
| custom_background->collection_id = ""; |
| custom_background->daily_refresh_enabled = false; |
| return custom_background; |
| } |
| |
| // Attempt to get custom background URL from preferences. |
| if (IsCustomBackgroundPrefValid()) { |
| auto custom_background = absl::make_optional<CustomBackground>(); |
| const base::Value::Dict& background_info = |
| pref_service_->GetDict(prefs::kNtpCustomBackgroundDict); |
| GURL custom_background_url( |
| background_info.Find(kNtpCustomBackgroundURL)->GetString()); |
| |
| std::string collection_id; |
| const base::Value* id_value = |
| background_info.Find(kNtpCustomBackgroundCollectionId); |
| if (id_value) |
| collection_id = id_value->GetString(); |
| |
| // Set custom background information in theme info (attributions are |
| // optional). |
| const base::Value* daily_refresh_timestamp = |
| background_info.Find(kNtpCustomBackgroundRefreshTimestamp); |
| const base::Value* attribution_line_1 = |
| background_info.Find(kNtpCustomBackgroundAttributionLine1); |
| const base::Value* attribution_line_2 = |
| background_info.Find(kNtpCustomBackgroundAttributionLine2); |
| const base::Value* attribution_action_url = |
| background_info.Find(kNtpCustomBackgroundAttributionActionURL); |
| const base::Value* color = |
| base::FeatureList::IsEnabled( |
| ntp_features::kCustomizeChromeColorExtraction) |
| ? background_info.Find(kNtpCustomBackgroundMainColor) |
| : nullptr; |
| custom_background->custom_background_url = custom_background_url; |
| custom_background->is_uploaded_image = false; |
| custom_background->collection_id = collection_id; |
| custom_background->daily_refresh_enabled = |
| daily_refresh_timestamp && daily_refresh_timestamp->GetInt() != 0; |
| std::string custom_background_url_spec = custom_background_url.spec(); |
| size_t image_options_index = custom_background_url_spec.find("="); |
| if (image_options_index != std::string::npos) { |
| custom_background->custom_background_snapshot_url = |
| GURL(custom_background_url_spec.substr(0, image_options_index) + |
| kSidePanelSnapshotImageOptions); |
| } else { |
| custom_background->custom_background_snapshot_url = |
| GURL(custom_background_url_spec + kSidePanelSnapshotImageOptions); |
| } |
| if (attribution_line_1) { |
| custom_background->custom_background_attribution_line_1 = |
| background_info.Find(kNtpCustomBackgroundAttributionLine1) |
| ->GetString(); |
| } |
| if (attribution_line_2) { |
| custom_background->custom_background_attribution_line_2 = |
| background_info.Find(kNtpCustomBackgroundAttributionLine2) |
| ->GetString(); |
| } |
| if (attribution_action_url) { |
| GURL action_url( |
| background_info.Find(kNtpCustomBackgroundAttributionActionURL) |
| ->GetString()); |
| |
| if (!action_url.SchemeIsCryptographic()) { |
| custom_background->custom_background_attribution_action_url = GURL(); |
| } else { |
| custom_background->custom_background_attribution_action_url = |
| action_url; |
| } |
| } |
| if (color) { |
| custom_background->custom_background_main_color = |
| static_cast<uint32_t>(color->GetInt()); |
| } |
| return custom_background; |
| } |
| |
| return absl::nullopt; |
| } |
| |
| void NtpCustomBackgroundService::AddObserver( |
| NtpCustomBackgroundServiceObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void NtpCustomBackgroundService::RemoveObserver( |
| NtpCustomBackgroundServiceObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| bool NtpCustomBackgroundService::IsCustomBackgroundDisabledByPolicy() { |
| // |prefs::kNtpCustomBackgroundDict| is managed by policy only if |
| // |policy::key::kNTPCustomBackgroundEnabled| is set to false and therefore |
| // should be empty. |
| bool managed = |
| pref_service_->IsManagedPreference(prefs::kNtpCustomBackgroundDict); |
| if (managed) { |
| DCHECK(pref_service_->GetDict(prefs::kNtpCustomBackgroundDict).empty()); |
| } |
| return managed; |
| } |
| |
| bool NtpCustomBackgroundService::IsCustomBackgroundSet() { |
| return pref_service_->GetBoolean(prefs::kNtpCustomBackgroundLocalToDevice) || |
| IsCustomBackgroundPrefValid(); |
| } |
| |
| void NtpCustomBackgroundService::AddValidBackdropUrlForTesting( |
| const GURL& url) const { |
| background_service_->AddValidBackdropUrlForTesting(url); |
| } |
| |
| void NtpCustomBackgroundService::AddValidBackdropCollectionForTesting( |
| const std::string& collection_id) const { |
| background_service_->AddValidBackdropCollectionForTesting(collection_id); |
| } |
| |
| void NtpCustomBackgroundService::SetNextCollectionImageForTesting( |
| const CollectionImage& image) const { |
| background_service_->SetNextCollectionImageForTesting(image); |
| } |
| |
| void NtpCustomBackgroundService::SetClockForTesting(base::Clock* clock) { |
| clock_ = clock; |
| } |
| |
| void NtpCustomBackgroundService::SetBackgroundToLocalResource() { |
| background_updated_timestamp_ = base::TimeTicks::Now(); |
| pref_service_->SetBoolean(prefs::kNtpCustomBackgroundLocalToDevice, true); |
| NotifyAboutBackgrounds(); |
| } |
| |
| bool NtpCustomBackgroundService::IsCustomBackgroundPrefValid() { |
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| const base::Value::Dict& background_info = |
| profile_->GetPrefs()->GetDict(prefs::kNtpCustomBackgroundDict); |
| |
| const base::Value* background_url = |
| background_info.Find(kNtpCustomBackgroundURL); |
| if (!background_url) |
| return false; |
| |
| return GURL(background_url->GetString()).is_valid(); |
| } |
| |
| void NtpCustomBackgroundService::NotifyAboutBackgrounds() { |
| for (NtpCustomBackgroundServiceObserver& observer : observers_) |
| observer.OnCustomBackgroundImageUpdated(); |
| } |
| |
| void NtpCustomBackgroundService::RevertBackgroundChanges() { |
| if (previous_background_info_.has_value()) { |
| pref_service_->Set(prefs::kNtpCustomBackgroundDict, |
| *previous_background_info_); |
| } |
| if (previous_local_background_) { |
| SetBackgroundToLocalResource(); |
| } |
| previous_background_info_.reset(); |
| previous_local_background_ = false; |
| } |
| |
| void NtpCustomBackgroundService::ConfirmBackgroundChanges() { |
| previous_background_info_.reset(); |
| previous_local_background_ = false; |
| } |