• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/themes/theme_service.h"
6 
7 #include "base/string_split.h"
8 #include "base/string_util.h"
9 #include "base/utf_string_conversions.h"
10 #include "chrome/browser/extensions/extension_service.h"
11 #include "chrome/browser/metrics/user_metrics.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/themes/browser_theme_pack.h"
14 #include "chrome/common/chrome_constants.h"
15 #include "chrome/common/pref_names.h"
16 #include "content/common/notification_service.h"
17 #include "content/common/notification_type.h"
18 #include "grit/app_resources.h"
19 #include "grit/theme_resources.h"
20 #include "ui/base/resource/resource_bundle.h"
21 
22 #if defined(OS_WIN)
23 #include "views/widget/widget_win.h"
24 #endif
25 
26 // Strings used in alignment properties.
27 const char* ThemeService::kAlignmentTop = "top";
28 const char* ThemeService::kAlignmentBottom = "bottom";
29 const char* ThemeService::kAlignmentLeft = "left";
30 const char* ThemeService::kAlignmentRight = "right";
31 
32 // Strings used in background tiling repetition properties.
33 const char* ThemeService::kTilingNoRepeat = "no-repeat";
34 const char* ThemeService::kTilingRepeatX = "repeat-x";
35 const char* ThemeService::kTilingRepeatY = "repeat-y";
36 const char* ThemeService::kTilingRepeat = "repeat";
37 
38 // The default theme if we haven't installed a theme yet or if we've clicked
39 // the "Use Classic" button.
40 const char* ThemeService::kDefaultThemeID = "";
41 
42 namespace {
43 
44 // The default theme if we've gone to the theme gallery and installed the
45 // "Default" theme. We have to detect this case specifically. (By the time we
46 // realize we've installed the default theme, we already have an extension
47 // unpacked on the filesystem.)
48 const char* kDefaultThemeGalleryID = "hkacjpbfdknhflllbcmjibkdeoafencn";
49 
TintForUnderline(SkColor input)50 SkColor TintForUnderline(SkColor input) {
51   return SkColorSetA(input, SkColorGetA(input) / 3);
52 }
53 
IncreaseLightness(SkColor color,double percent)54 SkColor IncreaseLightness(SkColor color, double percent) {
55   color_utils::HSL result;
56   color_utils::SkColorToHSL(color, &result);
57   result.l += (1 - result.l) * percent;
58   return color_utils::HSLToSkColor(result, SkColorGetA(color));
59 }
60 
61 // Default colors.
62 const SkColor kDefaultColorFrame = SkColorSetRGB(66, 116, 201);
63 const SkColor kDefaultColorFrameInactive = SkColorSetRGB(161, 182, 228);
64 const SkColor kDefaultColorFrameIncognito = SkColorSetRGB(83, 106, 139);
65 const SkColor kDefaultColorFrameIncognitoInactive =
66     SkColorSetRGB(126, 139, 156);
67 #if defined(OS_MACOSX)
68 const SkColor kDefaultColorToolbar = SkColorSetRGB(230, 230, 230);
69 #else
70 const SkColor kDefaultColorToolbar = SkColorSetRGB(223, 223, 223);
71 #endif
72 const SkColor kDefaultColorTabText = SK_ColorBLACK;
73 #if defined(OS_MACOSX)
74 const SkColor kDefaultColorBackgroundTabText = SK_ColorBLACK;
75 const SkColor kDefaultColorBookmarkText = SK_ColorBLACK;
76 #else
77 const SkColor kDefaultColorBackgroundTabText = SkColorSetRGB(64, 64, 64);
78 const SkColor kDefaultColorBookmarkText = SkColorSetRGB(18, 50, 114);
79 #endif
80 #if defined(OS_WIN)
81 const SkColor kDefaultColorNTPBackground =
82     color_utils::GetSysSkColor(COLOR_WINDOW);
83 const SkColor kDefaultColorNTPText =
84     color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
85 const SkColor kDefaultColorNTPLink =
86     color_utils::GetSysSkColor(COLOR_HOTLIGHT);
87 #else
88 // TODO(beng): source from theme provider.
89 const SkColor kDefaultColorNTPBackground = SK_ColorWHITE;
90 const SkColor kDefaultColorNTPText = SK_ColorBLACK;
91 const SkColor kDefaultColorNTPLink = SkColorSetRGB(6, 55, 116);
92 #endif
93 const SkColor kDefaultColorNTPHeader = SkColorSetRGB(150, 150, 150);
94 const SkColor kDefaultColorNTPSection = SkColorSetRGB(229, 229, 229);
95 const SkColor kDefaultColorNTPSectionText = SK_ColorBLACK;
96 const SkColor kDefaultColorNTPSectionLink = SkColorSetRGB(6, 55, 116);
97 const SkColor kDefaultColorControlBackground = SkColorSetARGB(0, 0, 0, 0);
98 const SkColor kDefaultColorButtonBackground = SkColorSetARGB(0, 0, 0, 0);
99 #if defined(OS_MACOSX)
100 const SkColor kDefaultColorToolbarButtonStroke = SkColorSetARGB(75, 81, 81, 81);
101 const SkColor kDefaultColorToolbarButtonStrokeInactive =
102     SkColorSetARGB(75, 99, 99, 99);
103 const SkColor kDefaultColorToolbarBezel = SkColorSetRGB(247, 247, 247);
104 const SkColor kDefaultColorToolbarStroke = SkColorSetRGB(103, 103, 103);
105 const SkColor kDefaultColorToolbarStrokeInactive = SkColorSetRGB(123, 123, 123);
106 #endif
107 
108 // Default tints.
109 const color_utils::HSL kDefaultTintButtons = { -1, -1, -1 };
110 const color_utils::HSL kDefaultTintFrame = { -1, -1, -1 };
111 const color_utils::HSL kDefaultTintFrameInactive = { -1, -1, 0.75f };
112 const color_utils::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f };
113 const color_utils::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f };
114 const color_utils::HSL kDefaultTintBackgroundTab = { -1, 0.5, 0.75 };
115 
116 // Default display properties.
117 const int kDefaultDisplayPropertyNTPAlignment =
118     ThemeService::ALIGN_BOTTOM;
119 const int kDefaultDisplayPropertyNTPTiling =
120     ThemeService::NO_REPEAT;
121 const int kDefaultDisplayPropertyNTPInverseLogo = 0;
122 
123 // The sum of kFrameBorderThickness and kNonClientRestoredExtraThickness from
124 // OpaqueBrowserFrameView.
125 const int kRestoredTabVerticalOffset = 15;
126 
127 // The image resources we will allow people to theme.
128 const int kThemeableImages[] = {
129   IDR_THEME_FRAME,
130   IDR_THEME_FRAME_INACTIVE,
131   IDR_THEME_FRAME_INCOGNITO,
132   IDR_THEME_FRAME_INCOGNITO_INACTIVE,
133   IDR_THEME_TOOLBAR,
134   IDR_THEME_TAB_BACKGROUND,
135   IDR_THEME_TAB_BACKGROUND_INCOGNITO,
136   IDR_THEME_TAB_BACKGROUND_V,
137   IDR_THEME_NTP_BACKGROUND,
138   IDR_THEME_FRAME_OVERLAY,
139   IDR_THEME_FRAME_OVERLAY_INACTIVE,
140   IDR_THEME_BUTTON_BACKGROUND,
141   IDR_THEME_NTP_ATTRIBUTION,
142   IDR_THEME_WINDOW_CONTROL_BACKGROUND
143 };
144 
HasThemeableImage(int themeable_image_id)145 bool HasThemeableImage(int themeable_image_id) {
146   static std::set<int> themeable_images;
147   if (themeable_images.empty()) {
148     themeable_images.insert(
149         kThemeableImages, kThemeableImages + arraysize(kThemeableImages));
150   }
151   return themeable_images.count(themeable_image_id) > 0;
152 }
153 
154 // The image resources that will be tinted by the 'button' tint value.
155 // If you change this list, you must increment the version number in
156 // browser_theme_pack.cc, and you should assign persistent IDs to the
157 // data table at the start of said file or else tinted versions of
158 // these resources will not be created.
159 const int kToolbarButtonIDs[] = {
160   IDR_BACK, IDR_BACK_D, IDR_BACK_H, IDR_BACK_P,
161   IDR_FORWARD, IDR_FORWARD_D, IDR_FORWARD_H, IDR_FORWARD_P,
162   IDR_HOME, IDR_HOME_H, IDR_HOME_P,
163   IDR_RELOAD, IDR_RELOAD_H, IDR_RELOAD_P,
164   IDR_STOP, IDR_STOP_D, IDR_STOP_H, IDR_STOP_P,
165   IDR_LOCATIONBG_C, IDR_LOCATIONBG_L, IDR_LOCATIONBG_R,
166   IDR_BROWSER_ACTIONS_OVERFLOW, IDR_BROWSER_ACTIONS_OVERFLOW_H,
167   IDR_BROWSER_ACTIONS_OVERFLOW_P,
168   IDR_TOOLS, IDR_TOOLS_H, IDR_TOOLS_P,
169   IDR_MENU_DROPARROW,
170   IDR_THROBBER, IDR_THROBBER_WAITING, IDR_THROBBER_LIGHT,
171 };
172 
173 // Writes the theme pack to disk on a separate thread.
174 class WritePackToDiskTask : public Task {
175  public:
WritePackToDiskTask(BrowserThemePack * pack,const FilePath & path)176   WritePackToDiskTask(BrowserThemePack* pack, const FilePath& path)
177       : theme_pack_(pack), pack_path_(path) {}
178 
Run()179   virtual void Run() {
180     if (!theme_pack_->WriteToDisk(pack_path_)) {
181       NOTREACHED() << "Could not write theme pack to disk";
182     }
183   }
184 
185  private:
186   scoped_refptr<BrowserThemePack> theme_pack_;
187   FilePath pack_path_;
188 };
189 
190 }  // namespace
191 
IsThemeableImage(int resource_id)192 bool ThemeService::IsThemeableImage(int resource_id) {
193   return HasThemeableImage(resource_id);
194 }
195 
ThemeService()196 ThemeService::ThemeService()
197     : rb_(ResourceBundle::GetSharedInstance()),
198       profile_(NULL),
199       number_of_infobars_(0) {
200   // Initialize the themeable image map so we can use it on other threads.
201   HasThemeableImage(0);
202 }
203 
~ThemeService()204 ThemeService::~ThemeService() {
205   FreePlatformCaches();
206 }
207 
Init(Profile * profile)208 void ThemeService::Init(Profile* profile) {
209   DCHECK(CalledOnValidThread());
210   profile_ = profile;
211 
212   // Listen to EXTENSION_LOADED instead of EXTENSION_INSTALLED because
213   // the extension cannot yet be found via GetExtensionById() if it is
214   // installed but not loaded (which may confuse listeners to
215   // BROWSER_THEME_CHANGED).
216   registrar_.Add(this,
217                  NotificationType::EXTENSION_LOADED,
218                  Source<Profile>(profile_));
219 
220   LoadThemePrefs();
221 }
222 
GetBitmapNamed(int id) const223 SkBitmap* ThemeService::GetBitmapNamed(int id) const {
224   DCHECK(CalledOnValidThread());
225 
226   SkBitmap* bitmap = NULL;
227 
228   if (theme_pack_.get())
229     bitmap = theme_pack_->GetBitmapNamed(id);
230 
231   if (!bitmap)
232     bitmap = rb_.GetBitmapNamed(id);
233 
234   return bitmap;
235 }
236 
GetColor(int id) const237 SkColor ThemeService::GetColor(int id) const {
238   DCHECK(CalledOnValidThread());
239 
240   SkColor color;
241   if (theme_pack_.get() && theme_pack_->GetColor(id, &color))
242     return color;
243 
244   // For backward compat with older themes, some newer colors are generated from
245   // older ones if they are missing.
246   switch (id) {
247     case COLOR_NTP_SECTION_HEADER_TEXT:
248       return IncreaseLightness(GetColor(COLOR_NTP_TEXT), 0.30);
249     case COLOR_NTP_SECTION_HEADER_TEXT_HOVER:
250       return GetColor(COLOR_NTP_TEXT);
251     case COLOR_NTP_SECTION_HEADER_RULE:
252       return IncreaseLightness(GetColor(COLOR_NTP_TEXT), 0.70);
253     case COLOR_NTP_SECTION_HEADER_RULE_LIGHT:
254       return IncreaseLightness(GetColor(COLOR_NTP_TEXT), 0.86);
255     case COLOR_NTP_TEXT_LIGHT:
256       return IncreaseLightness(GetColor(COLOR_NTP_TEXT), 0.40);
257   }
258 
259   return GetDefaultColor(id);
260 }
261 
GetDisplayProperty(int id,int * result) const262 bool ThemeService::GetDisplayProperty(int id, int* result) const {
263   if (theme_pack_.get())
264     return theme_pack_->GetDisplayProperty(id, result);
265 
266   return GetDefaultDisplayProperty(id, result);
267 }
268 
ShouldUseNativeFrame() const269 bool ThemeService::ShouldUseNativeFrame() const {
270   if (HasCustomImage(IDR_THEME_FRAME))
271     return false;
272 #if defined(OS_WIN)
273   return views::WidgetWin::IsAeroGlassEnabled();
274 #else
275   return false;
276 #endif
277 }
278 
HasCustomImage(int id) const279 bool ThemeService::HasCustomImage(int id) const {
280   if (!HasThemeableImage(id))
281     return false;
282 
283   if (theme_pack_)
284     return theme_pack_->HasCustomImage(id);
285 
286   return false;
287 }
288 
GetRawData(int id) const289 RefCountedMemory* ThemeService::GetRawData(int id) const {
290   // Check to see whether we should substitute some images.
291   int ntp_alternate;
292   GetDisplayProperty(NTP_LOGO_ALTERNATE, &ntp_alternate);
293   if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0)
294     id = IDR_PRODUCT_LOGO_WHITE;
295 
296   RefCountedMemory* data = NULL;
297   if (theme_pack_.get())
298     data = theme_pack_->GetRawData(id);
299   if (!data)
300     data = rb_.LoadDataResourceBytes(id);
301 
302   return data;
303 }
304 
SetTheme(const Extension * extension)305 void ThemeService::SetTheme(const Extension* extension) {
306   // Clear our image cache.
307   FreePlatformCaches();
308 
309   DCHECK(extension);
310   DCHECK(extension->is_theme());
311 
312   BuildFromExtension(extension);
313   SaveThemeID(extension->id());
314 
315   NotifyThemeChanged();
316   UserMetrics::RecordAction(UserMetricsAction("Themes_Installed"), profile_);
317 }
318 
RemoveUnusedThemes()319 void ThemeService::RemoveUnusedThemes() {
320   if (!profile_)
321     return;
322   ExtensionService* service = profile_->GetExtensionService();
323   if (!service)
324     return;
325   std::string current_theme = GetThemeID();
326   std::vector<std::string> remove_list;
327   const ExtensionList* extensions = service->extensions();
328   for (ExtensionList::const_iterator it = extensions->begin();
329        it != extensions->end(); ++it) {
330     if ((*it)->is_theme() && (*it)->id() != current_theme) {
331       remove_list.push_back((*it)->id());
332     }
333   }
334   for (size_t i = 0; i < remove_list.size(); ++i)
335     service->UninstallExtension(remove_list[i], false, NULL);
336 }
337 
UseDefaultTheme()338 void ThemeService::UseDefaultTheme() {
339   ClearAllThemeData();
340   NotifyThemeChanged();
341   UserMetrics::RecordAction(UserMetricsAction("Themes_Reset"), profile_);
342 }
343 
SetNativeTheme()344 void ThemeService::SetNativeTheme() {
345   UseDefaultTheme();
346 }
347 
UsingDefaultTheme()348 bool ThemeService::UsingDefaultTheme() {
349   std::string id = GetThemeID();
350   return id == ThemeService::kDefaultThemeID ||
351       id == kDefaultThemeGalleryID;
352 }
353 
GetThemeID() const354 std::string ThemeService::GetThemeID() const {
355   return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
356 }
357 
358 // static
AlignmentToString(int alignment)359 std::string ThemeService::AlignmentToString(int alignment) {
360   // Convert from an AlignmentProperty back into a string.
361   std::string vertical_string;
362   std::string horizontal_string;
363 
364   if (alignment & ThemeService::ALIGN_TOP)
365     vertical_string = kAlignmentTop;
366   else if (alignment & ThemeService::ALIGN_BOTTOM)
367     vertical_string = kAlignmentBottom;
368 
369   if (alignment & ThemeService::ALIGN_LEFT)
370     horizontal_string = kAlignmentLeft;
371   else if (alignment & ThemeService::ALIGN_RIGHT)
372     horizontal_string = kAlignmentRight;
373 
374   if (vertical_string.empty())
375     return horizontal_string;
376   if (horizontal_string.empty())
377     return vertical_string;
378   return vertical_string + " " + horizontal_string;
379 }
380 
381 // static
StringToAlignment(const std::string & alignment)382 int ThemeService::StringToAlignment(const std::string& alignment) {
383   std::vector<std::wstring> split;
384   base::SplitStringAlongWhitespace(UTF8ToWide(alignment), &split);
385 
386   int alignment_mask = 0;
387   for (std::vector<std::wstring>::iterator alignments(split.begin());
388        alignments != split.end(); ++alignments) {
389     std::string comp = WideToUTF8(*alignments);
390     const char* component = comp.c_str();
391 
392     if (base::strcasecmp(component, kAlignmentTop) == 0)
393       alignment_mask |= ThemeService::ALIGN_TOP;
394     else if (base::strcasecmp(component, kAlignmentBottom) == 0)
395       alignment_mask |= ThemeService::ALIGN_BOTTOM;
396 
397     if (base::strcasecmp(component, kAlignmentLeft) == 0)
398       alignment_mask |= ThemeService::ALIGN_LEFT;
399     else if (base::strcasecmp(component, kAlignmentRight) == 0)
400       alignment_mask |= ThemeService::ALIGN_RIGHT;
401   }
402   return alignment_mask;
403 }
404 
405 // static
TilingToString(int tiling)406 std::string ThemeService::TilingToString(int tiling) {
407   // Convert from a TilingProperty back into a string.
408   if (tiling == ThemeService::REPEAT_X)
409     return kTilingRepeatX;
410   if (tiling == ThemeService::REPEAT_Y)
411     return kTilingRepeatY;
412   if (tiling == ThemeService::REPEAT)
413     return kTilingRepeat;
414   return kTilingNoRepeat;
415 }
416 
417 // static
StringToTiling(const std::string & tiling)418 int ThemeService::StringToTiling(const std::string& tiling) {
419   const char* component = tiling.c_str();
420 
421   if (base::strcasecmp(component, kTilingRepeatX) == 0)
422     return ThemeService::REPEAT_X;
423   if (base::strcasecmp(component, kTilingRepeatY) == 0)
424     return ThemeService::REPEAT_Y;
425   if (base::strcasecmp(component, kTilingRepeat) == 0)
426     return ThemeService::REPEAT;
427   // NO_REPEAT is the default choice.
428   return ThemeService::NO_REPEAT;
429 }
430 
431 // static
GetDefaultTint(int id)432 color_utils::HSL ThemeService::GetDefaultTint(int id) {
433   switch (id) {
434     case TINT_FRAME:
435       return kDefaultTintFrame;
436     case TINT_FRAME_INACTIVE:
437       return kDefaultTintFrameInactive;
438     case TINT_FRAME_INCOGNITO:
439       return kDefaultTintFrameIncognito;
440     case TINT_FRAME_INCOGNITO_INACTIVE:
441       return kDefaultTintFrameIncognitoInactive;
442     case TINT_BUTTONS:
443       return kDefaultTintButtons;
444     case TINT_BACKGROUND_TAB:
445       return kDefaultTintBackgroundTab;
446     default:
447       color_utils::HSL result = {-1, -1, -1};
448       return result;
449   }
450 }
451 
452 // static
GetDefaultColor(int id)453 SkColor ThemeService::GetDefaultColor(int id) {
454   switch (id) {
455     case COLOR_FRAME:
456       return kDefaultColorFrame;
457     case COLOR_FRAME_INACTIVE:
458       return kDefaultColorFrameInactive;
459     case COLOR_FRAME_INCOGNITO:
460       return kDefaultColorFrameIncognito;
461     case COLOR_FRAME_INCOGNITO_INACTIVE:
462       return kDefaultColorFrameIncognitoInactive;
463     case COLOR_TOOLBAR:
464       return kDefaultColorToolbar;
465     case COLOR_TAB_TEXT:
466       return kDefaultColorTabText;
467     case COLOR_BACKGROUND_TAB_TEXT:
468       return kDefaultColorBackgroundTabText;
469     case COLOR_BOOKMARK_TEXT:
470       return kDefaultColorBookmarkText;
471     case COLOR_NTP_BACKGROUND:
472       return kDefaultColorNTPBackground;
473     case COLOR_NTP_TEXT:
474       return kDefaultColorNTPText;
475     case COLOR_NTP_LINK:
476       return kDefaultColorNTPLink;
477     case COLOR_NTP_LINK_UNDERLINE:
478       return TintForUnderline(kDefaultColorNTPLink);
479     case COLOR_NTP_HEADER:
480       return kDefaultColorNTPHeader;
481     case COLOR_NTP_SECTION:
482       return kDefaultColorNTPSection;
483     case COLOR_NTP_SECTION_TEXT:
484       return kDefaultColorNTPSectionText;
485     case COLOR_NTP_SECTION_LINK:
486       return kDefaultColorNTPSectionLink;
487     case COLOR_NTP_SECTION_LINK_UNDERLINE:
488       return TintForUnderline(kDefaultColorNTPSectionLink);
489     case COLOR_CONTROL_BACKGROUND:
490       return kDefaultColorControlBackground;
491     case COLOR_BUTTON_BACKGROUND:
492       return kDefaultColorButtonBackground;
493 #if defined(OS_MACOSX)
494     case COLOR_TOOLBAR_BUTTON_STROKE:
495       return kDefaultColorToolbarButtonStroke;
496     case COLOR_TOOLBAR_BUTTON_STROKE_INACTIVE:
497       return kDefaultColorToolbarButtonStrokeInactive;
498     case COLOR_TOOLBAR_BEZEL:
499       return kDefaultColorToolbarBezel;
500     case COLOR_TOOLBAR_STROKE:
501       return kDefaultColorToolbarStroke;
502     case COLOR_TOOLBAR_STROKE_INACTIVE:
503       return kDefaultColorToolbarStrokeInactive;
504 #endif
505     default:
506       // Return a debugging red color.
507       return 0xffff0000;
508   }
509 }
510 
511 // static
GetDefaultDisplayProperty(int id,int * result)512 bool ThemeService::GetDefaultDisplayProperty(int id, int* result) {
513   switch (id) {
514     case NTP_BACKGROUND_ALIGNMENT:
515       *result = kDefaultDisplayPropertyNTPAlignment;
516       return true;
517     case NTP_BACKGROUND_TILING:
518       *result = kDefaultDisplayPropertyNTPTiling;
519       return true;
520     case NTP_LOGO_ALTERNATE:
521       *result = kDefaultDisplayPropertyNTPInverseLogo;
522       return true;
523   }
524 
525   return false;
526 }
527 
528 // static
GetTintableToolbarButtons()529 const std::set<int>& ThemeService::GetTintableToolbarButtons() {
530   static std::set<int> button_set;
531   if (button_set.empty()) {
532     button_set = std::set<int>(
533         kToolbarButtonIDs,
534         kToolbarButtonIDs + arraysize(kToolbarButtonIDs));
535   }
536 
537   return button_set;
538 }
539 
GetTint(int id) const540 color_utils::HSL ThemeService::GetTint(int id) const {
541   DCHECK(CalledOnValidThread());
542 
543   color_utils::HSL hsl;
544   if (theme_pack_.get() && theme_pack_->GetTint(id, &hsl))
545     return hsl;
546 
547   return GetDefaultTint(id);
548 }
549 
ClearAllThemeData()550 void ThemeService::ClearAllThemeData() {
551   // Clear our image cache.
552   FreePlatformCaches();
553   theme_pack_ = NULL;
554 
555   profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename);
556   SaveThemeID(kDefaultThemeID);
557 }
558 
LoadThemePrefs()559 void ThemeService::LoadThemePrefs() {
560   PrefService* prefs = profile_->GetPrefs();
561 
562   std::string current_id = GetThemeID();
563   if (current_id != kDefaultThemeID) {
564     bool loaded_pack = false;
565 
566     // If we don't have a file pack, we're updating from an old version.
567     FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename);
568     if (path != FilePath()) {
569       theme_pack_ = BrowserThemePack::BuildFromDataPack(path, current_id);
570       loaded_pack = theme_pack_.get() != NULL;
571     }
572 
573     if (loaded_pack) {
574       UserMetrics::RecordAction(UserMetricsAction("Themes.Loaded"), profile_);
575     } else {
576       // TODO(erg): We need to pop up a dialog informing the user that their
577       // theme is being migrated.
578       ExtensionService* service = profile_->GetExtensionService();
579       if (service) {
580         const Extension* extension =
581             service->GetExtensionById(current_id, false);
582         if (extension) {
583           DLOG(ERROR) << "Migrating theme";
584           BuildFromExtension(extension);
585           UserMetrics::RecordAction(UserMetricsAction("Themes.Migrated"),
586                                     profile_);
587         } else {
588           DLOG(ERROR) << "Theme is mysteriously gone.";
589           ClearAllThemeData();
590           UserMetrics::RecordAction(UserMetricsAction("Themes.Gone"), profile_);
591         }
592       }
593     }
594   }
595 }
596 
NotifyThemeChanged()597 void ThemeService::NotifyThemeChanged() {
598   VLOG(1) << "Sending BROWSER_THEME_CHANGED";
599   // Redraw!
600   NotificationService* service = NotificationService::current();
601   service->Notify(NotificationType::BROWSER_THEME_CHANGED,
602                   Source<ThemeService>(this),
603                   NotificationService::NoDetails());
604 #if defined(OS_MACOSX)
605   NotifyPlatformThemeChanged();
606 #endif  // OS_MACOSX
607 }
608 
609 #if defined(OS_WIN)
FreePlatformCaches()610 void ThemeService::FreePlatformCaches() {
611   // Views (Skia) has no platform image cache to clear.
612 }
613 #endif
614 
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)615 void ThemeService::Observe(NotificationType type,
616                            const NotificationSource& source,
617                            const NotificationDetails& details) {
618   DCHECK(type == NotificationType::EXTENSION_LOADED);
619   const Extension* extension = Details<const Extension>(details).ptr();
620   if (!extension->is_theme()) {
621     return;
622   }
623   SetTheme(extension);
624 }
625 
SavePackName(const FilePath & pack_path)626 void ThemeService::SavePackName(const FilePath& pack_path) {
627   profile_->GetPrefs()->SetFilePath(
628       prefs::kCurrentThemePackFilename, pack_path);
629 }
630 
SaveThemeID(const std::string & id)631 void ThemeService::SaveThemeID(const std::string& id) {
632   profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
633 }
634 
BuildFromExtension(const Extension * extension)635 void ThemeService::BuildFromExtension(const Extension* extension) {
636   scoped_refptr<BrowserThemePack> pack(
637       BrowserThemePack::BuildFromExtension(extension));
638   if (!pack.get()) {
639     // TODO(erg): We've failed to install the theme; perhaps we should tell the
640     // user? http://crbug.com/34780
641     LOG(ERROR) << "Could not load theme.";
642     return;
643   }
644 
645   // Write the packed file to disk.
646   FilePath pack_path = extension->path().Append(chrome::kThemePackFilename);
647   BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
648                           new WritePackToDiskTask(pack, pack_path));
649 
650   SavePackName(pack_path);
651   theme_pack_ = pack;
652 }
653 
OnInfobarDisplayed()654 void ThemeService::OnInfobarDisplayed() {
655   number_of_infobars_++;
656 }
657 
OnInfobarDestroyed()658 void ThemeService::OnInfobarDestroyed() {
659   number_of_infobars_--;
660 
661   if (number_of_infobars_ == 0)
662     RemoveUnusedThemes();
663 }
664