blob: 1743695606c4827b116b7c890d11809cc1ae581a [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_THEMES_THEME_SERVICE_H_
#define CHROME_BROWSER_THEMES_THEME_SERVICE_H_
#include <memory>
#include <string>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ref.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/scoped_observation.h"
#include "base/task/cancelable_task_tracker.h"
#include "chrome/browser/search/background/theme_delegate.h"
#include "chrome/browser/themes/theme_helper.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h"
#include "extensions/buildflags/buildflags.h"
#include "extensions/common/extension_id.h"
#include "ui/base/mojom/themes.mojom.h"
#include "ui/base/theme_provider.h"
#include "ui/color/system_theme.h"
class BrowserThemePack;
class CustomThemeSupplier;
class NtpCustomBackgroundService;
class Profile;
class ThemeServiceObserver;
class ThemeSyncableService;
namespace extensions {
class Extension;
} // namespace extensions
namespace theme_service_internal {
class ThemeServiceTest;
} // namespace theme_service_internal
namespace ui {
class ColorProvider;
} // namespace ui
class BrowserThemeProviderDelegate {
public:
virtual CustomThemeSupplier* GetThemeSupplier() const = 0;
virtual bool ShouldUseCustomFrame() const = 0;
};
// A theme consists of a set of colors and images, including the NTP background
// image. See CustomThemeSupplier for details. There are multiple sources for
// themes, including extensions, NTP, the system theme, and policy.
// TODO(https://crbug.com/341787825): When the NTP generates a custom background
// image, the color is stored in ThemeService but the background image is stored
// in NtpCustomBackgroundService. This divergence from other theme sources
// introduces complexity and fragility.
class ThemeService : public KeyedService,
public BrowserThemeProviderDelegate,
public ThemeDelegate {
public:
// This is stored as an integer in the profile prefs, so entries should not be
// renumbered and numeric values should never be reused.
enum class BrowserColorScheme {
kSystem = 0,
kLight = 1,
kDark = 2,
};
// This class keeps track of the number of existing |ThemeReinstaller|
// objects. When that number reaches 0 then unused themes will be deleted.
class ThemeReinstaller {
public:
ThemeReinstaller(Profile* profile, base::OnceClosure installer);
ThemeReinstaller(const ThemeReinstaller&) = delete;
ThemeReinstaller& operator=(const ThemeReinstaller&) = delete;
~ThemeReinstaller();
void Reinstall();
private:
base::OnceClosure installer_;
const raw_ptr<ThemeService> theme_service_;
};
// Constant ID to use for all autogenerated themes.
static const char kAutogeneratedThemeID[];
// Constant ID to use for all user color themes.
static const char kUserColorThemeID[];
// Creates a ThemeProvider with a custom theme supplier specified via
// |delegate|. The return value must not outlive |profile|'s ThemeService.
static std::unique_ptr<ui::ThemeProvider> CreateBoundThemeProvider(
Profile* profile,
BrowserThemeProviderDelegate* delegate);
ThemeService(Profile* profile, const ThemeHelper& theme_helper);
ThemeService(const ThemeService&) = delete;
ThemeService& operator=(const ThemeService&) = delete;
~ThemeService() override;
void Init();
// KeyedService:
void Shutdown() override;
// Overridden from BrowserThemeProviderDelegate:
CustomThemeSupplier* GetThemeSupplier() const override;
bool ShouldUseCustomFrame() const override;
// Set the current theme to the theme defined in |extension|.
// |extension| must already be added to this profile's
// ExtensionService.
void SetTheme(const extensions::Extension* extension);
// Similar to SetTheme, but doesn't show an undo infobar.
void RevertToExtensionTheme(const std::string& extension_id);
// Sets the platform theme based on `system_theme`.
virtual void UseTheme(ui::SystemTheme system_theme);
// Reset the theme to default.
virtual void UseDefaultTheme();
// Toggle whether the browser follows its own theme or from the OS. For now,
// this is distinct from the ui::SystemTheme.
virtual void UseDeviceTheme(bool follow);
// Returns true if the browser should follow the OS theme.
virtual bool UsingDeviceTheme() const;
// Set the current theme to the system theme. On some platforms, the system
// theme is the default theme.
virtual void UseSystemTheme();
// Returns true if the default theme and system theme are not the same on
// this platform.
virtual bool IsSystemThemeDistinctFromDefaultTheme() const;
// Forwards to ThemeProviderBase::IsDefaultTheme().
// Virtual for testing.
virtual bool UsingDefaultTheme() const;
// Whether we are using the system theme. On Linux, the system theme is the
// GTK or QT themes, not the "Classic" theme.
virtual bool UsingSystemTheme() const;
// ThemeDelegate:
void OnBackgroundColorExtracted(SkColor color) override;
void OnNtpCustomBackgroundServiceShuttingDown() override;
bool UsingExtensionTheme() const override;
// Forwards to ThemeProviderBase::IsAutogeneratedTheme().
// Virtual for testing.
virtual bool UsingAutogeneratedTheme() const;
// Whether current theme colors are enforced through a policy.
// Virtual for testing.
virtual bool UsingPolicyTheme() const;
// Gets the id of the last installed theme. (The theme may have been further
// locally customized.)
virtual std::string GetThemeID() const;
// Uninstall theme extensions which are no longer in use.
void RemoveUnusedThemes();
// Returns the syncable service for syncing theme. The returned service is
// owned by |this| object.
virtual ThemeSyncableService* GetThemeSyncableService() const;
// Gets the ThemeProvider for |profile|. This will be different for an
// incognito profile and its original profile, even though both profiles use
// the same ThemeService.
//
// Before using this function, consider if the caller is in a rooted UI tree.
// If it is, strongly favor referring to the conceptual roots for a
// ThemeProvider. For Views, this is the Widget. For Cocoa, this is the
// AppControl.
static const ui::ThemeProvider& GetThemeProviderForProfile(Profile* profile);
static CustomThemeSupplier* GetThemeSupplierForProfile(Profile* profile);
// Gets the ColorProvider for this ThemeService.
//
// Before using this function, consider if the caller is in a rooted UI tree.
// If it is, strongly favor referring to the conceptual roots for a
// ColorProvider. For Views, this is the Widget. For Cocoa, this is the
// AppControl.
ui::ColorProvider* GetColorProvider();
// Builds an autogenerated theme from a given |color| and applies it.
virtual void BuildAutogeneratedThemeFromColor(SkColor color);
// Builds an autogenerated theme from a given |color| and applies it.
virtual void BuildAutogeneratedThemeFromColor(SkColor color,
bool store_in_prefs);
// Returns the theme color for an autogenerated theme.
virtual SkColor GetAutogeneratedThemeColor() const;
// Builds an autogenerated theme from the applied policy color and applies it.
virtual void BuildAutogeneratedPolicyTheme();
// Returns the theme color for the current policy theme.
virtual SkColor GetPolicyThemeColor() const;
// Sets the browser color scheme preference.
virtual void SetBrowserColorScheme(BrowserColorScheme color_scheme);
// Gets the browser color scheme preference.
virtual BrowserColorScheme GetBrowserColorScheme() const;
// Returns true if either the browser color scheme is dark, or the browser
// color scheme is "system" and the native theme is dark.
bool BrowserUsesDarkColors() const;
// Sets/gets the browser user color preference.
virtual void SetUserColor(std::optional<SkColor> user_color);
virtual std::optional<SkColor> GetUserColor() const;
// Sets/gets the browser kSchemeVariant preference.
void SetBrowserColorVariant(ui::mojom::BrowserColorVariant color_variant);
virtual ui::mojom::BrowserColorVariant GetBrowserColorVariant() const;
// Convenience method that allows setting both the kUserColor and
// kSchemeVariant before propagating theme update notifications.
virtual void SetUserColorAndBrowserColorVariant(
SkColor user_color,
ui::mojom::BrowserColorVariant color_variant);
// Sets/gets the browser grayscale theme preference.
virtual void SetIsGrayscale(bool is_grayscale);
virtual bool GetIsGrayscale() const;
// Returns true if the current theme is the "baseline" blue theme.
virtual bool GetIsBaseline() const;
// Returns |ThemeService::ThemeReinstaller| for the current theme.
std::unique_ptr<ThemeService::ThemeReinstaller>
BuildReinstallerForCurrentTheme();
// Virtual for testing.
virtual void AddObserver(ThemeServiceObserver* observer);
void RemoveObserver(ThemeServiceObserver* observer);
const ThemeHelper& theme_helper_for_testing() const { return *theme_helper_; }
// Don't create "Cached Theme.pak" in the extension directory, for testing.
static void DisableThemePackForTesting();
void ResetThemeSyncableServiceForTest();
protected:
// Set a custom default theme instead of the normal default theme.
virtual void SetCustomDefaultTheme(
scoped_refptr<CustomThemeSupplier> theme_supplier);
// Returns the theme service type that should be used on startup.
virtual ui::SystemTheme GetDefaultSystemTheme() const;
// Clears override fields and saves the dictionary.
virtual void ClearThemeData(bool clear_ntp_background);
// Initialize current theme state data from preferences.
virtual void InitFromPrefs();
// Let all the browser views know that themes have changed.
virtual void NotifyThemeChanged();
// If there is an inconsistency in preferences, change preferences to a
// consistent state.
virtual void FixInconsistentPreferencesIfNeeded();
Profile* profile() const { return profile_; }
void set_ready() { ready_ = true; }
// True if the theme service is ready to be used.
// TODO(pkotwicz): Add DCHECKS to the theme service's getters once
// ThemeSource no longer uses the ThemeService when it is not ready.
bool ready_ = false;
private:
// This class implements ui::ThemeProvider on behalf of ThemeHelper and
// keeps track of the incognito state and CustemThemeSupplier for the calling
// code.
class BrowserThemeProvider : public ui::ThemeProvider {
public:
BrowserThemeProvider(const ThemeHelper& theme_helper,
bool incognito,
const BrowserThemeProviderDelegate* delegate);
BrowserThemeProvider(const BrowserThemeProvider&) = delete;
BrowserThemeProvider& operator=(const BrowserThemeProvider&) = delete;
~BrowserThemeProvider() override;
// Overridden from ui::ThemeProvider:
gfx::ImageSkia* GetImageSkiaNamed(int id) const override;
color_utils::HSL GetTint(int original_id) const override;
int GetDisplayProperty(int id) const override;
bool ShouldUseNativeFrame() const override;
bool HasCustomImage(int id) const override;
base::RefCountedMemory* GetRawData(
int id,
ui::ResourceScaleFactor scale_factor) const override;
private:
CustomThemeSupplier* GetThemeSupplier() const;
const raw_ref<const ThemeHelper> theme_helper_;
bool incognito_;
raw_ptr<const BrowserThemeProviderDelegate> delegate_;
};
friend class BrowserThemeProvider;
friend class theme_service_internal::ThemeServiceTest;
// virtual for testing.
virtual void DoSetTheme(const extensions::Extension* extension,
bool suppress_infobar);
// Called when the extension service is ready.
void OnExtensionServiceReady();
// Migrate the theme to the new theme pack schema by recreating the data pack
// from the extension.
void MigrateTheme();
// Replaces the current theme supplier with a new one and calls
// StopUsingTheme() or StartUsingTheme() as appropriate.
void SwapThemeSupplier(scoped_refptr<CustomThemeSupplier> theme_supplier);
// Implementation of SetTheme() (and the fallback from InitFromPrefs() in
// case we don't have a theme pack). |new_theme| indicates whether this is a
// newly installed theme or a migration.
void BuildFromExtension(const extensions::Extension* extension,
bool new_theme);
// Callback when |pack| has finished or failed building.
void OnThemeBuiltFromExtension(const extensions::ExtensionId& extension_id,
scoped_refptr<BrowserThemePack> pack,
bool new_theme);
// Handles theme color policy pref updates. if policy value contains valid
// color(s), sets browser theme accordingly.
void HandlePolicyColorUpdate();
// Functions that modify theme prefs.
void ClearThemePrefs();
void SetThemePrefsForExtension(const extensions::Extension* extension);
void SetThemePrefsForColor(SkColor color);
bool DisableExtension(const std::string& extension_id);
raw_ptr<Profile> profile_;
PrefChangeRegistrar pref_change_registrar_;
const raw_ref<const ThemeHelper> theme_helper_;
scoped_refptr<CustomThemeSupplier> theme_supplier_;
// The id of the theme extension which has just been installed but has not
// been loaded yet. The theme extension with |installed_pending_load_id_| may
// never be loaded if the install is due to updating a disabled theme.
// |pending_install_id_| should be set to |kDefaultThemeID| if there are no
// recently installed theme extensions
std::string installed_pending_load_id_ = ThemeHelper::kDefaultThemeID;
// The number of infobars currently displayed.
int number_of_reinstallers_ = 0;
// Configuring ThemeService into a desired theme state may involve the
// configuration of multiple theme prefs. However updating a single theme pref
// may result in requests to propagate theme update notifications. This flag
// is used to avoid propagating updates until all theme prefs have been set
// to their desired values.
bool should_suppress_theme_updates_ = false;
// Declared before |theme_syncable_service_|, because ThemeSyncableService
// removes itself from the |observers_| list on destruction.
base::ObserverList<ThemeServiceObserver> observers_;
std::unique_ptr<ThemeSyncableService> theme_syncable_service_;
#if BUILDFLAG(ENABLE_EXTENSIONS)
class ThemeObserver;
std::unique_ptr<ThemeObserver> theme_observer_;
#endif
BrowserThemeProvider original_theme_provider_;
BrowserThemeProvider incognito_theme_provider_;
// Allows us to cancel building a theme pack from an extension.
base::CancelableTaskTracker build_extension_task_tracker_;
// The ID of the theme that's currently being built on a different thread.
// We hold onto this just to be sure not to uninstall the extension view
// RemoveUnusedThemes while it's still being built.
std::string building_extension_id_;
raw_ptr<NtpCustomBackgroundService> ntp_custom_background_service_ = nullptr;
base::WeakPtrFactory<ThemeService> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_THEMES_THEME_SERVICE_H_