Refactor WebsiteSettings to operate on a SecurityInfo
[chromium-blink-merge.git] / chrome / browser / themes / theme_service.cc
blob59e32eff292a789b7081f93be8961b454630b0cf
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.
5 #include "chrome/browser/themes/theme_service.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/ref_counted_memory.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/extension_service.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/themes/browser_theme_pack.h"
22 #include "chrome/browser/themes/custom_theme_supplier.h"
23 #include "chrome/browser/themes/theme_properties.h"
24 #include "chrome/browser/themes/theme_syncable_service.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/pref_names.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/user_metrics.h"
29 #include "extensions/browser/extension_prefs.h"
30 #include "extensions/browser/extension_registry.h"
31 #include "extensions/browser/extension_system.h"
32 #include "extensions/browser/uninstall_reason.h"
33 #include "extensions/common/extension.h"
34 #include "extensions/common/extension_set.h"
35 #include "grit/theme_resources.h"
36 #include "ui/base/layout.h"
37 #include "ui/base/resource/resource_bundle.h"
38 #include "ui/gfx/image/image_skia.h"
39 #include "ui/native_theme/common_theme.h"
40 #include "ui/native_theme/native_theme.h"
42 #if defined(ENABLE_EXTENSIONS)
43 #include "extensions/browser/extension_registry_observer.h"
44 #endif
46 #if defined(ENABLE_SUPERVISED_USERS)
47 #include "chrome/browser/supervised_user/supervised_user_theme.h"
48 #endif
50 #if defined(OS_WIN)
51 #include "ui/base/win/shell.h"
52 #endif
54 using base::UserMetricsAction;
55 using content::BrowserThread;
56 using extensions::Extension;
57 using extensions::UnloadedExtensionInfo;
58 using ui::ResourceBundle;
60 typedef ThemeProperties Properties;
62 // The default theme if we haven't installed a theme yet or if we've clicked
63 // the "Use Classic" button.
64 const char* ThemeService::kDefaultThemeID = "";
66 namespace {
68 // The default theme if we've gone to the theme gallery and installed the
69 // "Default" theme. We have to detect this case specifically. (By the time we
70 // realize we've installed the default theme, we already have an extension
71 // unpacked on the filesystem.)
72 const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn";
74 // Wait this many seconds after startup to garbage collect unused themes.
75 // Removing unused themes is done after a delay because there is no
76 // reason to do it at startup.
77 // ExtensionService::GarbageCollectExtensions() does something similar.
78 const int kRemoveUnusedThemesStartupDelay = 30;
80 SkColor IncreaseLightness(SkColor color, double percent) {
81 color_utils::HSL result;
82 color_utils::SkColorToHSL(color, &result);
83 result.l += (1 - result.l) * percent;
84 return color_utils::HSLToSkColor(result, SkColorGetA(color));
87 // Writes the theme pack to disk on a separate thread.
88 void WritePackToDiskCallback(BrowserThemePack* pack,
89 const base::FilePath& path) {
90 if (!pack->WriteToDisk(path))
91 NOTREACHED() << "Could not write theme pack to disk";
94 // Heuristic to determine if color is grayscale. This is used to decide whether
95 // to use the colorful or white logo, if a theme fails to specify which.
96 bool IsColorGrayscale(SkColor color) {
97 const int kChannelTolerance = 9;
98 int r = SkColorGetR(color);
99 int g = SkColorGetG(color);
100 int b = SkColorGetB(color);
101 int range = std::max(r, std::max(g, b)) - std::min(r, std::min(g, b));
102 return range < kChannelTolerance;
105 } // namespace
107 #if defined(ENABLE_EXTENSIONS)
108 class ThemeService::ThemeObserver
109 : public extensions::ExtensionRegistryObserver {
110 public:
111 explicit ThemeObserver(ThemeService* service) : theme_service_(service) {
112 extensions::ExtensionRegistry::Get(theme_service_->profile_)
113 ->AddObserver(this);
116 ~ThemeObserver() override {
117 extensions::ExtensionRegistry::Get(theme_service_->profile_)
118 ->RemoveObserver(this);
121 private:
122 void OnExtensionWillBeInstalled(content::BrowserContext* browser_context,
123 const extensions::Extension* extension,
124 bool is_update,
125 bool from_ephemeral,
126 const std::string& old_name) override {
127 if (extension->is_theme()) {
128 // The theme may be initially disabled. Wait till it is loaded (if ever).
129 theme_service_->installed_pending_load_id_ = extension->id();
133 void OnExtensionLoaded(content::BrowserContext* browser_context,
134 const extensions::Extension* extension) override {
135 if (extension->is_theme() &&
136 theme_service_->installed_pending_load_id_ != kDefaultThemeID &&
137 theme_service_->installed_pending_load_id_ == extension->id()) {
138 theme_service_->SetTheme(extension);
140 theme_service_->installed_pending_load_id_ = kDefaultThemeID;
143 void OnExtensionUnloaded(
144 content::BrowserContext* browser_context,
145 const extensions::Extension* extension,
146 extensions::UnloadedExtensionInfo::Reason reason) override {
147 if (reason != extensions::UnloadedExtensionInfo::REASON_UPDATE &&
148 reason != extensions::UnloadedExtensionInfo::REASON_LOCK_ALL &&
149 extension->is_theme() &&
150 extension->id() == theme_service_->GetThemeID()) {
151 theme_service_->UseDefaultTheme();
155 ThemeService* theme_service_;
157 #endif // defined(ENABLE_EXTENSIONS)
159 ThemeService::ThemeService()
160 : ready_(false),
161 rb_(ResourceBundle::GetSharedInstance()),
162 profile_(nullptr),
163 installed_pending_load_id_(kDefaultThemeID),
164 number_of_infobars_(0),
165 weak_ptr_factory_(this) {
168 ThemeService::~ThemeService() {
169 FreePlatformCaches();
172 void ThemeService::Init(Profile* profile) {
173 DCHECK(CalledOnValidThread());
174 profile_ = profile;
176 LoadThemePrefs();
178 registrar_.Add(this,
179 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
180 content::Source<Profile>(profile_));
182 theme_syncable_service_.reset(new ThemeSyncableService(profile_, this));
185 gfx::Image ThemeService::GetImageNamed(int id) const {
186 DCHECK(CalledOnValidThread());
188 gfx::Image image;
189 if (theme_supplier_.get())
190 image = theme_supplier_->GetImageNamed(id);
192 if (image.IsEmpty())
193 image = rb_.GetNativeImageNamed(id);
195 return image;
198 bool ThemeService::IsSystemThemeDistinctFromDefaultTheme() const {
199 return false;
202 bool ThemeService::UsingSystemTheme() const {
203 return UsingDefaultTheme();
206 gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id) const {
207 gfx::Image image = GetImageNamed(id);
208 if (image.IsEmpty())
209 return nullptr;
210 // TODO(pkotwicz): Remove this const cast. The gfx::Image interface returns
211 // its images const. GetImageSkiaNamed() also should but has many callsites.
212 return const_cast<gfx::ImageSkia*>(image.ToImageSkia());
215 SkColor ThemeService::GetColor(int id) const {
216 DCHECK(CalledOnValidThread());
217 SkColor color;
218 if (theme_supplier_.get() && theme_supplier_->GetColor(id, &color))
219 return color;
221 // For backward compat with older themes, some newer colors are generated from
222 // older ones if they are missing.
223 switch (id) {
224 case Properties::COLOR_NTP_SECTION_HEADER_TEXT:
225 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.30);
226 case Properties::COLOR_NTP_SECTION_HEADER_TEXT_HOVER:
227 return GetColor(Properties::COLOR_NTP_TEXT);
228 case Properties::COLOR_NTP_SECTION_HEADER_RULE:
229 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.70);
230 case Properties::COLOR_NTP_SECTION_HEADER_RULE_LIGHT:
231 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.86);
232 case Properties::COLOR_NTP_TEXT_LIGHT:
233 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.40);
234 case Properties::COLOR_TAB_ICON: {
235 SkColor base_color;
236 bool found_color = ui::CommonThemeGetSystemColor(
237 ui::NativeTheme::kColorId_ChromeIconGrey, &base_color);
238 DCHECK(found_color);
239 color_utils::HSL hsl = GetTint(Properties::TINT_BUTTONS);
240 return color_utils::HSLShift(base_color, hsl);
242 case Properties::COLOR_THROBBER_SPINNING:
243 case Properties::COLOR_THROBBER_WAITING: {
244 SkColor base_color;
245 bool found_color = ui::CommonThemeGetSystemColor(
246 id == Properties::COLOR_THROBBER_SPINNING
247 ? ui::NativeTheme::kColorId_ThrobberSpinningColor
248 : ui::NativeTheme::kColorId_ThrobberWaitingColor,
249 &base_color);
250 DCHECK(found_color);
251 color_utils::HSL hsl = GetTint(Properties::TINT_BUTTONS);
252 return color_utils::HSLShift(base_color, hsl);
254 #if defined(ENABLE_SUPERVISED_USERS)
255 case Properties::COLOR_SUPERVISED_USER_LABEL:
256 return color_utils::GetReadableColor(
257 SK_ColorWHITE,
258 GetColor(Properties::COLOR_SUPERVISED_USER_LABEL_BACKGROUND));
259 case Properties::COLOR_SUPERVISED_USER_LABEL_BACKGROUND:
260 return color_utils::BlendTowardOppositeLuminance(
261 GetColor(Properties::COLOR_FRAME), 0x80);
262 case Properties::COLOR_SUPERVISED_USER_LABEL_BORDER:
263 return color_utils::AlphaBlend(
264 GetColor(Properties::COLOR_SUPERVISED_USER_LABEL_BACKGROUND),
265 SK_ColorBLACK,
266 230);
267 #endif
268 case Properties::COLOR_STATUS_BAR_TEXT: {
269 // A long time ago, we blended the toolbar and the tab text together to
270 // get the status bar text because, at the time, our text rendering in
271 // views couldn't do alpha blending. Even though this is no longer the
272 // case, this blending decision is built into the majority of themes that
273 // exist, and we must keep doing it.
274 SkColor toolbar_color = GetColor(Properties::COLOR_TOOLBAR);
275 SkColor text_color = GetColor(Properties::COLOR_TAB_TEXT);
276 return SkColorSetARGB(
277 SkColorGetA(text_color),
278 (SkColorGetR(text_color) + SkColorGetR(toolbar_color)) / 2,
279 (SkColorGetG(text_color) + SkColorGetR(toolbar_color)) / 2,
280 (SkColorGetB(text_color) + SkColorGetR(toolbar_color)) / 2);
284 return Properties::GetDefaultColor(id);
287 int ThemeService::GetDisplayProperty(int id) const {
288 int result = 0;
289 if (theme_supplier_.get() &&
290 theme_supplier_->GetDisplayProperty(id, &result)) {
291 return result;
294 switch (id) {
295 case Properties::NTP_BACKGROUND_ALIGNMENT:
296 return Properties::ALIGN_CENTER;
298 case Properties::NTP_BACKGROUND_TILING:
299 return Properties::NO_REPEAT;
301 case Properties::NTP_LOGO_ALTERNATE:
302 return UsingDefaultTheme() || UsingSystemTheme() ||
303 (!HasCustomImage(IDR_THEME_NTP_BACKGROUND) &&
304 IsColorGrayscale(GetColor(Properties::COLOR_NTP_BACKGROUND))) ?
305 0 : 1;
307 default:
308 return -1;
312 bool ThemeService::ShouldUseNativeFrame() const {
313 if (HasCustomImage(IDR_THEME_FRAME))
314 return false;
315 #if defined(OS_WIN)
316 return ui::win::IsAeroGlassEnabled();
317 #else
318 return false;
319 #endif
322 bool ThemeService::HasCustomImage(int id) const {
323 return BrowserThemePack::IsPersistentImageID(id) &&
324 theme_supplier_ && theme_supplier_->HasCustomImage(id);
327 base::RefCountedMemory* ThemeService::GetRawData(
328 int id,
329 ui::ScaleFactor scale_factor) const {
330 // Check to see whether we should substitute some images.
331 int ntp_alternate = GetDisplayProperty(Properties::NTP_LOGO_ALTERNATE);
332 if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
333 id = IDR_PRODUCT_LOGO_WHITE;
335 base::RefCountedMemory* data = nullptr;
336 if (theme_supplier_.get())
337 data = theme_supplier_->GetRawData(id, scale_factor);
338 if (!data)
339 data = rb_.LoadDataResourceBytesForScale(id, ui::SCALE_FACTOR_100P);
341 return data;
344 void ThemeService::Shutdown() {
345 #if defined(ENABLE_EXTENSIONS)
346 theme_observer_.reset();
347 #endif
350 void ThemeService::Observe(int type,
351 const content::NotificationSource& source,
352 const content::NotificationDetails& details) {
353 using content::Details;
354 switch (type) {
355 case extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED:
356 registrar_.Remove(this,
357 extensions::NOTIFICATION_EXTENSIONS_READY_DEPRECATED,
358 content::Source<Profile>(profile_));
359 OnExtensionServiceReady();
360 break;
361 case extensions::NOTIFICATION_EXTENSION_ENABLED: {
362 const Extension* extension = Details<const Extension>(details).ptr();
363 if (extension->is_theme())
364 SetTheme(extension);
365 break;
367 default:
368 NOTREACHED();
372 void ThemeService::SetTheme(const Extension* extension) {
373 DCHECK(extension->is_theme());
374 ExtensionService* service =
375 extensions::ExtensionSystem::Get(profile_)->extension_service();
376 if (!service->IsExtensionEnabled(extension->id())) {
377 // |extension| is disabled when reverting to the previous theme via an
378 // infobar.
379 service->EnableExtension(extension->id());
380 // Enabling the extension will call back to SetTheme().
381 return;
384 std::string previous_theme_id = GetThemeID();
386 // Clear our image cache.
387 FreePlatformCaches();
389 BuildFromExtension(extension);
390 SaveThemeID(extension->id());
392 NotifyThemeChanged();
393 content::RecordAction(UserMetricsAction("Themes_Installed"));
395 if (previous_theme_id != kDefaultThemeID &&
396 previous_theme_id != extension->id() &&
397 service->GetInstalledExtension(previous_theme_id)) {
398 // Do not disable the previous theme if it is already uninstalled. Sending
399 // NOTIFICATION_BROWSER_THEME_CHANGED causes the previous theme to be
400 // uninstalled when the notification causes the remaining infobar to close
401 // and does not open any new infobars. See crbug.com/468280.
403 // Disable the old theme.
404 service->DisableExtension(previous_theme_id,
405 extensions::Extension::DISABLE_USER_ACTION);
409 void ThemeService::SetCustomDefaultTheme(
410 scoped_refptr<CustomThemeSupplier> theme_supplier) {
411 ClearAllThemeData();
412 SwapThemeSupplier(theme_supplier);
413 NotifyThemeChanged();
416 bool ThemeService::ShouldInitWithSystemTheme() const {
417 return false;
420 void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
421 // We do not want to garbage collect themes on startup (|ready_| is false).
422 // Themes will get garbage collected after |kRemoveUnusedThemesStartupDelay|.
423 if (!profile_ || !ready_)
424 return;
425 if (!ignore_infobars && number_of_infobars_ != 0)
426 return;
428 ExtensionService* service =
429 extensions::ExtensionSystem::Get(profile_)->extension_service();
430 if (!service)
431 return;
433 std::string current_theme = GetThemeID();
434 std::vector<std::string> remove_list;
435 scoped_ptr<const extensions::ExtensionSet> extensions(
436 extensions::ExtensionRegistry::Get(profile_)
437 ->GenerateInstalledExtensionsSet());
438 extensions::ExtensionPrefs* prefs = extensions::ExtensionPrefs::Get(profile_);
439 for (extensions::ExtensionSet::const_iterator it = extensions->begin();
440 it != extensions->end(); ++it) {
441 const extensions::Extension* extension = it->get();
442 if (extension->is_theme() &&
443 extension->id() != current_theme) {
444 // Only uninstall themes which are not disabled or are disabled with
445 // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
446 // themes because externally installed themes are initially disabled.
447 int disable_reason = prefs->GetDisableReasons(extension->id());
448 if (!prefs->IsExtensionDisabled(extension->id()) ||
449 disable_reason == Extension::DISABLE_USER_ACTION) {
450 remove_list.push_back((*it)->id());
454 // TODO: Garbage collect all unused themes. This method misses themes which
455 // are installed but not loaded because they are blacklisted by a management
456 // policy provider.
458 for (size_t i = 0; i < remove_list.size(); ++i) {
459 service->UninstallExtension(remove_list[i],
460 extensions::UNINSTALL_REASON_ORPHANED_THEME,
461 base::Bind(&base::DoNothing), nullptr);
465 void ThemeService::UseDefaultTheme() {
466 if (ready_)
467 content::RecordAction(UserMetricsAction("Themes_Reset"));
468 #if defined(ENABLE_SUPERVISED_USERS)
469 if (IsSupervisedUser()) {
470 SetSupervisedUserTheme();
471 return;
473 #endif
474 ClearAllThemeData();
475 NotifyThemeChanged();
478 void ThemeService::UseSystemTheme() {
479 UseDefaultTheme();
482 bool ThemeService::UsingDefaultTheme() const {
483 std::string id = GetThemeID();
484 return id == ThemeService::kDefaultThemeID ||
485 id == kDefaultThemeGalleryID;
488 std::string ThemeService::GetThemeID() const {
489 return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
492 color_utils::HSL ThemeService::GetTint(int id) const {
493 DCHECK(CalledOnValidThread());
495 color_utils::HSL hsl;
496 if (theme_supplier_.get() && theme_supplier_->GetTint(id, &hsl))
497 return hsl;
499 return ThemeProperties::GetDefaultTint(id);
502 void ThemeService::ClearAllThemeData() {
503 if (!ready_)
504 return;
506 SwapThemeSupplier(nullptr);
508 // Clear our image cache.
509 FreePlatformCaches();
511 profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
512 SaveThemeID(kDefaultThemeID);
514 // There should be no more infobars. This may not be the case because of
515 // http://crbug.com/62154
516 // RemoveUnusedThemes is called on a task because ClearAllThemeData() may
517 // be called as a result of NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED.
518 base::ThreadTaskRunnerHandle::Get()->PostTask(
519 FROM_HERE, base::Bind(&ThemeService::RemoveUnusedThemes,
520 weak_ptr_factory_.GetWeakPtr(), true));
523 void ThemeService::LoadThemePrefs() {
524 PrefService* prefs = profile_->GetPrefs();
526 std::string current_id = GetThemeID();
527 if (current_id == kDefaultThemeID) {
528 #if defined(ENABLE_SUPERVISED_USERS)
529 // Supervised users have a different default theme.
530 if (IsSupervisedUser()) {
531 SetSupervisedUserTheme();
532 set_ready();
533 return;
535 #endif
536 if (ShouldInitWithSystemTheme())
537 UseSystemTheme();
538 else
539 UseDefaultTheme();
540 set_ready();
541 return;
544 bool loaded_pack = false;
546 // If we don't have a file pack, we're updating from an old version.
547 base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
548 if (path != base::FilePath()) {
549 SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
550 loaded_pack = theme_supplier_.get() != nullptr;
553 if (loaded_pack) {
554 content::RecordAction(UserMetricsAction("Themes.Loaded"));
555 set_ready();
557 // Else: wait for the extension service to be ready so that the theme pack
558 // can be recreated from the extension.
561 void ThemeService::NotifyThemeChanged() {
562 if (!ready_)
563 return;
565 DVLOG(1) << "Sending BROWSER_THEME_CHANGED";
566 // Redraw!
567 content::NotificationService* service =
568 content::NotificationService::current();
569 service->Notify(chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
570 content::Source<ThemeService>(this),
571 content::NotificationService::NoDetails());
572 #if defined(OS_MACOSX)
573 NotifyPlatformThemeChanged();
574 #endif // OS_MACOSX
576 // Notify sync that theme has changed.
577 if (theme_syncable_service_.get()) {
578 theme_syncable_service_->OnThemeChange();
582 #if defined(USE_AURA)
583 void ThemeService::FreePlatformCaches() {
584 // Views (Skia) has no platform image cache to clear.
586 #endif
588 void ThemeService::OnExtensionServiceReady() {
589 if (!ready_) {
590 // If the ThemeService is not ready yet, the custom theme data pack needs to
591 // be recreated from the extension.
592 MigrateTheme();
593 set_ready();
595 // Send notification in case anyone requested data and cached it when the
596 // theme service was not ready yet.
597 NotifyThemeChanged();
600 #if defined(ENABLE_EXTENSIONS)
601 theme_observer_.reset(new ThemeObserver(this));
602 #endif
604 registrar_.Add(this,
605 extensions::NOTIFICATION_EXTENSION_ENABLED,
606 content::Source<Profile>(profile_));
608 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
609 FROM_HERE, base::Bind(&ThemeService::RemoveUnusedThemes,
610 weak_ptr_factory_.GetWeakPtr(), false),
611 base::TimeDelta::FromSeconds(kRemoveUnusedThemesStartupDelay));
614 void ThemeService::MigrateTheme() {
615 // TODO(erg): We need to pop up a dialog informing the user that their
616 // theme is being migrated.
617 ExtensionService* service =
618 extensions::ExtensionSystem::Get(profile_)->extension_service();
619 const Extension* extension =
620 service ? service->GetExtensionById(GetThemeID(), false) : nullptr;
621 if (extension) {
622 DLOG(ERROR) << "Migrating theme";
623 BuildFromExtension(extension);
624 content::RecordAction(UserMetricsAction("Themes.Migrated"));
625 } else {
626 DLOG(ERROR) << "Theme is mysteriously gone.";
627 ClearAllThemeData();
628 content::RecordAction(UserMetricsAction("Themes.Gone"));
632 void ThemeService::SwapThemeSupplier(
633 scoped_refptr<CustomThemeSupplier> theme_supplier) {
634 if (theme_supplier_.get())
635 theme_supplier_->StopUsingTheme();
636 theme_supplier_ = theme_supplier;
637 if (theme_supplier_.get())
638 theme_supplier_->StartUsingTheme();
641 void ThemeService::SavePackName(const base::FilePath& pack_path) {
642 profile_->GetPrefs()->SetFilePath(
643 prefs::kCurrentThemePackFilename, pack_path);
646 void ThemeService::SaveThemeID(const std::string& id) {
647 profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
650 void ThemeService::BuildFromExtension(const Extension* extension) {
651 scoped_refptr<BrowserThemePack> pack(
652 BrowserThemePack::BuildFromExtension(extension));
653 if (!pack.get()) {
654 // TODO(erg): We've failed to install the theme; perhaps we should tell the
655 // user? http://crbug.com/34780
656 LOG(ERROR) << "Could not load theme.";
657 return;
660 ExtensionService* service =
661 extensions::ExtensionSystem::Get(profile_)->extension_service();
662 if (!service)
663 return;
665 // Write the packed file to disk.
666 base::FilePath pack_path =
667 extension->path().Append(chrome::kThemePackFilename);
668 service->GetFileTaskRunner()->PostTask(
669 FROM_HERE,
670 base::Bind(&WritePackToDiskCallback, pack, pack_path));
672 SavePackName(pack_path);
673 SwapThemeSupplier(pack);
676 #if defined(ENABLE_SUPERVISED_USERS)
677 bool ThemeService::IsSupervisedUser() const {
678 return profile_->IsSupervised();
681 void ThemeService::SetSupervisedUserTheme() {
682 SetCustomDefaultTheme(new SupervisedUserTheme);
684 #endif
686 void ThemeService::OnInfobarDisplayed() {
687 number_of_infobars_++;
690 void ThemeService::OnInfobarDestroyed() {
691 number_of_infobars_--;
693 if (number_of_infobars_ == 0)
694 RemoveUnusedThemes(false);
697 ThemeSyncableService* ThemeService::GetThemeSyncableService() const {
698 return theme_syncable_service_.get();