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