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