blob: 425d76259b00a5d8ad1977ae7b38e314b135e542 [file] [log] [blame]
[email protected]f1567dd2012-01-12 18:15:471// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]18280372011-03-22 18:05:222// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/themes/theme_service.h"
6
avi664c07b2015-12-26 02:18:317#include <stddef.h>
8
huangsc23c1982014-09-16 16:37:529#include <algorithm>
10
[email protected]85bce45182011-11-29 03:10:5211#include "base/bind.h"
estadeb8867dff2016-11-03 17:33:3312#include "base/files/file_util.h"
skyostil02598352015-06-12 12:37:2513#include "base/location.h"
[email protected]90fa2652012-04-24 16:18:3514#include "base/memory/ref_counted_memory.h"
bratell0a7406f2017-03-28 07:46:3715#include "base/metrics/user_metrics.h"
[email protected]fb441962013-05-08 05:35:2416#include "base/sequenced_task_runner.h"
skyostil02598352015-06-12 12:37:2517#include "base/single_thread_task_runner.h"
[email protected]9f0abdb2013-06-10 21:49:3418#include "base/strings/string_util.h"
[email protected]e309f312013-06-07 21:50:0819#include "base/strings/utf_string_conversions.h"
gabb15e19072016-05-11 20:45:4120#include "base/threading/thread_task_runner_handle.h"
avi664c07b2015-12-26 02:18:3121#include "build/build_config.h"
[email protected]dcc8fbc2013-07-12 00:54:0922#include "chrome/browser/chrome_notification_types.h"
[email protected]18280372011-03-22 18:05:2223#include "chrome/browser/extensions/extension_service.h"
24#include "chrome/browser/profiles/profile.h"
[email protected]a0ea76c2011-03-23 17:36:4225#include "chrome/browser/themes/browser_theme_pack.h"
[email protected]ed0cef02013-07-26 12:41:1126#include "chrome/browser/themes/custom_theme_supplier.h"
[email protected]e119b802013-02-18 18:55:3927#include "chrome/browser/themes/theme_properties.h"
estade68691b282015-12-11 21:50:4028#include "chrome/browser/themes/theme_service_factory.h"
[email protected]280453e2012-09-28 19:09:4129#include "chrome/browser/themes/theme_syncable_service.h"
[email protected]a0ea76c2011-03-23 17:36:4230#include "chrome/common/chrome_constants.h"
brettw9e85ef42016-11-01 21:01:2431#include "chrome/common/features.h"
[email protected]a0ea76c2011-03-23 17:36:4232#include "chrome/common/pref_names.h"
thestig4a9b0ef2016-08-29 08:22:1233#include "chrome/grit/theme_resources.h"
34#include "components/grit/components_scaled_resources.h"
brettwb1fc1b82016-02-02 00:19:0835#include "components/prefs/pref_service.h"
[email protected]ad50def52011-10-19 23:17:0736#include "content/public/browser/notification_service.h"
[email protected]7c82539c2014-02-19 06:09:1737#include "extensions/browser/extension_prefs.h"
[email protected]f47f7172014-03-19 19:27:1038#include "extensions/browser/extension_registry.h"
[email protected]59b0e602014-01-30 00:41:2439#include "extensions/browser/extension_system.h"
[email protected]e43c61f2014-07-20 21:46:3440#include "extensions/browser/uninstall_reason.h"
[email protected]289c44b2013-12-17 03:26:5741#include "extensions/common/extension.h"
42#include "extensions/common/extension_set.h"
brettw00899e62016-11-12 02:10:1743#include "extensions/features/features.h"
[email protected]c49201a2012-05-24 11:04:5744#include "ui/base/layout.h"
[email protected]a0ea76c2011-03-23 17:36:4245#include "ui/base/resource/resource_bundle.h"
estade756366e2015-09-23 20:26:1946#include "ui/gfx/color_palette.h"
[email protected]546882b2012-05-11 00:53:3247#include "ui/gfx/image/image_skia.h"
estade4b7d20a2015-05-03 20:21:2648#include "ui/native_theme/common_theme.h"
49#include "ui/native_theme/native_theme.h"
[email protected]18280372011-03-22 18:05:2250
brettw00899e62016-11-12 02:10:1751#if BUILDFLAG(ENABLE_EXTENSIONS)
limasdf96f0702b2015-03-13 21:57:0952#include "extensions/browser/extension_registry_observer.h"
53#endif
54
brettw9e85ef42016-11-01 21:01:2455#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
mckeva1a18dc2014-09-29 18:52:3356#include "chrome/browser/supervised_user/supervised_user_theme.h"
57#endif
58
[email protected]e6e30ac2014-01-13 21:24:3959using base::UserMetricsAction;
[email protected]631bb742011-11-02 11:29:3960using content::BrowserThread;
[email protected]1c321ee52012-05-21 03:02:3461using extensions::Extension;
[email protected]b0af4792013-10-23 09:12:1362using extensions::UnloadedExtensionInfo;
[email protected]8e7b2cf42012-04-18 14:26:5863using ui::ResourceBundle;
[email protected]631bb742011-11-02 11:29:3964
pkastingf89f1552016-03-03 01:50:5065
66// Helpers --------------------------------------------------------------------
[email protected]a0ea76c2011-03-23 17:36:4267
68namespace {
69
70// The default theme if we've gone to the theme gallery and installed the
71// "Default" theme. We have to detect this case specifically. (By the time we
72// realize we've installed the default theme, we already have an extension
73// unpacked on the filesystem.)
thestig53c883cd2015-12-17 03:09:3374const char kDefaultThemeGalleryID[] = "hkacjpbfdknhflllbcmjibkdeoafencn";
[email protected]a0ea76c2011-03-23 17:36:4275
[email protected]b1ac70462013-08-14 21:33:3076// Wait this many seconds after startup to garbage collect unused themes.
77// Removing unused themes is done after a delay because there is no
78// reason to do it at startup.
79// ExtensionService::GarbageCollectExtensions() does something similar.
80const int kRemoveUnusedThemesStartupDelay = 30;
81
[email protected]a0ea76c2011-03-23 17:36:4282SkColor IncreaseLightness(SkColor color, double percent) {
83 color_utils::HSL result;
84 color_utils::SkColorToHSL(color, &result);
85 result.l += (1 - result.l) * percent;
86 return color_utils::HSLToSkColor(result, SkColorGetA(color));
87}
88
[email protected]a0ea76c2011-03-23 17:36:4289// Writes the theme pack to disk on a separate thread.
[email protected]7f8f24f2012-11-15 19:40:1490void WritePackToDiskCallback(BrowserThemePack* pack,
estadeb8867dff2016-11-03 17:33:3391 const base::FilePath& directory) {
92 base::FilePath path = directory.Append(chrome::kThemePackFilename);
[email protected]85bce45182011-11-29 03:10:5293 if (!pack->WriteToDisk(path))
94 NOTREACHED() << "Could not write theme pack to disk";
estadeb8867dff2016-11-03 17:33:3395
96 // Clean up any theme .pak that was generated during the Material Design
97 // transitional period.
98 // TODO(estade): remove this line in Q2 2017.
99 base::DeleteFile(directory.AppendASCII("Cached Theme Material Design.pak"),
100 false);
[email protected]85bce45182011-11-29 03:10:52101}
[email protected]a0ea76c2011-03-23 17:36:42102
huangsc23c1982014-09-16 16:37:52103// Heuristic to determine if color is grayscale. This is used to decide whether
104// to use the colorful or white logo, if a theme fails to specify which.
105bool IsColorGrayscale(SkColor color) {
106 const int kChannelTolerance = 9;
107 int r = SkColorGetR(color);
108 int g = SkColorGetG(color);
109 int b = SkColorGetB(color);
110 int range = std::max(r, std::max(g, b)) - std::min(r, std::min(g, b));
111 return range < kChannelTolerance;
112}
113
[email protected]a0ea76c2011-03-23 17:36:42114} // namespace
115
pkastingf89f1552016-03-03 01:50:50116
117// ThemeService::BrowserThemeProvider -----------------------------------------
118
119ThemeService::BrowserThemeProvider::BrowserThemeProvider(
120 const ThemeService& theme_service,
121 bool incognito)
122 : theme_service_(theme_service), incognito_(incognito) {}
123
124ThemeService::BrowserThemeProvider::~BrowserThemeProvider() {}
125
126gfx::ImageSkia* ThemeService::BrowserThemeProvider::GetImageSkiaNamed(
127 int id) const {
128 return theme_service_.GetImageSkiaNamed(id, incognito_);
129}
130
131SkColor ThemeService::BrowserThemeProvider::GetColor(int id) const {
132 return theme_service_.GetColor(id, incognito_);
133}
134
estade80c47662016-07-02 00:42:16135color_utils::HSL ThemeService::BrowserThemeProvider::GetTint(int id) const {
136 return theme_service_.GetTint(id, incognito_);
137}
138
pkastingf89f1552016-03-03 01:50:50139int ThemeService::BrowserThemeProvider::GetDisplayProperty(int id) const {
140 return theme_service_.GetDisplayProperty(id);
141}
142
143bool ThemeService::BrowserThemeProvider::ShouldUseNativeFrame() const {
144 return theme_service_.ShouldUseNativeFrame();
145}
146
147bool ThemeService::BrowserThemeProvider::HasCustomImage(int id) const {
148 return theme_service_.HasCustomImage(id);
149}
150
151base::RefCountedMemory* ThemeService::BrowserThemeProvider::GetRawData(
152 int id,
153 ui::ScaleFactor scale_factor) const {
154 return theme_service_.GetRawData(id, scale_factor);
155}
156
157
158// ThemeService::ThemeObserver ------------------------------------------------
159
brettw00899e62016-11-12 02:10:17160#if BUILDFLAG(ENABLE_EXTENSIONS)
limasdf96f0702b2015-03-13 21:57:09161class ThemeService::ThemeObserver
162 : public extensions::ExtensionRegistryObserver {
163 public:
164 explicit ThemeObserver(ThemeService* service) : theme_service_(service) {
165 extensions::ExtensionRegistry::Get(theme_service_->profile_)
166 ->AddObserver(this);
167 }
168
169 ~ThemeObserver() override {
170 extensions::ExtensionRegistry::Get(theme_service_->profile_)
171 ->RemoveObserver(this);
172 }
173
174 private:
175 void OnExtensionWillBeInstalled(content::BrowserContext* browser_context,
176 const extensions::Extension* extension,
177 bool is_update,
limasdf96f0702b2015-03-13 21:57:09178 const std::string& old_name) override {
179 if (extension->is_theme()) {
180 // The theme may be initially disabled. Wait till it is loaded (if ever).
181 theme_service_->installed_pending_load_id_ = extension->id();
182 }
183 }
184
185 void OnExtensionLoaded(content::BrowserContext* browser_context,
186 const extensions::Extension* extension) override {
187 if (extension->is_theme() &&
188 theme_service_->installed_pending_load_id_ != kDefaultThemeID &&
189 theme_service_->installed_pending_load_id_ == extension->id()) {
190 theme_service_->SetTheme(extension);
191 }
192 theme_service_->installed_pending_load_id_ = kDefaultThemeID;
193 }
194
195 void OnExtensionUnloaded(
196 content::BrowserContext* browser_context,
197 const extensions::Extension* extension,
198 extensions::UnloadedExtensionInfo::Reason reason) override {
199 if (reason != extensions::UnloadedExtensionInfo::REASON_UPDATE &&
200 reason != extensions::UnloadedExtensionInfo::REASON_LOCK_ALL &&
201 extension->is_theme() &&
202 extension->id() == theme_service_->GetThemeID()) {
203 theme_service_->UseDefaultTheme();
204 }
205 }
206
207 ThemeService* theme_service_;
208};
brettw00899e62016-11-12 02:10:17209#endif // BUILDFLAG(ENABLE_EXTENSIONS)
limasdf96f0702b2015-03-13 21:57:09210
pkastingf89f1552016-03-03 01:50:50211
212// ThemeService ---------------------------------------------------------------
213
214// The default theme if we haven't installed a theme yet or if we've clicked
215// the "Use Classic" button.
216const char ThemeService::kDefaultThemeID[] = "";
217
[email protected]a0ea76c2011-03-23 17:36:42218ThemeService::ThemeService()
[email protected]94d410e2013-08-07 21:51:30219 : ready_(false),
220 rb_(ResourceBundle::GetSharedInstance()),
limasdf96f0702b2015-03-13 21:57:09221 profile_(nullptr),
[email protected]b1ac70462013-08-14 21:33:30222 installed_pending_load_id_(kDefaultThemeID),
[email protected]43b424e2013-08-12 04:51:13223 number_of_infobars_(0),
estade68691b282015-12-11 21:50:40224 original_theme_provider_(*this, false),
estade89bc30b2016-02-09 19:08:53225 incognito_theme_provider_(*this, true),
estade68691b282015-12-11 21:50:40226 weak_ptr_factory_(this) {}
[email protected]a0ea76c2011-03-23 17:36:42227
228ThemeService::~ThemeService() {
229 FreePlatformCaches();
230}
231
232void ThemeService::Init(Profile* profile) {
233 DCHECK(CalledOnValidThread());
234 profile_ = profile;
235
236 LoadThemePrefs();
[email protected]280453e2012-09-28 19:09:41237
[email protected]b1ac70462013-08-14 21:33:30238 registrar_.Add(this,
[email protected]adf5a102014-07-31 12:44:06239 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
[email protected]b1ac70462013-08-14 21:33:30240 content::Source<Profile>(profile_));
[email protected]264c60b2013-03-20 01:30:54241
[email protected]280453e2012-09-28 19:09:41242 theme_syncable_service_.reset(new ThemeSyncableService(profile_, this));
[email protected]a0ea76c2011-03-23 17:36:42243}
244
limasdf96f0702b2015-03-13 21:57:09245void ThemeService::Shutdown() {
brettw00899e62016-11-12 02:10:17246#if BUILDFLAG(ENABLE_EXTENSIONS)
limasdf96f0702b2015-03-13 21:57:09247 theme_observer_.reset();
248#endif
249}
250
[email protected]264c60b2013-03-20 01:30:54251void ThemeService::Observe(int type,
252 const content::NotificationSource& source,
253 const content::NotificationDetails& details) {
[email protected]b1ac70462013-08-14 21:33:30254 using content::Details;
255 switch (type) {
[email protected]adf5a102014-07-31 12:44:06256 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED:
257 registrar_.Remove(this,
258 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
259 content::Source<Profile>(profile_));
[email protected]b1ac70462013-08-14 21:33:30260 OnExtensionServiceReady();
261 break;
[email protected]adf5a102014-07-31 12:44:06262 case extensions::NOTIFICATION_EXTENSION_ENABLED: {
[email protected]b1ac70462013-08-14 21:33:30263 const Extension* extension = Details<const Extension>(details).ptr();
264 if (extension->is_theme())
265 SetTheme(extension);
266 break;
267 }
limasdf96f0702b2015-03-13 21:57:09268 default:
269 NOTREACHED();
[email protected]b1ac70462013-08-14 21:33:30270 }
[email protected]264c60b2013-03-20 01:30:54271}
272
[email protected]a0ea76c2011-03-23 17:36:42273void ThemeService::SetTheme(const Extension* extension) {
[email protected]b1ac70462013-08-14 21:33:30274 DCHECK(extension->is_theme());
275 ExtensionService* service =
276 extensions::ExtensionSystem::Get(profile_)->extension_service();
277 if (!service->IsExtensionEnabled(extension->id())) {
278 // |extension| is disabled when reverting to the previous theme via an
279 // infobar.
280 service->EnableExtension(extension->id());
281 // Enabling the extension will call back to SetTheme().
282 return;
283 }
284
285 std::string previous_theme_id = GetThemeID();
286
[email protected]a0ea76c2011-03-23 17:36:42287 // Clear our image cache.
288 FreePlatformCaches();
289
[email protected]a0ea76c2011-03-23 17:36:42290 BuildFromExtension(extension);
[email protected]d5949312012-12-03 22:13:30291 SaveThemeID(extension->id());
[email protected]a0ea76c2011-03-23 17:36:42292
[email protected]a3e61e82011-04-15 20:32:08293 NotifyThemeChanged();
bratell0a7406f2017-03-28 07:46:37294 base::RecordAction(UserMetricsAction("Themes_Installed"));
[email protected]b1ac70462013-08-14 21:33:30295
296 if (previous_theme_id != kDefaultThemeID &&
pkotwicz315d1a312015-09-15 00:05:51297 previous_theme_id != extension->id() &&
298 service->GetInstalledExtension(previous_theme_id)) {
299 // Do not disable the previous theme if it is already uninstalled. Sending
300 // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
301 // uninstalled when the notification causes the remaining infobar to close
302 // and does not open any new infobars. See crbug.com/468280.
303
[email protected]b1ac70462013-08-14 21:33:30304 // Disable the old theme.
305 service->DisableExtension(previous_theme_id,
306 extensions::Extension::DISABLE_USER_ACTION);
307 }
[email protected]a0ea76c2011-03-23 17:36:42308}
309
pkastingf89f1552016-03-03 01:50:50310void ThemeService::UseDefaultTheme() {
311 if (ready_)
bratell0a7406f2017-03-28 07:46:37312 base::RecordAction(UserMetricsAction("Themes_Reset"));
brettw9e85ef42016-11-01 21:01:24313#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
pkastingf89f1552016-03-03 01:50:50314 if (IsSupervisedUser()) {
315 SetSupervisedUserTheme();
316 return;
317 }
318#endif
[email protected]ed0cef02013-07-26 12:41:11319 ClearAllThemeData();
[email protected]ed0cef02013-07-26 12:41:11320 NotifyThemeChanged();
321}
322
pkastingf89f1552016-03-03 01:50:50323void ThemeService::UseSystemTheme() {
324 UseDefaultTheme();
325}
326
327bool ThemeService::IsSystemThemeDistinctFromDefaultTheme() const {
[email protected]ed0cef02013-07-26 12:41:11328 return false;
329}
330
pkastingf89f1552016-03-03 01:50:50331bool ThemeService::UsingDefaultTheme() const {
332 std::string id = GetThemeID();
333 return id == ThemeService::kDefaultThemeID ||
334 id == kDefaultThemeGalleryID;
335}
336
337bool ThemeService::UsingSystemTheme() const {
338 return UsingDefaultTheme();
339}
340
341std::string ThemeService::GetThemeID() const {
342 return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
343}
344
345void ThemeService::OnInfobarDisplayed() {
346 number_of_infobars_++;
347}
348
349void ThemeService::OnInfobarDestroyed() {
350 number_of_infobars_--;
351
352 if (number_of_infobars_ == 0)
353 RemoveUnusedThemes(false);
354}
355
[email protected]b1ac70462013-08-14 21:33:30356void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
[email protected]ed0cef02013-07-26 12:41:11357 // We do not want to garbage collect themes on startup (|ready_| is false).
[email protected]b1ac70462013-08-14 21:33:30358 // Themes will get garbage collected after |kRemoveUnusedThemesStartupDelay|.
[email protected]ed0cef02013-07-26 12:41:11359 if (!profile_ || !ready_)
[email protected]d5949312012-12-03 22:13:30360 return;
[email protected]b1ac70462013-08-14 21:33:30361 if (!ignore_infobars && number_of_infobars_ != 0)
362 return;
[email protected]ed0cef02013-07-26 12:41:11363
[email protected]f47f7172014-03-19 19:27:10364 ExtensionService* service =
365 extensions::ExtensionSystem::Get(profile_)->extension_service();
[email protected]d5949312012-12-03 22:13:30366 if (!service)
367 return;
[email protected]f47f7172014-03-19 19:27:10368
[email protected]d5949312012-12-03 22:13:30369 std::string current_theme = GetThemeID();
370 std::vector<std::string> remove_list;
dcheng4af48582016-04-19 00:29:35371 std::unique_ptr<const extensions::ExtensionSet> extensions(
[email protected]f47f7172014-03-19 19:27:10372 extensions::ExtensionRegistry::Get(profile_)
373 ->GenerateInstalledExtensionsSet());
[email protected]7c82539c2014-02-19 06:09:17374 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
[email protected]289c44b2013-12-17 03:26:57375 for (extensions::ExtensionSet::const_iterator it = extensions->begin();
[email protected]d5949312012-12-03 22:13:30376 it != extensions->end(); ++it) {
dcheng319a21952014-08-26 22:52:40377 const extensions::Extension* extension = it->get();
[email protected]b1ac70462013-08-14 21:33:30378 if (extension->is_theme() &&
379 extension->id() != current_theme) {
380 // Only uninstall themes which are not disabled or are disabled with
381 // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
382 // themes because externally installed themes are initially disabled.
383 int disable_reason = prefs->GetDisableReasons(extension->id());
384 if (!prefs->IsExtensionDisabled(extension->id()) ||
385 disable_reason == Extension::DISABLE_USER_ACTION) {
386 remove_list.push_back((*it)->id());
387 }
[email protected]d5949312012-12-03 22:13:30388 }
389 }
[email protected]b1ac70462013-08-14 21:33:30390 // TODO: Garbage collect all unused themes. This method misses themes which
391 // are installed but not loaded because they are blacklisted by a management
392 // policy provider.
393
[email protected]cc2f55c2014-07-08 02:19:04394 for (size_t i = 0; i < remove_list.size(); ++i) {
[email protected]42d58f62014-07-31 01:32:45395 service->UninstallExtension(remove_list[i],
396 extensions::UNINSTALL_REASON_ORPHANED_THEME,
limasdf96f0702b2015-03-13 21:57:09397 base::Bind(&base::DoNothing), nullptr);
[email protected]cc2f55c2014-07-08 02:19:04398 }
[email protected]d5949312012-12-03 22:13:30399}
400
pkastingf89f1552016-03-03 01:50:50401ThemeSyncableService* ThemeService::GetThemeSyncableService() const {
402 return theme_syncable_service_.get();
403}
404
405// static
406const ui::ThemeProvider& ThemeService::GetThemeProviderForProfile(
407 Profile* profile) {
408 ThemeService* service = ThemeServiceFactory::GetForProfile(profile);
409 bool incognito = profile->GetProfileType() == Profile::INCOGNITO_PROFILE;
410 return incognito ? service->incognito_theme_provider_
411 : service->original_theme_provider_;
412}
413
414void ThemeService::SetCustomDefaultTheme(
415 scoped_refptr<CustomThemeSupplier> theme_supplier) {
[email protected]94d410e2013-08-07 21:51:30416 ClearAllThemeData();
pkastingf89f1552016-03-03 01:50:50417 SwapThemeSupplier(theme_supplier);
[email protected]94d410e2013-08-07 21:51:30418 NotifyThemeChanged();
[email protected]a0ea76c2011-03-23 17:36:42419}
420
pkastingf89f1552016-03-03 01:50:50421bool ThemeService::ShouldInitWithSystemTheme() const {
422 return false;
[email protected]a0ea76c2011-03-23 17:36:42423}
424
pkasting20d38210d2016-03-08 09:45:54425SkColor ThemeService::GetDefaultColor(int id, bool incognito) const {
426 // For backward compat with older themes, some newer colors are generated from
427 // older ones if they are missing.
428 const int kNtpText = ThemeProperties::COLOR_NTP_TEXT;
429 const int kLabelBackground =
430 ThemeProperties::COLOR_SUPERVISED_USER_LABEL_BACKGROUND;
431 switch (id) {
432 case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON:
433 return color_utils::HSLShift(
434 gfx::kChromeIconGrey,
435 GetTint(ThemeProperties::TINT_BUTTONS, incognito));
436 case ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON_INACTIVE:
thomasandersonb1f49892017-01-05 02:16:42437 // The active color is overridden in GtkUi.
pkasting20d38210d2016-03-08 09:45:54438 return SkColorSetA(
439 GetColor(ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON, incognito),
440 0x33);
thomasanderson307f9f292017-02-09 06:33:15441 case ThemeProperties::COLOR_LOCATION_BAR_BORDER:
442 return SkColorSetA(SK_ColorBLACK, 0x4D);
pkastinge330ac62016-03-19 06:07:03443 case ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR:
444 case ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR_INACTIVE: {
pkasting3eb05e62016-03-17 01:01:02445 const SkColor tab_color =
446 GetColor(ThemeProperties::COLOR_TOOLBAR, incognito);
pkastinge330ac62016-03-19 06:07:03447 const int frame_id = (id == ThemeProperties::COLOR_TOOLBAR_TOP_SEPARATOR)
448 ? ThemeProperties::COLOR_FRAME
449 : ThemeProperties::COLOR_FRAME_INACTIVE;
450 const SkColor frame_color = GetColor(frame_id, incognito);
pkasting3eb05e62016-03-17 01:01:02451 const SeparatorColorKey key(tab_color, frame_color);
452 auto i = separator_color_cache_.find(key);
453 if (i != separator_color_cache_.end())
454 return i->second;
455 const SkColor separator_color = GetSeparatorColor(tab_color, frame_color);
456 separator_color_cache_[key] = separator_color;
457 return separator_color;
458 }
estade15814a72016-05-20 00:42:46459 case ThemeProperties::COLOR_TOOLBAR_VERTICAL_SEPARATOR: {
460 return SkColorSetA(
461 GetColor(ThemeProperties::COLOR_TOOLBAR_BUTTON_ICON, incognito),
462 0x4D);
463 }
pkasting20d38210d2016-03-08 09:45:54464 case ThemeProperties::COLOR_BACKGROUND_TAB: {
465 // The tints here serve a different purpose than TINT_BACKGROUND_TAB.
466 // That tint is used to create background tab images for custom themes by
467 // lightening the frame images. The tints here create solid colors for
pkasting3eb05e62016-03-17 01:01:02468 // background tabs by darkening the foreground tab (toolbar) color. These
469 // values are chosen to turn the default normal and incognito MD frame
470 // colors (0xf2f2f2 and 0x505050) into 0xd0d0d0 and 0x373737,
471 // respectively.
472 const color_utils::HSL kTint = {-1, -1, 0.42975};
pkasting20d38210d2016-03-08 09:45:54473 const color_utils::HSL kTintIncognito = {-1, -1, 0.34375};
474 return color_utils::HSLShift(
475 GetColor(ThemeProperties::COLOR_TOOLBAR, incognito),
476 incognito ? kTintIncognito : kTint);
477 }
kylixrd6b6a68d2016-06-20 14:05:20478 case ThemeProperties::COLOR_BOOKMARK_BAR_INSTRUCTIONS_TEXT:
479 if (UsingDefaultTheme())
480 break;
481 return GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT, incognito);
estade88c375832016-07-18 22:25:50482 case ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_BACKGROUND:
483 if (UsingDefaultTheme())
484 break;
485 return GetColor(ThemeProperties::COLOR_TOOLBAR, incognito);
pkasting20d38210d2016-03-08 09:45:54486 case ThemeProperties::COLOR_DETACHED_BOOKMARK_BAR_SEPARATOR:
487 if (UsingDefaultTheme())
488 break;
489 // Use 50% of bookmark text color as separator color.
490 return SkColorSetA(
491 GetColor(ThemeProperties::COLOR_BOOKMARK_TEXT, incognito), 128);
pkasting20d38210d2016-03-08 09:45:54492 case ThemeProperties::COLOR_NTP_TEXT_LIGHT:
493 return IncreaseLightness(GetColor(kNtpText, incognito), 0.40);
494 case ThemeProperties::COLOR_TAB_THROBBER_SPINNING:
495 case ThemeProperties::COLOR_TAB_THROBBER_WAITING: {
496 SkColor base_color =
497 ui::GetAuraColor(id == ThemeProperties::COLOR_TAB_THROBBER_SPINNING
498 ? ui::NativeTheme::kColorId_ThrobberSpinningColor
499 : ui::NativeTheme::kColorId_ThrobberWaitingColor,
500 nullptr);
501 color_utils::HSL hsl = GetTint(ThemeProperties::TINT_BUTTONS, incognito);
502 return color_utils::HSLShift(base_color, hsl);
503 }
brettw9e85ef42016-11-01 21:01:24504#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
pkasting20d38210d2016-03-08 09:45:54505 case ThemeProperties::COLOR_SUPERVISED_USER_LABEL:
506 return color_utils::GetReadableColor(
507 SK_ColorWHITE, GetColor(kLabelBackground, incognito));
508 case ThemeProperties::COLOR_SUPERVISED_USER_LABEL_BACKGROUND:
509 return color_utils::BlendTowardOppositeLuma(
510 GetColor(ThemeProperties::COLOR_FRAME, incognito), 0x80);
511 case ThemeProperties::COLOR_SUPERVISED_USER_LABEL_BORDER:
512 return color_utils::AlphaBlend(GetColor(kLabelBackground, incognito),
513 SK_ColorBLACK, 230);
514#endif
515 }
516
estade66736a72016-03-30 01:26:23517 // Always fall back to the non-incognito color when there's a custom theme
518 // because the default (classic) incognito color may be dramatically different
519 // (optimized for a light-on-dark color).
520 return ThemeProperties::GetDefaultColor(id, incognito && !theme_supplier_);
pkasting20d38210d2016-03-08 09:45:54521}
522
estade89bc30b2016-02-09 19:08:53523color_utils::HSL ThemeService::GetTint(int id, bool incognito) const {
[email protected]a0ea76c2011-03-23 17:36:42524 DCHECK(CalledOnValidThread());
525
526 color_utils::HSL hsl;
estade68691b282015-12-11 21:50:40527 if (theme_supplier_ && theme_supplier_->GetTint(id, &hsl))
[email protected]a0ea76c2011-03-23 17:36:42528 return hsl;
529
estade66736a72016-03-30 01:26:23530 // Always fall back to the non-incognito tint when there's a custom theme.
531 // See comment in GetDefaultColor().
532 return ThemeProperties::GetDefaultTint(id, incognito && !theme_supplier_);
[email protected]a0ea76c2011-03-23 17:36:42533}
534
535void ThemeService::ClearAllThemeData() {
[email protected]94d410e2013-08-07 21:51:30536 if (!ready_)
537 return;
538
limasdf96f0702b2015-03-13 21:57:09539 SwapThemeSupplier(nullptr);
[email protected]ed0cef02013-07-26 12:41:11540
[email protected]a0ea76c2011-03-23 17:36:42541 // Clear our image cache.
542 FreePlatformCaches();
[email protected]a0ea76c2011-03-23 17:36:42543
544 profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
[email protected]d5949312012-12-03 22:13:30545 SaveThemeID(kDefaultThemeID);
[email protected]8b1058bb2013-04-08 20:15:27546
[email protected]b1ac70462013-08-14 21:33:30547 // There should be no more infobars. This may not be the case because of
548 // http://crbug.com/62154
549 // RemoveUnusedThemes is called on a task because ClearAllThemeData() may
limasdf53f32be12017-04-25 12:13:27550 // be called as a result of OnExtensionUnloaded().
skyostil02598352015-06-12 12:37:25551 base::ThreadTaskRunnerHandle::Get()->PostTask(
552 FROM_HERE, base::Bind(&ThemeService::RemoveUnusedThemes,
553 weak_ptr_factory_.GetWeakPtr(), true));
[email protected]a0ea76c2011-03-23 17:36:42554}
555
556void ThemeService::LoadThemePrefs() {
557 PrefService* prefs = profile_->GetPrefs();
558
559 std::string current_id = GetThemeID();
[email protected]264c60b2013-03-20 01:30:54560 if (current_id == kDefaultThemeID) {
brettw9e85ef42016-11-01 21:01:24561#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
[email protected]d20d0432014-06-12 17:14:05562 // Supervised users have a different default theme.
mckeva1a18dc2014-09-29 18:52:33563 if (IsSupervisedUser()) {
[email protected]d20d0432014-06-12 17:14:05564 SetSupervisedUserTheme();
mckeva1a18dc2014-09-29 18:52:33565 set_ready();
566 return;
567 }
568#endif
569 if (ShouldInitWithSystemTheme())
[email protected]448d7dc2014-05-13 03:22:55570 UseSystemTheme();
[email protected]ed0cef02013-07-26 12:41:11571 else
572 UseDefaultTheme();
[email protected]264c60b2013-03-20 01:30:54573 set_ready();
574 return;
575 }
[email protected]a0ea76c2011-03-23 17:36:42576
[email protected]264c60b2013-03-20 01:30:54577 bool loaded_pack = false;
[email protected]a0ea76c2011-03-23 17:36:42578
estadeb8867dff2016-11-03 17:33:33579 // If we don't have a file pack, we're updating from an old version.
[email protected]264c60b2013-03-20 01:30:54580 base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
581 if (path != base::FilePath()) {
estadeb8867dff2016-11-03 17:33:33582 path = path.Append(chrome::kThemePackFilename);
[email protected]ed0cef02013-07-26 12:41:11583 SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
scheib4dac7f02016-05-12 00:55:03584 if (theme_supplier_)
585 loaded_pack = true;
[email protected]264c60b2013-03-20 01:30:54586 }
587
588 if (loaded_pack) {
bratell0a7406f2017-03-28 07:46:37589 base::RecordAction(UserMetricsAction("Themes.Loaded"));
[email protected]264c60b2013-03-20 01:30:54590 set_ready();
[email protected]a0ea76c2011-03-23 17:36:42591 }
[email protected]b1ac70462013-08-14 21:33:30592 // Else: wait for the extension service to be ready so that the theme pack
593 // can be recreated from the extension.
[email protected]a0ea76c2011-03-23 17:36:42594}
595
[email protected]a3e61e82011-04-15 20:32:08596void ThemeService::NotifyThemeChanged() {
[email protected]ed0cef02013-07-26 12:41:11597 if (!ready_)
598 return;
599
[email protected]8e88ca12011-11-04 20:45:39600 DVLOG(1) << "Sending BROWSER_THEME_CHANGED";
[email protected]a0ea76c2011-03-23 17:36:42601 // Redraw!
[email protected]ad50def52011-10-19 23:17:07602 content::NotificationService* service =
603 content::NotificationService::current();
[email protected]432115822011-07-10 15:52:27604 service->Notify(chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53605 content::Source<ThemeService>(this),
[email protected]ad50def52011-10-19 23:17:07606 content::NotificationService::NoDetails());
[email protected]a0ea76c2011-03-23 17:36:42607#if defined(OS_MACOSX)
608 NotifyPlatformThemeChanged();
609#endif // OS_MACOSX
[email protected]280453e2012-09-28 19:09:41610
611 // Notify sync that theme has changed.
612 if (theme_syncable_service_.get()) {
613 theme_syncable_service_->OnThemeChange();
614 }
[email protected]a0ea76c2011-03-23 17:36:42615}
616
[email protected]f9db7d2d2014-04-11 16:07:11617#if defined(USE_AURA)
[email protected]a0ea76c2011-03-23 17:36:42618void ThemeService::FreePlatformCaches() {
619 // Views (Skia) has no platform image cache to clear.
620}
621#endif
622
pkasting20d38210d2016-03-08 09:45:54623bool ThemeService::ShouldUseNativeFrame() const {
624 return false;
625}
626
627bool ThemeService::HasCustomImage(int id) const {
628 return BrowserThemePack::IsPersistentImageID(id) && theme_supplier_ &&
629 theme_supplier_->HasCustomImage(id);
630}
631
pkasting3eb05e62016-03-17 01:01:02632// static
633SkColor ThemeService::GetSeparatorColor(SkColor tab_color,
634 SkColor frame_color) {
635 // We use this alpha value for the separator if possible.
636 const SkAlpha kAlpha = 0x40;
637
638 // In most cases, if the tab is lighter than the frame, we darken the
639 // frame; if the tab is darker than the frame, we lighten the frame.
640 // However, if the frame is already very dark or very light, respectively,
641 // this won't contrast sufficiently with the frame color, so we'll need to
642 // reverse when we're lightening and darkening.
643 const double tab_luminance = color_utils::GetRelativeLuminance(tab_color);
644 const double frame_luminance = color_utils::GetRelativeLuminance(frame_color);
645 const bool lighten = tab_luminance < frame_luminance;
646 SkColor separator_color = lighten ? SK_ColorWHITE : SK_ColorBLACK;
647 double separator_luminance = color_utils::GetRelativeLuminance(
648 color_utils::AlphaBlend(separator_color, frame_color, kAlpha));
649 // The minimum contrast ratio here is just under the ~1.1469 in the default MD
650 // incognito theme. We want the separator to still darken the frame in that
651 // theme, but that's about as low of contrast as we're willing to accept.
652 const double kMinContrastRatio = 1.1465;
653 if (color_utils::GetContrastRatio(separator_luminance, frame_luminance) >=
654 kMinContrastRatio)
655 return SkColorSetA(separator_color, kAlpha);
656
657 // We need to reverse whether we're darkening or lightening. We know the new
658 // separator color will contrast with the frame; check whether it also
659 // contrasts at least as well with the tab.
660 separator_color = color_utils::InvertColor(separator_color);
661 separator_luminance = color_utils::GetRelativeLuminance(
662 color_utils::AlphaBlend(separator_color, frame_color, kAlpha));
663 if (color_utils::GetContrastRatio(separator_luminance, tab_luminance) >=
664 color_utils::GetContrastRatio(separator_luminance, frame_luminance))
665 return SkColorSetA(separator_color, kAlpha);
666
667 // The reversed separator doesn't contrast enough with the tab. Compute the
668 // resulting luminance from adjusting the tab color, instead of the frame
669 // color, by the separator color.
670 const double target_luminance = color_utils::GetRelativeLuminance(
671 color_utils::AlphaBlend(separator_color, tab_color, kAlpha));
672
673 // Now try to compute an alpha for the separator such that, when blended with
674 // the frame, it results in the above luminance. Because the luminance
675 // computation is not easily invertible, we use a binary search over the
676 // possible range of alpha values.
677 SkAlpha alpha = 128;
678 for (int delta = lighten ? 64 : -64; delta != 0; delta /= 2) {
679 const double luminance = color_utils::GetRelativeLuminance(
680 color_utils::AlphaBlend(separator_color, frame_color, alpha));
681 if (luminance == target_luminance)
682 break;
683 alpha += (luminance < target_luminance) ? -delta : delta;
684 }
685 return SkColorSetA(separator_color, alpha);
686}
687
estade89bc30b2016-02-09 19:08:53688gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id, bool incognito) const {
689 gfx::Image image = GetImageNamed(id, incognito);
estade68691b282015-12-11 21:50:40690 if (image.IsEmpty())
691 return nullptr;
692 // TODO(pkotwicz): Remove this const cast. The gfx::Image interface returns
693 // its images const. GetImageSkiaNamed() also should but has many callsites.
694 return const_cast<gfx::ImageSkia*>(image.ToImageSkia());
695}
696
estade89bc30b2016-02-09 19:08:53697SkColor ThemeService::GetColor(int id, bool incognito) const {
estade68691b282015-12-11 21:50:40698 DCHECK(CalledOnValidThread());
estade42d476e2015-12-18 00:28:11699
700 // For legacy reasons, |theme_supplier_| requires the incognito variants
701 // of color IDs.
702 int theme_supplier_id = id;
estade89bc30b2016-02-09 19:08:53703 if (incognito) {
pkasting0918e572016-01-15 22:15:47704 if (id == ThemeProperties::COLOR_FRAME)
705 theme_supplier_id = ThemeProperties::COLOR_FRAME_INCOGNITO;
706 else if (id == ThemeProperties::COLOR_FRAME_INACTIVE)
707 theme_supplier_id = ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE;
estade42d476e2015-12-18 00:28:11708 }
709
estade68691b282015-12-11 21:50:40710 SkColor color;
estade42d476e2015-12-18 00:28:11711 if (theme_supplier_ && theme_supplier_->GetColor(theme_supplier_id, &color))
estade68691b282015-12-11 21:50:40712 return color;
713
pkasting20d38210d2016-03-08 09:45:54714 return GetDefaultColor(id, incognito);
estade68691b282015-12-11 21:50:40715}
716
717int ThemeService::GetDisplayProperty(int id) const {
718 int result = 0;
719 if (theme_supplier_ && theme_supplier_->GetDisplayProperty(id, &result)) {
720 return result;
721 }
722
723 switch (id) {
pkasting0918e572016-01-15 22:15:47724 case ThemeProperties::NTP_BACKGROUND_ALIGNMENT:
725 return ThemeProperties::ALIGN_CENTER;
estade68691b282015-12-11 21:50:40726
pkasting0918e572016-01-15 22:15:47727 case ThemeProperties::NTP_BACKGROUND_TILING:
728 return ThemeProperties::NO_REPEAT;
estade68691b282015-12-11 21:50:40729
pkasting0918e572016-01-15 22:15:47730 case ThemeProperties::NTP_LOGO_ALTERNATE: {
731 if (UsingDefaultTheme() || UsingSystemTheme())
732 return 0;
733 if (HasCustomImage(IDR_THEME_NTP_BACKGROUND))
734 return 1;
735 return IsColorGrayscale(
736 GetColor(ThemeProperties::COLOR_NTP_BACKGROUND, false)) ? 0 : 1;
737 }
estade68691b282015-12-11 21:50:40738
739 default:
740 return -1;
741 }
742}
743
estade68691b282015-12-11 21:50:40744base::RefCountedMemory* ThemeService::GetRawData(
745 int id,
746 ui::ScaleFactor scale_factor) const {
747 // Check to see whether we should substitute some images.
pkasting0918e572016-01-15 22:15:47748 int ntp_alternate = GetDisplayProperty(ThemeProperties::NTP_LOGO_ALTERNATE);
estade68691b282015-12-11 21:50:40749 if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
750 id = IDR_PRODUCT_LOGO_WHITE;
751
752 base::RefCountedMemory* data = nullptr;
753 if (theme_supplier_)
754 data = theme_supplier_->GetRawData(id, scale_factor);
755 if (!data)
756 data = rb_.LoadDataResourceBytesForScale(id, ui::SCALE_FACTOR_100P);
757
758 return data;
759}
760
estade89bc30b2016-02-09 19:08:53761gfx::Image ThemeService::GetImageNamed(int id, bool incognito) const {
estade68691b282015-12-11 21:50:40762 DCHECK(CalledOnValidThread());
763
estade89bc30b2016-02-09 19:08:53764 int adjusted_id = id;
765 if (incognito) {
766 if (id == IDR_THEME_FRAME)
767 adjusted_id = IDR_THEME_FRAME_INCOGNITO;
768 else if (id == IDR_THEME_FRAME_INACTIVE)
769 adjusted_id = IDR_THEME_FRAME_INCOGNITO_INACTIVE;
770 }
771
estade68691b282015-12-11 21:50:40772 gfx::Image image;
773 if (theme_supplier_)
estade89bc30b2016-02-09 19:08:53774 image = theme_supplier_->GetImageNamed(adjusted_id);
estade68691b282015-12-11 21:50:40775
776 if (image.IsEmpty())
estade89bc30b2016-02-09 19:08:53777 image = rb_.GetNativeImageNamed(adjusted_id);
estade68691b282015-12-11 21:50:40778
779 return image;
780}
781
[email protected]b1ac70462013-08-14 21:33:30782void ThemeService::OnExtensionServiceReady() {
783 if (!ready_) {
784 // If the ThemeService is not ready yet, the custom theme data pack needs to
785 // be recreated from the extension.
786 MigrateTheme();
787 set_ready();
788
789 // Send notification in case anyone requested data and cached it when the
790 // theme service was not ready yet.
791 NotifyThemeChanged();
792 }
793
brettw00899e62016-11-12 02:10:17794#if BUILDFLAG(ENABLE_EXTENSIONS)
limasdf96f0702b2015-03-13 21:57:09795 theme_observer_.reset(new ThemeObserver(this));
796#endif
797
[email protected]b1ac70462013-08-14 21:33:30798 registrar_.Add(this,
[email protected]adf5a102014-07-31 12:44:06799 extensions::NOTIFICATION_EXTENSION_ENABLED,
[email protected]b1ac70462013-08-14 21:33:30800 content::Source<Profile>(profile_));
[email protected]b1ac70462013-08-14 21:33:30801
skyostil02598352015-06-12 12:37:25802 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
803 FROM_HERE, base::Bind(&ThemeService::RemoveUnusedThemes,
804 weak_ptr_factory_.GetWeakPtr(), false),
[email protected]b1ac70462013-08-14 21:33:30805 base::TimeDelta::FromSeconds(kRemoveUnusedThemesStartupDelay));
[email protected]ed0cef02013-07-26 12:41:11806}
807
[email protected]264c60b2013-03-20 01:30:54808void ThemeService::MigrateTheme() {
[email protected]b1ac70462013-08-14 21:33:30809 // TODO(erg): We need to pop up a dialog informing the user that their
810 // theme is being migrated.
[email protected]264c60b2013-03-20 01:30:54811 ExtensionService* service =
812 extensions::ExtensionSystem::Get(profile_)->extension_service();
limasdf96f0702b2015-03-13 21:57:09813 const Extension* extension =
814 service ? service->GetExtensionById(GetThemeID(), false) : nullptr;
[email protected]264c60b2013-03-20 01:30:54815 if (extension) {
816 DLOG(ERROR) << "Migrating theme";
817 BuildFromExtension(extension);
bratell0a7406f2017-03-28 07:46:37818 base::RecordAction(UserMetricsAction("Themes.Migrated"));
[email protected]264c60b2013-03-20 01:30:54819 } else {
820 DLOG(ERROR) << "Theme is mysteriously gone.";
821 ClearAllThemeData();
bratell0a7406f2017-03-28 07:46:37822 base::RecordAction(UserMetricsAction("Themes.Gone"));
[email protected]264c60b2013-03-20 01:30:54823 }
824}
825
[email protected]b1ac70462013-08-14 21:33:30826void ThemeService::SwapThemeSupplier(
827 scoped_refptr<CustomThemeSupplier> theme_supplier) {
estade68691b282015-12-11 21:50:40828 if (theme_supplier_)
[email protected]b1ac70462013-08-14 21:33:30829 theme_supplier_->StopUsingTheme();
830 theme_supplier_ = theme_supplier;
estade68691b282015-12-11 21:50:40831 if (theme_supplier_)
[email protected]b1ac70462013-08-14 21:33:30832 theme_supplier_->StartUsingTheme();
833}
834
[email protected]650b2d52013-02-10 03:41:45835void ThemeService::SavePackName(const base::FilePath& pack_path) {
[email protected]a0ea76c2011-03-23 17:36:42836 profile_->GetPrefs()->SetFilePath(
837 prefs::kCurrentThemePackFilename, pack_path);
838}
839
[email protected]d5949312012-12-03 22:13:30840void ThemeService::SaveThemeID(const std::string& id) {
841 profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
842}
843
[email protected]a0ea76c2011-03-23 17:36:42844void ThemeService::BuildFromExtension(const Extension* extension) {
845 scoped_refptr<BrowserThemePack> pack(
846 BrowserThemePack::BuildFromExtension(extension));
847 if (!pack.get()) {
848 // TODO(erg): We've failed to install the theme; perhaps we should tell the
849 // user? http://crbug.com/34780
850 LOG(ERROR) << "Could not load theme.";
851 return;
852 }
853
[email protected]7f8f24f2012-11-15 19:40:14854 ExtensionService* service =
855 extensions::ExtensionSystem::Get(profile_)->extension_service();
856 if (!service)
857 return;
858
[email protected]a0ea76c2011-03-23 17:36:42859 // Write the packed file to disk.
[email protected]7f8f24f2012-11-15 19:40:14860 service->GetFileTaskRunner()->PostTask(
estadeb8867dff2016-11-03 17:33:33861 FROM_HERE, base::Bind(&WritePackToDiskCallback, base::RetainedRef(pack),
862 extension->path()));
[email protected]a0ea76c2011-03-23 17:36:42863
estadeb8867dff2016-11-03 17:33:33864 // Save only the extension path. The packed file will be loaded via
865 // LoadThemePrefs().
jonrossc38cce9b2015-09-24 15:42:22866 SavePackName(extension->path());
[email protected]ed0cef02013-07-26 12:41:11867 SwapThemeSupplier(pack);
[email protected]a0ea76c2011-03-23 17:36:42868}
869
brettw9e85ef42016-11-01 21:01:24870#if BUILDFLAG(ENABLE_SUPERVISED_USERS)
[email protected]d20d0432014-06-12 17:14:05871bool ThemeService::IsSupervisedUser() const {
872 return profile_->IsSupervised();
[email protected]768c0ff2013-06-21 02:50:55873}
874
[email protected]d20d0432014-06-12 17:14:05875void ThemeService::SetSupervisedUserTheme() {
[email protected]cce15bb2014-06-17 13:43:51876 SetCustomDefaultTheme(new SupervisedUserTheme);
[email protected]ed0cef02013-07-26 12:41:11877}
mckeva1a18dc2014-09-29 18:52:33878#endif