blob: 90a3c8cc432919a4708790cd060f58dbbca51896 [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
[email protected]85bce45182011-11-29 03:10:527#include "base/bind.h"
[email protected]90fa2652012-04-24 16:18:358#include "base/memory/ref_counted_memory.h"
[email protected]b1ac70462013-08-14 21:33:309#include "base/message_loop/message_loop.h"
[email protected]3853a4c2013-02-11 17:15:5710#include "base/prefs/pref_service.h"
[email protected]fb441962013-05-08 05:35:2411#include "base/sequenced_task_runner.h"
[email protected]9f0abdb2013-06-10 21:49:3412#include "base/strings/string_util.h"
[email protected]e309f312013-06-07 21:50:0813#include "base/strings/utf_string_conversions.h"
[email protected]dcc8fbc2013-07-12 00:54:0914#include "chrome/browser/chrome_notification_types.h"
[email protected]18280372011-03-22 18:05:2215#include "chrome/browser/extensions/extension_service.h"
[email protected]7f8f24f2012-11-15 19:40:1416#include "chrome/browser/extensions/extension_system.h"
[email protected]b310f9962013-08-05 12:29:1517#include "chrome/browser/managed_mode/managed_user_theme.h"
[email protected]18280372011-03-22 18:05:2218#include "chrome/browser/profiles/profile.h"
[email protected]a0ea76c2011-03-23 17:36:4219#include "chrome/browser/themes/browser_theme_pack.h"
[email protected]ed0cef02013-07-26 12:41:1120#include "chrome/browser/themes/custom_theme_supplier.h"
[email protected]e119b802013-02-18 18:55:3921#include "chrome/browser/themes/theme_properties.h"
[email protected]280453e2012-09-28 19:09:4122#include "chrome/browser/themes/theme_syncable_service.h"
[email protected]a0ea76c2011-03-23 17:36:4223#include "chrome/common/chrome_constants.h"
[email protected]a0ea76c2011-03-23 17:36:4224#include "chrome/common/pref_names.h"
[email protected]ad50def52011-10-19 23:17:0725#include "content/public/browser/notification_service.h"
[email protected]7f6f44c2011-12-14 13:23:3826#include "content/public/browser/user_metrics.h"
[email protected]a0ea76c2011-03-23 17:36:4227#include "grit/theme_resources.h"
[email protected]29b25d092011-06-29 20:57:3428#include "grit/ui_resources.h"
[email protected]c49201a2012-05-24 11:04:5729#include "ui/base/layout.h"
[email protected]a0ea76c2011-03-23 17:36:4230#include "ui/base/resource/resource_bundle.h"
[email protected]546882b2012-05-11 00:53:3231#include "ui/gfx/image/image_skia.h"
[email protected]18280372011-03-22 18:05:2232
[email protected]779823e2012-10-01 22:51:5333#if defined(OS_WIN)
[email protected]19c9f902012-08-22 16:24:4034#include "ui/base/win/shell.h"
[email protected]18280372011-03-22 18:05:2235#endif
36
[email protected]631bb742011-11-02 11:29:3937using content::BrowserThread;
[email protected]7f6f44c2011-12-14 13:23:3838using content::UserMetricsAction;
[email protected]1c321ee52012-05-21 03:02:3439using extensions::Extension;
[email protected]b0af4792013-10-23 09:12:1340using extensions::UnloadedExtensionInfo;
[email protected]8e7b2cf42012-04-18 14:26:5841using ui::ResourceBundle;
[email protected]631bb742011-11-02 11:29:3942
[email protected]e119b802013-02-18 18:55:3943typedef ThemeProperties Properties;
[email protected]a0ea76c2011-03-23 17:36:4244
45// The default theme if we haven't installed a theme yet or if we've clicked
46// the "Use Classic" button.
47const char* ThemeService::kDefaultThemeID = "";
48
49namespace {
50
51// The default theme if we've gone to the theme gallery and installed the
52// "Default" theme. We have to detect this case specifically. (By the time we
53// realize we've installed the default theme, we already have an extension
54// unpacked on the filesystem.)
55const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn";
56
[email protected]b1ac70462013-08-14 21:33:3057// Wait this many seconds after startup to garbage collect unused themes.
58// Removing unused themes is done after a delay because there is no
59// reason to do it at startup.
60// ExtensionService::GarbageCollectExtensions() does something similar.
61const int kRemoveUnusedThemesStartupDelay = 30;
62
[email protected]a0ea76c2011-03-23 17:36:4263SkColor TintForUnderline(SkColor input) {
64 return SkColorSetA(input, SkColorGetA(input) / 3);
65}
66
67SkColor IncreaseLightness(SkColor color, double percent) {
68 color_utils::HSL result;
69 color_utils::SkColorToHSL(color, &result);
70 result.l += (1 - result.l) * percent;
71 return color_utils::HSLToSkColor(result, SkColorGetA(color));
72}
73
[email protected]a0ea76c2011-03-23 17:36:4274// Writes the theme pack to disk on a separate thread.
[email protected]7f8f24f2012-11-15 19:40:1475void WritePackToDiskCallback(BrowserThemePack* pack,
[email protected]650b2d52013-02-10 03:41:4576 const base::FilePath& path) {
[email protected]85bce45182011-11-29 03:10:5277 if (!pack->WriteToDisk(path))
78 NOTREACHED() << "Could not write theme pack to disk";
79}
[email protected]a0ea76c2011-03-23 17:36:4280
81} // namespace
82
[email protected]a0ea76c2011-03-23 17:36:4283ThemeService::ThemeService()
[email protected]94d410e2013-08-07 21:51:3084 : ready_(false),
85 rb_(ResourceBundle::GetSharedInstance()),
[email protected]a0ea76c2011-03-23 17:36:4286 profile_(NULL),
[email protected]b1ac70462013-08-14 21:33:3087 installed_pending_load_id_(kDefaultThemeID),
[email protected]43b424e2013-08-12 04:51:1388 number_of_infobars_(0),
89 weak_ptr_factory_(this) {
[email protected]a0ea76c2011-03-23 17:36:4290}
91
92ThemeService::~ThemeService() {
93 FreePlatformCaches();
94}
95
96void ThemeService::Init(Profile* profile) {
97 DCHECK(CalledOnValidThread());
98 profile_ = profile;
99
[email protected]a0ea76c2011-03-23 17:36:42100 LoadThemePrefs();
[email protected]280453e2012-09-28 19:09:41101
[email protected]b1ac70462013-08-14 21:33:30102 registrar_.Add(this,
103 chrome::NOTIFICATION_EXTENSIONS_READY,
104 content::Source<Profile>(profile_));
[email protected]264c60b2013-03-20 01:30:54105
[email protected]280453e2012-09-28 19:09:41106 theme_syncable_service_.reset(new ThemeSyncableService(profile_, this));
[email protected]a0ea76c2011-03-23 17:36:42107}
108
[email protected]bc091772012-10-03 06:07:24109gfx::Image ThemeService::GetImageNamed(int id) const {
[email protected]7664efd2011-12-05 21:31:42110 DCHECK(CalledOnValidThread());
111
[email protected]59723ccf2013-04-05 17:52:13112 gfx::Image image;
[email protected]ed0cef02013-07-26 12:41:11113 if (theme_supplier_.get())
114 image = theme_supplier_->GetImageNamed(id);
[email protected]7664efd2011-12-05 21:31:42115
[email protected]59723ccf2013-04-05 17:52:13116 if (image.IsEmpty())
117 image = rb_.GetNativeImageNamed(id);
[email protected]7664efd2011-12-05 21:31:42118
[email protected]59723ccf2013-04-05 17:52:13119 return image;
[email protected]7664efd2011-12-05 21:31:42120}
121
[email protected]546882b2012-05-11 00:53:32122gfx::ImageSkia* ThemeService::GetImageSkiaNamed(int id) const {
[email protected]bc091772012-10-03 06:07:24123 gfx::Image image = GetImageNamed(id);
124 if (image.IsEmpty())
[email protected]546882b2012-05-11 00:53:32125 return NULL;
126 // TODO(pkotwicz): Remove this const cast. The gfx::Image interface returns
127 // its images const. GetImageSkiaNamed() also should but has many callsites.
[email protected]bc091772012-10-03 06:07:24128 return const_cast<gfx::ImageSkia*>(image.ToImageSkia());
[email protected]a0ea76c2011-03-23 17:36:42129}
130
131SkColor ThemeService::GetColor(int id) const {
132 DCHECK(CalledOnValidThread());
[email protected]a0ea76c2011-03-23 17:36:42133 SkColor color;
[email protected]ed0cef02013-07-26 12:41:11134 if (theme_supplier_.get() && theme_supplier_->GetColor(id, &color))
[email protected]a0ea76c2011-03-23 17:36:42135 return color;
136
[email protected]a0ea76c2011-03-23 17:36:42137 // For backward compat with older themes, some newer colors are generated from
138 // older ones if they are missing.
139 switch (id) {
[email protected]e119b802013-02-18 18:55:39140 case Properties::COLOR_NTP_SECTION_HEADER_TEXT:
141 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.30);
142 case Properties::COLOR_NTP_SECTION_HEADER_TEXT_HOVER:
143 return GetColor(Properties::COLOR_NTP_TEXT);
144 case Properties::COLOR_NTP_SECTION_HEADER_RULE:
145 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.70);
146 case Properties::COLOR_NTP_SECTION_HEADER_RULE_LIGHT:
147 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.86);
148 case Properties::COLOR_NTP_TEXT_LIGHT:
149 return IncreaseLightness(GetColor(Properties::COLOR_NTP_TEXT), 0.40);
[email protected]053448f2013-06-18 13:41:08150 case Properties::COLOR_MANAGED_USER_LABEL:
[email protected]ed0cef02013-07-26 12:41:11151 return color_utils::GetReadableColor(
152 SK_ColorWHITE,
153 GetColor(Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND));
[email protected]053448f2013-06-18 13:41:08154 case Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND:
[email protected]ed0cef02013-07-26 12:41:11155 return color_utils::BlendTowardOppositeLuminance(
156 GetColor(Properties::COLOR_FRAME), 0x80);
[email protected]21e783892013-08-08 17:23:47157 case Properties::COLOR_MANAGED_USER_LABEL_BORDER:
158 return color_utils::AlphaBlend(
159 GetColor(Properties::COLOR_MANAGED_USER_LABEL_BACKGROUND),
160 SK_ColorBLACK,
161 230);
[email protected]a0ea76c2011-03-23 17:36:42162 }
163
[email protected]e119b802013-02-18 18:55:39164 return Properties::GetDefaultColor(id);
[email protected]a0ea76c2011-03-23 17:36:42165}
166
[email protected]7ce1599f2013-08-30 08:54:04167int ThemeService::GetDisplayProperty(int id) const {
168 int result = 0;
169 if (theme_supplier_.get() &&
170 theme_supplier_->GetDisplayProperty(id, &result)) {
171 return result;
172 }
[email protected]a0ea76c2011-03-23 17:36:42173
[email protected]7ce1599f2013-08-30 08:54:04174 if (id == Properties::NTP_LOGO_ALTERNATE &&
175 !UsingDefaultTheme() &&
176 !UsingNativeTheme()) {
177 // Use the alternate logo for themes from the web store except for
178 // |kDefaultThemeGalleryID|.
179 return 1;
180 }
181
182 return Properties::GetDefaultDisplayProperty(id);
[email protected]a0ea76c2011-03-23 17:36:42183}
184
185bool ThemeService::ShouldUseNativeFrame() const {
186 if (HasCustomImage(IDR_THEME_FRAME))
187 return false;
[email protected]779823e2012-10-01 22:51:53188#if defined(OS_WIN)
[email protected]19c9f902012-08-22 16:24:40189 return ui::win::IsAeroGlassEnabled();
[email protected]a0ea76c2011-03-23 17:36:42190#else
191 return false;
192#endif
193}
194
195bool ThemeService::HasCustomImage(int id) const {
[email protected]e119b802013-02-18 18:55:39196 if (!Properties::IsThemeableImage(id))
[email protected]a0ea76c2011-03-23 17:36:42197 return false;
198
[email protected]ed0cef02013-07-26 12:41:11199 if (theme_supplier_.get())
200 return theme_supplier_->HasCustomImage(id);
[email protected]d1b00ae62013-07-05 18:28:15201
[email protected]a0ea76c2011-03-23 17:36:42202 return false;
203}
204
[email protected]0a3e3c6112012-08-05 20:07:45205base::RefCountedMemory* ThemeService::GetRawData(
206 int id,
207 ui::ScaleFactor scale_factor) const {
[email protected]a0ea76c2011-03-23 17:36:42208 // Check to see whether we should substitute some images.
[email protected]7ce1599f2013-08-30 08:54:04209 int ntp_alternate = GetDisplayProperty(Properties::NTP_LOGO_ALTERNATE);
[email protected]a0ea76c2011-03-23 17:36:42210 if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
211 id = IDR_PRODUCT_LOGO_WHITE;
212
[email protected]68c7630b2012-05-02 22:37:42213 base::RefCountedMemory* data = NULL;
[email protected]ed0cef02013-07-26 12:41:11214 if (theme_supplier_.get())
215 data = theme_supplier_->GetRawData(id, scale_factor);
[email protected]a0ea76c2011-03-23 17:36:42216 if (!data)
[email protected]4d8bb1a92012-11-01 21:12:40217 data = rb_.LoadDataResourceBytesForScale(id, ui::SCALE_FACTOR_100P);
[email protected]a0ea76c2011-03-23 17:36:42218
219 return data;
220}
221
[email protected]264c60b2013-03-20 01:30:54222void ThemeService::Observe(int type,
223 const content::NotificationSource& source,
224 const content::NotificationDetails& details) {
[email protected]b1ac70462013-08-14 21:33:30225 using content::Details;
226 switch (type) {
227 case chrome::NOTIFICATION_EXTENSIONS_READY:
228 registrar_.Remove(this, chrome::NOTIFICATION_EXTENSIONS_READY,
229 content::Source<Profile>(profile_));
230 OnExtensionServiceReady();
231 break;
232 case chrome::NOTIFICATION_EXTENSION_INSTALLED:
233 {
234 // The theme may be initially disabled. Wait till it is loaded (if ever).
235 Details<const extensions::InstalledExtensionInfo> installed_details(
236 details);
237 if (installed_details->extension->is_theme())
238 installed_pending_load_id_ = installed_details->extension->id();
239 break;
240 }
241 case chrome::NOTIFICATION_EXTENSION_LOADED:
242 {
243 const Extension* extension = Details<const Extension>(details).ptr();
244 if (extension->is_theme() &&
245 installed_pending_load_id_ != kDefaultThemeID &&
246 installed_pending_load_id_ == extension->id()) {
247 SetTheme(extension);
248 }
249 installed_pending_load_id_ = kDefaultThemeID;
250 break;
251 }
252 case chrome::NOTIFICATION_EXTENSION_ENABLED:
253 {
254 const Extension* extension = Details<const Extension>(details).ptr();
255 if (extension->is_theme())
256 SetTheme(extension);
257 break;
258 }
259 case chrome::NOTIFICATION_EXTENSION_UNLOADED:
260 {
[email protected]b0af4792013-10-23 09:12:13261 Details<const UnloadedExtensionInfo> unloaded_details(details);
262 if (unloaded_details->reason != UnloadedExtensionInfo::REASON_UPDATE &&
[email protected]b1ac70462013-08-14 21:33:30263 unloaded_details->extension->is_theme() &&
264 unloaded_details->extension->id() == GetThemeID()) {
265 UseDefaultTheme();
266 }
267 break;
268 }
269 }
[email protected]264c60b2013-03-20 01:30:54270}
271
[email protected]a0ea76c2011-03-23 17:36:42272void ThemeService::SetTheme(const Extension* extension) {
[email protected]b1ac70462013-08-14 21:33:30273 DCHECK(extension->is_theme());
274 ExtensionService* service =
275 extensions::ExtensionSystem::Get(profile_)->extension_service();
276 if (!service->IsExtensionEnabled(extension->id())) {
277 // |extension| is disabled when reverting to the previous theme via an
278 // infobar.
279 service->EnableExtension(extension->id());
280 // Enabling the extension will call back to SetTheme().
281 return;
282 }
283
284 std::string previous_theme_id = GetThemeID();
285
[email protected]a0ea76c2011-03-23 17:36:42286 // Clear our image cache.
287 FreePlatformCaches();
288
[email protected]a0ea76c2011-03-23 17:36:42289 BuildFromExtension(extension);
[email protected]d5949312012-12-03 22:13:30290 SaveThemeID(extension->id());
[email protected]a0ea76c2011-03-23 17:36:42291
[email protected]a3e61e82011-04-15 20:32:08292 NotifyThemeChanged();
[email protected]7f6f44c2011-12-14 13:23:38293 content::RecordAction(UserMetricsAction("Themes_Installed"));
[email protected]b1ac70462013-08-14 21:33:30294
295 if (previous_theme_id != kDefaultThemeID &&
296 previous_theme_id != extension->id()) {
297 // Disable the old theme.
298 service->DisableExtension(previous_theme_id,
299 extensions::Extension::DISABLE_USER_ACTION);
300 }
[email protected]a0ea76c2011-03-23 17:36:42301}
302
[email protected]ed0cef02013-07-26 12:41:11303void ThemeService::SetCustomDefaultTheme(
304 scoped_refptr<CustomThemeSupplier> theme_supplier) {
305 ClearAllThemeData();
306 SwapThemeSupplier(theme_supplier);
307 NotifyThemeChanged();
308}
309
310bool ThemeService::ShouldInitWithNativeTheme() const {
311 return false;
312}
313
[email protected]b1ac70462013-08-14 21:33:30314void ThemeService::RemoveUnusedThemes(bool ignore_infobars) {
[email protected]ed0cef02013-07-26 12:41:11315 // We do not want to garbage collect themes on startup (|ready_| is false).
[email protected]b1ac70462013-08-14 21:33:30316 // Themes will get garbage collected after |kRemoveUnusedThemesStartupDelay|.
[email protected]ed0cef02013-07-26 12:41:11317 if (!profile_ || !ready_)
[email protected]d5949312012-12-03 22:13:30318 return;
[email protected]b1ac70462013-08-14 21:33:30319 if (!ignore_infobars && number_of_infobars_ != 0)
320 return;
[email protected]ed0cef02013-07-26 12:41:11321
[email protected]d5949312012-12-03 22:13:30322 ExtensionService* service = profile_->GetExtensionService();
323 if (!service)
324 return;
325 std::string current_theme = GetThemeID();
326 std::vector<std::string> remove_list;
[email protected]b1ac70462013-08-14 21:33:30327 scoped_ptr<const ExtensionSet> extensions(
328 service->GenerateInstalledExtensionsSet());
329 extensions::ExtensionPrefs* prefs = service->extension_prefs();
[email protected]d5949312012-12-03 22:13:30330 for (ExtensionSet::const_iterator it = extensions->begin();
331 it != extensions->end(); ++it) {
[email protected]b1ac70462013-08-14 21:33:30332 const extensions::Extension* extension = *it;
333 if (extension->is_theme() &&
334 extension->id() != current_theme) {
335 // Only uninstall themes which are not disabled or are disabled with
336 // reason DISABLE_USER_ACTION. We cannot blanket uninstall all disabled
337 // themes because externally installed themes are initially disabled.
338 int disable_reason = prefs->GetDisableReasons(extension->id());
339 if (!prefs->IsExtensionDisabled(extension->id()) ||
340 disable_reason == Extension::DISABLE_USER_ACTION) {
341 remove_list.push_back((*it)->id());
342 }
[email protected]d5949312012-12-03 22:13:30343 }
344 }
[email protected]b1ac70462013-08-14 21:33:30345 // TODO: Garbage collect all unused themes. This method misses themes which
346 // are installed but not loaded because they are blacklisted by a management
347 // policy provider.
348
[email protected]d5949312012-12-03 22:13:30349 for (size_t i = 0; i < remove_list.size(); ++i)
350 service->UninstallExtension(remove_list[i], false, NULL);
351}
352
[email protected]a0ea76c2011-03-23 17:36:42353void ThemeService::UseDefaultTheme() {
[email protected]ed0cef02013-07-26 12:41:11354 if (ready_)
355 content::RecordAction(UserMetricsAction("Themes_Reset"));
356 if (IsManagedUser()) {
357 SetManagedUserTheme();
358 return;
359 }
[email protected]94d410e2013-08-07 21:51:30360 ClearAllThemeData();
361 NotifyThemeChanged();
[email protected]a0ea76c2011-03-23 17:36:42362}
363
364void ThemeService::SetNativeTheme() {
365 UseDefaultTheme();
366}
367
[email protected]6d3eb2e2011-04-22 18:50:57368bool ThemeService::UsingDefaultTheme() const {
[email protected]a0ea76c2011-03-23 17:36:42369 std::string id = GetThemeID();
370 return id == ThemeService::kDefaultThemeID ||
[email protected]7ce1599f2013-08-30 08:54:04371 id == kDefaultThemeGalleryID;
[email protected]a0ea76c2011-03-23 17:36:42372}
373
[email protected]6d3eb2e2011-04-22 18:50:57374bool ThemeService::UsingNativeTheme() const {
375 return UsingDefaultTheme();
376}
377
[email protected]a0ea76c2011-03-23 17:36:42378std::string ThemeService::GetThemeID() const {
[email protected]d5949312012-12-03 22:13:30379 return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
[email protected]a0ea76c2011-03-23 17:36:42380}
381
[email protected]a0ea76c2011-03-23 17:36:42382color_utils::HSL ThemeService::GetTint(int id) const {
383 DCHECK(CalledOnValidThread());
384
385 color_utils::HSL hsl;
[email protected]ed0cef02013-07-26 12:41:11386 if (theme_supplier_.get() && theme_supplier_->GetTint(id, &hsl))
[email protected]a0ea76c2011-03-23 17:36:42387 return hsl;
388
[email protected]e119b802013-02-18 18:55:39389 return ThemeProperties::GetDefaultTint(id);
[email protected]a0ea76c2011-03-23 17:36:42390}
391
392void ThemeService::ClearAllThemeData() {
[email protected]94d410e2013-08-07 21:51:30393 if (!ready_)
394 return;
395
[email protected]ed0cef02013-07-26 12:41:11396 SwapThemeSupplier(NULL);
397
[email protected]a0ea76c2011-03-23 17:36:42398 // Clear our image cache.
399 FreePlatformCaches();
[email protected]a0ea76c2011-03-23 17:36:42400
401 profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
[email protected]d5949312012-12-03 22:13:30402 SaveThemeID(kDefaultThemeID);
[email protected]8b1058bb2013-04-08 20:15:27403
[email protected]b1ac70462013-08-14 21:33:30404 // There should be no more infobars. This may not be the case because of
405 // http://crbug.com/62154
406 // RemoveUnusedThemes is called on a task because ClearAllThemeData() may
407 // be called as a result of NOTIFICATION_EXTENSION_UNLOADED.
408 base::MessageLoop::current()->PostTask(FROM_HERE,
409 base::Bind(&ThemeService::RemoveUnusedThemes,
410 weak_ptr_factory_.GetWeakPtr(),
411 true));
[email protected]a0ea76c2011-03-23 17:36:42412}
413
414void ThemeService::LoadThemePrefs() {
415 PrefService* prefs = profile_->GetPrefs();
416
417 std::string current_id = GetThemeID();
[email protected]264c60b2013-03-20 01:30:54418 if (current_id == kDefaultThemeID) {
[email protected]ed0cef02013-07-26 12:41:11419 // Managed users have a different default theme.
420 if (IsManagedUser())
421 SetManagedUserTheme();
422 else if (ShouldInitWithNativeTheme())
423 SetNativeTheme();
424 else
425 UseDefaultTheme();
[email protected]264c60b2013-03-20 01:30:54426 set_ready();
427 return;
428 }
[email protected]a0ea76c2011-03-23 17:36:42429
[email protected]264c60b2013-03-20 01:30:54430 bool loaded_pack = false;
[email protected]a0ea76c2011-03-23 17:36:42431
[email protected]264c60b2013-03-20 01:30:54432 // If we don't have a file pack, we're updating from an old version.
433 base::FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
434 if (path != base::FilePath()) {
[email protected]ed0cef02013-07-26 12:41:11435 SwapThemeSupplier(BrowserThemePack::BuildFromDataPack(path, current_id));
436 loaded_pack = theme_supplier_.get() != NULL;
[email protected]264c60b2013-03-20 01:30:54437 }
438
439 if (loaded_pack) {
440 content::RecordAction(UserMetricsAction("Themes.Loaded"));
441 set_ready();
[email protected]a0ea76c2011-03-23 17:36:42442 }
[email protected]b1ac70462013-08-14 21:33:30443 // Else: wait for the extension service to be ready so that the theme pack
444 // can be recreated from the extension.
[email protected]a0ea76c2011-03-23 17:36:42445}
446
[email protected]a3e61e82011-04-15 20:32:08447void ThemeService::NotifyThemeChanged() {
[email protected]ed0cef02013-07-26 12:41:11448 if (!ready_)
449 return;
450
[email protected]8e88ca12011-11-04 20:45:39451 DVLOG(1) << "Sending BROWSER_THEME_CHANGED";
[email protected]a0ea76c2011-03-23 17:36:42452 // Redraw!
[email protected]ad50def52011-10-19 23:17:07453 content::NotificationService* service =
454 content::NotificationService::current();
[email protected]432115822011-07-10 15:52:27455 service->Notify(chrome::NOTIFICATION_BROWSER_THEME_CHANGED,
[email protected]6c2381d2011-10-19 02:52:53456 content::Source<ThemeService>(this),
[email protected]ad50def52011-10-19 23:17:07457 content::NotificationService::NoDetails());
[email protected]a0ea76c2011-03-23 17:36:42458#if defined(OS_MACOSX)
459 NotifyPlatformThemeChanged();
460#endif // OS_MACOSX
[email protected]280453e2012-09-28 19:09:41461
462 // Notify sync that theme has changed.
463 if (theme_syncable_service_.get()) {
464 theme_syncable_service_->OnThemeChange();
465 }
[email protected]a0ea76c2011-03-23 17:36:42466}
467
[email protected]3fa441d2011-09-18 17:28:50468#if defined(OS_WIN) || defined(USE_AURA)
[email protected]a0ea76c2011-03-23 17:36:42469void ThemeService::FreePlatformCaches() {
470 // Views (Skia) has no platform image cache to clear.
471}
472#endif
473
[email protected]b1ac70462013-08-14 21:33:30474void ThemeService::OnExtensionServiceReady() {
475 if (!ready_) {
476 // If the ThemeService is not ready yet, the custom theme data pack needs to
477 // be recreated from the extension.
478 MigrateTheme();
479 set_ready();
480
481 // Send notification in case anyone requested data and cached it when the
482 // theme service was not ready yet.
483 NotifyThemeChanged();
484 }
485
486 registrar_.Add(this,
487 chrome::NOTIFICATION_EXTENSION_INSTALLED,
488 content::Source<Profile>(profile_));
489 registrar_.Add(this,
490 chrome::NOTIFICATION_EXTENSION_LOADED,
491 content::Source<Profile>(profile_));
492 registrar_.Add(this,
493 chrome::NOTIFICATION_EXTENSION_ENABLED,
494 content::Source<Profile>(profile_));
495 registrar_.Add(this,
496 chrome::NOTIFICATION_EXTENSION_UNLOADED,
497 content::Source<Profile>(profile_));
498
499 base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
500 base::Bind(&ThemeService::RemoveUnusedThemes,
501 weak_ptr_factory_.GetWeakPtr(),
502 false),
503 base::TimeDelta::FromSeconds(kRemoveUnusedThemesStartupDelay));
[email protected]ed0cef02013-07-26 12:41:11504}
505
[email protected]264c60b2013-03-20 01:30:54506void ThemeService::MigrateTheme() {
[email protected]b1ac70462013-08-14 21:33:30507 // TODO(erg): We need to pop up a dialog informing the user that their
508 // theme is being migrated.
[email protected]264c60b2013-03-20 01:30:54509 ExtensionService* service =
510 extensions::ExtensionSystem::Get(profile_)->extension_service();
511 const Extension* extension = service ?
512 service->GetExtensionById(GetThemeID(), false) : NULL;
513 if (extension) {
514 DLOG(ERROR) << "Migrating theme";
515 BuildFromExtension(extension);
516 content::RecordAction(UserMetricsAction("Themes.Migrated"));
517 } else {
518 DLOG(ERROR) << "Theme is mysteriously gone.";
519 ClearAllThemeData();
520 content::RecordAction(UserMetricsAction("Themes.Gone"));
521 }
522}
523
[email protected]b1ac70462013-08-14 21:33:30524void ThemeService::SwapThemeSupplier(
525 scoped_refptr<CustomThemeSupplier> theme_supplier) {
526 if (theme_supplier_.get())
527 theme_supplier_->StopUsingTheme();
528 theme_supplier_ = theme_supplier;
529 if (theme_supplier_.get())
530 theme_supplier_->StartUsingTheme();
531}
532
[email protected]650b2d52013-02-10 03:41:45533void ThemeService::SavePackName(const base::FilePath& pack_path) {
[email protected]a0ea76c2011-03-23 17:36:42534 profile_->GetPrefs()->SetFilePath(
535 prefs::kCurrentThemePackFilename, pack_path);
536}
537
[email protected]d5949312012-12-03 22:13:30538void ThemeService::SaveThemeID(const std::string& id) {
539 profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
540}
541
[email protected]a0ea76c2011-03-23 17:36:42542void ThemeService::BuildFromExtension(const Extension* extension) {
543 scoped_refptr<BrowserThemePack> pack(
544 BrowserThemePack::BuildFromExtension(extension));
545 if (!pack.get()) {
546 // TODO(erg): We've failed to install the theme; perhaps we should tell the
547 // user? http://crbug.com/34780
548 LOG(ERROR) << "Could not load theme.";
549 return;
550 }
551
[email protected]7f8f24f2012-11-15 19:40:14552 ExtensionService* service =
553 extensions::ExtensionSystem::Get(profile_)->extension_service();
554 if (!service)
555 return;
556
[email protected]a0ea76c2011-03-23 17:36:42557 // Write the packed file to disk.
[email protected]650b2d52013-02-10 03:41:45558 base::FilePath pack_path =
559 extension->path().Append(chrome::kThemePackFilename);
[email protected]7f8f24f2012-11-15 19:40:14560 service->GetFileTaskRunner()->PostTask(
561 FROM_HERE,
[email protected]85bce45182011-11-29 03:10:52562 base::Bind(&WritePackToDiskCallback, pack, pack_path));
[email protected]a0ea76c2011-03-23 17:36:42563
564 SavePackName(pack_path);
[email protected]ed0cef02013-07-26 12:41:11565 SwapThemeSupplier(pack);
[email protected]a0ea76c2011-03-23 17:36:42566}
567
[email protected]768c0ff2013-06-21 02:50:55568bool ThemeService::IsManagedUser() const {
[email protected]e000daf2013-07-31 16:50:58569 return profile_->IsManaged();
[email protected]768c0ff2013-06-21 02:50:55570}
571
[email protected]ed0cef02013-07-26 12:41:11572void ThemeService::SetManagedUserTheme() {
[email protected]ed0cef02013-07-26 12:41:11573 SetCustomDefaultTheme(new ManagedUserTheme);
[email protected]ed0cef02013-07-26 12:41:11574}
575
[email protected]d5949312012-12-03 22:13:30576void ThemeService::OnInfobarDisplayed() {
577 number_of_infobars_++;
578}
579
580void ThemeService::OnInfobarDestroyed() {
581 number_of_infobars_--;
582
583 if (number_of_infobars_ == 0)
[email protected]b1ac70462013-08-14 21:33:30584 RemoveUnusedThemes(false);
[email protected]d5949312012-12-03 22:13:30585}
586
[email protected]280453e2012-09-28 19:09:41587ThemeSyncableService* ThemeService::GetThemeSyncableService() const {
588 return theme_syncable_service_.get();
589}