Home | History | Annotate | Download | only in themes
      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 
     50 SkColor TintForUnderline(SkColor input) {
     51   return SkColorSetA(input, SkColorGetA(input) / 3);
     52 }
     53 
     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 
    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:
    176   WritePackToDiskTask(BrowserThemePack* pack, const FilePath& path)
    177       : theme_pack_(pack), pack_path_(path) {}
    178 
    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 
    192 bool ThemeService::IsThemeableImage(int resource_id) {
    193   return HasThemeableImage(resource_id);
    194 }
    195 
    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 
    204 ThemeService::~ThemeService() {
    205   FreePlatformCaches();
    206 }
    207 
    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 
    223 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 
    237 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 
    262 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 
    269 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 
    279 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 
    289 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 
    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 
    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 
    338 void ThemeService::UseDefaultTheme() {
    339   ClearAllThemeData();
    340   NotifyThemeChanged();
    341   UserMetrics::RecordAction(UserMetricsAction("Themes_Reset"), profile_);
    342 }
    343 
    344 void ThemeService::SetNativeTheme() {
    345   UseDefaultTheme();
    346 }
    347 
    348 bool ThemeService::UsingDefaultTheme() {
    349   std::string id = GetThemeID();
    350   return id == ThemeService::kDefaultThemeID ||
    351       id == kDefaultThemeGalleryID;
    352 }
    353 
    354 std::string ThemeService::GetThemeID() const {
    355   return profile_->GetPrefs()->GetString(prefs::kCurrentThemeID);
    356 }
    357 
    358 // static
    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
    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
    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
    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
    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
    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
    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
    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 
    540 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 
    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 
    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 
    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)
    610 void ThemeService::FreePlatformCaches() {
    611   // Views (Skia) has no platform image cache to clear.
    612 }
    613 #endif
    614 
    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 
    626 void ThemeService::SavePackName(const FilePath& pack_path) {
    627   profile_->GetPrefs()->SetFilePath(
    628       prefs::kCurrentThemePackFilename, pack_path);
    629 }
    630 
    631 void ThemeService::SaveThemeID(const std::string& id) {
    632   profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, id);
    633 }
    634 
    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 
    654 void ThemeService::OnInfobarDisplayed() {
    655   number_of_infobars_++;
    656 }
    657 
    658 void ThemeService::OnInfobarDestroyed() {
    659   number_of_infobars_--;
    660 
    661   if (number_of_infobars_ == 0)
    662     RemoveUnusedThemes();
    663 }
    664