1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef CHROME_BROWSER_THEMES_THEME_SERVICE_H_
6 #define CHROME_BROWSER_THEMES_THEME_SERVICE_H_
7 
8 #include <map>
9 #include <memory>
10 #include <string>
11 
12 #include "base/compiler_specific.h"
13 #include "base/macros.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/scoped_observer.h"
17 #include "base/task/cancelable_task_tracker.h"
18 #include "chrome/browser/themes/theme_helper.h"
19 #include "chrome/common/buildflags.h"
20 #include "components/keyed_service/core/keyed_service.h"
21 #include "extensions/buildflags/buildflags.h"
22 #include "extensions/common/extension_id.h"
23 #include "ui/base/theme_provider.h"
24 #include "ui/native_theme/native_theme.h"
25 #include "ui/native_theme/native_theme_observer.h"
26 
27 class BrowserThemePack;
28 class CustomThemeSupplier;
29 class ThemeSyncableService;
30 class Profile;
31 
32 namespace extensions {
33 class Extension;
34 }
35 
36 namespace theme_service_internal {
37 class ThemeServiceTest;
38 }
39 
40 class BrowserThemeProviderDelegate {
41  public:
42   virtual const CustomThemeSupplier* GetThemeSupplier() const = 0;
43 };
44 
45 class ThemeService : public KeyedService,
46                      public ui::NativeThemeObserver,
47                      public BrowserThemeProviderDelegate {
48  public:
49   // This class keeps track of the number of existing |ThemeReinstaller|
50   // objects. When that number reaches 0 then unused themes will be deleted.
51   class ThemeReinstaller {
52    public:
53     ThemeReinstaller(Profile* profile, base::OnceClosure installer);
54     ~ThemeReinstaller();
55 
56     void Reinstall();
57 
58    private:
59     base::OnceClosure installer_;
60     ThemeService* const theme_service_;
61 
62     DISALLOW_COPY_AND_ASSIGN(ThemeReinstaller);
63   };
64 
65   // Constant ID to use for all autogenerated themes.
66   static const char kAutogeneratedThemeID[];
67 
68   // Creates a ThemeProvider with a custom theme supplier specified via
69   // |delegate|. The return value must not outlive |profile|'s ThemeService.
70   static std::unique_ptr<ui::ThemeProvider> CreateBoundThemeProvider(
71       Profile* profile,
72       BrowserThemeProviderDelegate* delegate);
73 
74   explicit ThemeService(Profile* profile, const ThemeHelper& theme_helper);
75   ~ThemeService() override;
76 
77   void Init();
78 
79   // KeyedService:
80   void Shutdown() override;
81 
82   // Overridden from ui::NativeThemeObserver:
83   void OnNativeThemeUpdated(ui::NativeTheme* observed_theme) override;
84 
85   // Overridden from BrowserThemeProviderDelegate:
86   const CustomThemeSupplier* GetThemeSupplier() const override;
87 
88   // Set the current theme to the theme defined in |extension|.
89   // |extension| must already be added to this profile's
90   // ExtensionService.
91   void SetTheme(const extensions::Extension* extension);
92 
93   // Similar to SetTheme, but doesn't show an undo infobar.
94   void RevertToExtensionTheme(const std::string& extension_id);
95 
96   // Reset the theme to default.
97   virtual void UseDefaultTheme();
98 
99   // Set the current theme to the system theme. On some platforms, the system
100   // theme is the default theme.
101   virtual void UseSystemTheme();
102 
103   // Returns true if the default theme and system theme are not the same on
104   // this platform.
105   virtual bool IsSystemThemeDistinctFromDefaultTheme() const;
106 
107   // Forwards to ThemeProviderBase::IsDefaultTheme().
108   // Virtual for testing.
109   virtual bool UsingDefaultTheme() const;
110 
111   // Whether we are using the system theme. On GTK, the system theme is the GTK
112   // theme, not the "Classic" theme.
113   virtual bool UsingSystemTheme() const;
114 
115   // Forwards to ThemeProviderBase::IsExtensionTheme().
116   // Virtual for testing.
117   virtual bool UsingExtensionTheme() const;
118 
119   // Forwards to ThemeProviderBase::IsAutogeneratedTheme().
120   // Virtual for testing.
121   virtual bool UsingAutogeneratedTheme() const;
122 
123   // Gets the id of the last installed theme. (The theme may have been further
124   // locally customized.)
125   virtual std::string GetThemeID() const;
126 
127   // Uninstall theme extensions which are no longer in use.
128   void RemoveUnusedThemes();
129 
130   // Returns the syncable service for syncing theme. The returned service is
131   // owned by |this| object.
132   virtual ThemeSyncableService* GetThemeSyncableService() const;
133 
134   // Gets the ThemeProvider for |profile|. This will be different for an
135   // incognito profile and its original profile, even though both profiles use
136   // the same ThemeService.
137   static const ui::ThemeProvider& GetThemeProviderForProfile(Profile* profile);
138 
139   // Builds an autogenerated theme from a given |color| and applies it.
140   virtual void BuildAutogeneratedThemeFromColor(SkColor color);
141 
142   // Returns the theme color for an autogenerated theme.
143   virtual SkColor GetAutogeneratedThemeColor() const;
144 
145   // Returns |ThemeService::ThemeReinstaller| for the current theme.
146   std::unique_ptr<ThemeService::ThemeReinstaller>
147   BuildReinstallerForCurrentTheme();
148 
theme_helper_for_testing()149   const ThemeHelper& theme_helper_for_testing() const { return theme_helper_; }
150 
151   // Don't create "Cached Theme.pak" in the extension directory, for testing.
152   static void DisableThemePackForTesting();
153 
154  protected:
155   // Set a custom default theme instead of the normal default theme.
156   virtual void SetCustomDefaultTheme(
157       scoped_refptr<CustomThemeSupplier> theme_supplier);
158 
159   // Returns true if the ThemeService should use the system theme on startup.
160   virtual bool ShouldInitWithSystemTheme() const;
161 
162   // Clears all the override fields and saves the dictionary.
163   virtual void ClearAllThemeData();
164 
165   // Initialize current theme state data from preferences.
166   virtual void InitFromPrefs();
167 
168   // Let all the browser views know that themes have changed.
169   virtual void NotifyThemeChanged();
170 
171   // If there is an inconsistency in preferences, change preferences to a
172   // consistent state.
173   virtual void FixInconsistentPreferencesIfNeeded();
174 
profile()175   Profile* profile() const { return profile_; }
176 
set_ready()177   void set_ready() { ready_ = true; }
178 
179   // True if the theme service is ready to be used.
180   // TODO(pkotwicz): Add DCHECKS to the theme service's getters once
181   // ThemeSource no longer uses the ThemeService when it is not ready.
182   bool ready_ = false;
183 
184  private:
185   // This class implements ui::ThemeProvider on behalf of ThemeHelper and
186   // keeps track of the incognito state and CustemThemeSupplier for the calling
187   // code.
188   class BrowserThemeProvider : public ui::ThemeProvider {
189    public:
190     BrowserThemeProvider(const ThemeHelper& theme_helper,
191                          bool incognito,
192                          const BrowserThemeProviderDelegate* delegate);
193     ~BrowserThemeProvider() override;
194 
195     // Overridden from ui::ThemeProvider:
196     gfx::ImageSkia* GetImageSkiaNamed(int id) const override;
197     SkColor GetColor(int original_id) const override;
198     color_utils::HSL GetTint(int original_id) const override;
199     int GetDisplayProperty(int id) const override;
200     bool ShouldUseNativeFrame() const override;
201     bool HasCustomImage(int id) const override;
202     bool HasCustomColor(int id) const override;
203     base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor)
204         const override;
205 
206    private:
207     const CustomThemeSupplier* GetThemeSupplier() const;
208 
209     const ThemeHelper& theme_helper_;
210     bool incognito_;
211     const BrowserThemeProviderDelegate* delegate_;
212 
213     DISALLOW_COPY_AND_ASSIGN(BrowserThemeProvider);
214   };
215   friend class BrowserThemeProvider;
216   friend class theme_service_internal::ThemeServiceTest;
217 
218   // virtual for testing.
219   virtual void DoSetTheme(const extensions::Extension* extension,
220                           bool suppress_infobar);
221 
222 
223   // Called when the extension service is ready.
224   void OnExtensionServiceReady();
225 
226   // Migrate the theme to the new theme pack schema by recreating the data pack
227   // from the extension.
228   void MigrateTheme();
229 
230   // Replaces the current theme supplier with a new one and calls
231   // StopUsingTheme() or StartUsingTheme() as appropriate.
232   void SwapThemeSupplier(scoped_refptr<CustomThemeSupplier> theme_supplier);
233 
234   // Implementation of SetTheme() (and the fallback from InitFromPrefs() in
235   // case we don't have a theme pack). |new_theme| indicates whether this is a
236   // newly installed theme or a migration.
237   void BuildFromExtension(const extensions::Extension* extension,
238                           bool new_theme);
239 
240   // Callback when |pack| has finished or failed building.
241   void OnThemeBuiltFromExtension(const extensions::ExtensionId& extension_id,
242                                  scoped_refptr<BrowserThemePack> pack,
243                                  bool new_theme);
244 
245 #if BUILDFLAG(ENABLE_SUPERVISED_USERS)
246   // Returns true if the profile belongs to a supervised user.
247   bool IsSupervisedUser() const;
248 
249   // Sets the current theme to the supervised user theme. Should only be used
250   // for supervised user profiles.
251   void SetSupervisedUserTheme();
252 #endif
253 
254   // Functions that modify theme prefs.
255   void ClearThemePrefs();
256   void SetThemePrefsForExtension(const extensions::Extension* extension);
257   void SetThemePrefsForColor(SkColor color);
258 
259   bool DisableExtension(const std::string& extension_id);
260 
261   Profile* profile_;
262 
263   const ThemeHelper& theme_helper_;
264   scoped_refptr<CustomThemeSupplier> theme_supplier_;
265 
266   // The id of the theme extension which has just been installed but has not
267   // been loaded yet. The theme extension with |installed_pending_load_id_| may
268   // never be loaded if the install is due to updating a disabled theme.
269   // |pending_install_id_| should be set to |kDefaultThemeID| if there are no
270   // recently installed theme extensions
271   std::string installed_pending_load_id_ = ThemeHelper::kDefaultThemeID;
272 
273   // The number of infobars currently displayed.
274   int number_of_reinstallers_ = 0;
275 
276   std::unique_ptr<ThemeSyncableService> theme_syncable_service_;
277 
278 #if BUILDFLAG(ENABLE_EXTENSIONS)
279   class ThemeObserver;
280   std::unique_ptr<ThemeObserver> theme_observer_;
281 #endif
282 
283   BrowserThemeProvider original_theme_provider_;
284   BrowserThemeProvider incognito_theme_provider_;
285 
286   // Allows us to cancel building a theme pack from an extension.
287   base::CancelableTaskTracker build_extension_task_tracker_;
288 
289   // The ID of the theme that's currently being built on a different thread.
290   // We hold onto this just to be sure not to uninstall the extension view
291   // RemoveUnusedThemes while it's still being built.
292   std::string building_extension_id_;
293 
294   ScopedObserver<ui::NativeTheme, ui::NativeThemeObserver>
295       native_theme_observer_{this};
296 
297   base::WeakPtrFactory<ThemeService> weak_ptr_factory_{this};
298 
299   DISALLOW_COPY_AND_ASSIGN(ThemeService);
300 };
301 
302 #endif  // CHROME_BROWSER_THEMES_THEME_SERVICE_H_
303