diff options
Diffstat (limited to 'chrome/browser/browser_theme_provider.cc')
-rw-r--r-- | chrome/browser/browser_theme_provider.cc | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/chrome/browser/browser_theme_provider.cc b/chrome/browser/browser_theme_provider.cc new file mode 100644 index 0000000..cb68ed0c --- /dev/null +++ b/chrome/browser/browser_theme_provider.cc @@ -0,0 +1,527 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "browser_theme_provider.h" + +#include "base/gfx/png_decoder.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/extensions/extension.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/theme_resources_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "grit/theme_resources.h" +#include "net/base/file_stream.h" +#include "net/base/net_errors.h" +#include "skia/ext/image_operations.h" +#include "skia/ext/skia_utils.h" +#include "SkBitmap.h" + +// Strings used by themes to identify colors for different parts of our UI. +static const std::string kColorFrame = "frame"; +static const std::string kColorFrameInactive = "frame_inactive"; +static const std::string kColorFrameIncognito = "frame_incognito"; +static const std::string kColorFrameIncognitoInactive = + "frame_incognito_inactive"; +static const std::string kColorToolbar = "toolbar"; +static const std::string kColorTabText = "tab_text"; +static const std::string kColorBackgroundTabText = "background_tab_text"; +static const std::string kColorBookmarkText = "bookmark_text"; +static const std::string kColorNTPText = "ntp_text"; +static const std::string kColorNTPLink = "ntp_link"; +static const std::string kColorNTPSection = "ntp_section"; + +// Strings used by themes to identify tints to apply to different parts of +// our UI. The frame tints apply to the frame color and produce the +// COLOR_FRAME* colors. +static const std::string kTintButtons = "buttons"; +static const std::string kTintFrame = "frame"; +static const std::string kTintFrameInactive = "frame_inactive"; +static const std::string kTintFrameIncognito = "frame_incognito"; +static const std::string kTintFrameIncognitoInactive = + "frame_incognito_inactive"; +static const std::string kTintBackgroundTab = "background_tab"; + +// Default colors. +static const SkColor kDefaultColorFrame = SkColorSetRGB(77, 139, 217); +static const SkColor kDefaultColorFrameInactive = SkColorSetRGB(152, 188, 233); +static const SkColor kDefaultColorFrameIncognito = SkColorSetRGB(83, 106, 139); +static const SkColor kDefaultColorFrameIncognitoInactive = + SkColorSetRGB(126, 139, 156); +static const SkColor kDefaultColorToolbar = SkColorSetRGB(210, 225, 246); +static const SkColor kDefaultColorTabText = SkColorSetRGB(0, 0, 0); +static const SkColor kDefaultColorBackgroundTabText = SkColorSetRGB(64, 64, 64); + +// Default tints. +static const skia::HSL kDefaultTintButtons = { -1, -1, -1 }; +static const skia::HSL kDefaultTintFrame = { -1, -1, -1 }; +static const skia::HSL kDefaultTintFrameInactive = { -1, 0.5f, 0.72f }; +static const skia::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f }; +static const skia::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f }; +static const skia::HSL kDefaultTintBackgroundTab = { -1, 0.5, 0.75 }; + +// The image resources that will be tinted by the 'button' tint value. +static const int kToolbarButtonIDs[] = { + IDR_BACK, IDR_BACK_D, IDR_BACK_H, IDR_BACK_P, + IDR_FORWARD, IDR_FORWARD_D, IDR_FORWARD_H, IDR_FORWARD_P, + IDR_RELOAD, IDR_RELOAD_H, IDR_RELOAD_P, + IDR_HOME, IDR_HOME_H, IDR_HOME_P, + IDR_STAR, IDR_STAR_D, IDR_STAR_H, IDR_STAR_P, + IDR_STARRED, IDR_STARRED_H, IDR_STARRED_P, + IDR_GO, IDR_GO_H, IDR_GO_P, + IDR_STOP, IDR_STOP_H, IDR_STOP_P, + IDR_MENU_PAGE, IDR_MENU_PAGE_RTL, + IDR_MENU_CHROME, IDR_MENU_CHROME_RTL, + IDR_MENU_DROPARROW, + IDR_THROBBER, +}; + +// A map for kToolbarButtonIDs. +static std::map<const int, bool> button_images_; + +// A map of frame image IDs to the tints for those ids. +static std::map<const int, int> frame_tints_; + +BrowserThemeProvider::BrowserThemeProvider() + : rb_(ResourceBundle::GetSharedInstance()) { + static bool initialized = false; + if (!initialized) { + for (int i = 0; i < sizeof(kToolbarButtonIDs); ++i) { + button_images_[kToolbarButtonIDs[i]] = true; + } + frame_tints_[IDR_THEME_FRAME] = TINT_FRAME; + frame_tints_[IDR_THEME_FRAME_INACTIVE] = TINT_FRAME_INACTIVE; + frame_tints_[IDR_THEME_FRAME_INCOGNITO] = TINT_FRAME_INCOGNITO; + frame_tints_[IDR_THEME_FRAME_INCOGNITO_INACTIVE] = + TINT_FRAME_INCOGNITO_INACTIVE; + } +} + +BrowserThemeProvider::~BrowserThemeProvider() { } + +void BrowserThemeProvider::Init(Profile* profile) { + profile_ = profile; + LoadThemePrefs(); +} + +SkBitmap* BrowserThemeProvider::GetBitmapNamed(int id) { + // Check to see if we already have the Skia image in the cache. + ImageCache::const_iterator found = image_cache_.find(id); + if (found != image_cache_.end()) + return found->second; + + scoped_ptr<SkBitmap> result; + + // Try to load the image from the extension. + result.reset(LoadThemeBitmap(id)); + + // If the extension doesn't provide the requested image, but has provided + // a custom frame, then we may be able to generate the image required. + if (!result.get()) + result.reset(GenerateBitmap(id)); + + // If we still don't have an image, load it from resourcebundle. + if (!result.get()) + result.reset(rb_.GetBitmapNamed(id)); + + // If the requested image is part of the toolbar button set, and we have + // a provided tint for that set, tint it appropriately. + if (button_images_.count(id) && tints_.count(kTintButtons)) { + SkBitmap* tinted = + new SkBitmap(TintBitmap(*result.release(), TINT_BUTTONS)); + result.reset(tinted); + } + + if (result.get()) { + // We loaded successfully. Cache the bitmap. + image_cache_[id] = result.get(); + return result.release(); + } else { + NOTREACHED() << "Failed to load a requested image"; + return NULL; + } +} + +SkColor BrowserThemeProvider::GetColor(int id) { + // TODO(glen): Figure out if we need to tint these. http://crbug.com/11578 + switch (id) { + case COLOR_FRAME: + return (colors_.find(kColorFrame) != colors_.end()) ? + colors_[kColorFrame] : kDefaultColorFrame; + case COLOR_FRAME_INACTIVE: + return (colors_.find(kColorFrameInactive) != colors_.end()) ? + colors_[kColorFrameInactive] : kDefaultColorFrameInactive; + case COLOR_FRAME_INCOGNITO: + return (colors_.find(kColorFrameIncognito) != colors_.end()) ? + colors_[kColorFrameIncognito] : kDefaultColorFrameIncognito; + case COLOR_FRAME_INCOGNITO_INACTIVE: + return (colors_.find(kColorFrameIncognitoInactive) != colors_.end()) ? + colors_[kColorFrameIncognitoInactive] : + kDefaultColorFrameIncognitoInactive; + case COLOR_TOOLBAR: + return (colors_.find(kColorToolbar) != colors_.end()) ? + colors_[kColorToolbar] : kDefaultColorToolbar; + case COLOR_TAB_TEXT: + return (colors_.find(kColorTabText) != colors_.end()) ? + colors_[kColorTabText] : kDefaultColorTabText; + case COLOR_BACKGROUND_TAB_TEXT: + return (colors_.find(kColorBackgroundTabText) != colors_.end()) ? + colors_[kColorBackgroundTabText] : + kDefaultColorBackgroundTabText; + default: + NOTREACHED() << "Unknown color requested"; + } + + // Return a debugging red color. + return 0xffff0000; +} + +void BrowserThemeProvider::SetTheme(Extension* extension) { + // Clear our image cache. + image_cache_.clear(); + + DCHECK(extension); + DCHECK(extension->IsTheme()); + SetImageData(extension->GetThemeImages(), + extension->path()); + SetColorData(extension->GetThemeColors()); + SetTintData(extension->GetThemeTints()); + GenerateFrameColors(); + GenerateFrameImages(); + + SaveImageData(extension->GetThemeImages()); + SaveColorData(); + SaveTintData(); + + NotifyThemeChanged(); + UserMetrics::RecordAction(L"Themes_Installed", profile_); +} + +void BrowserThemeProvider::UseDefaultTheme() { + // Clear our image cache. + image_cache_.clear(); + + images_.clear(); + colors_.clear(); + tints_.clear(); + + SaveImageData(NULL); + SaveColorData(); + SaveTintData(); + + NotifyThemeChanged(); +} + +SkBitmap* BrowserThemeProvider::LoadThemeBitmap(int id) { + // Attempt to find the image in our theme bundle. + std::vector<unsigned char> raw_data, png_data; + if (images_.count(id)) { + // First check to see if we have a registered theme extension and whether + // it can handle this resource. + FilePath path = FilePath(UTF8ToWide(images_[id])); + if (!path.empty()) { + net::FileStream file; + int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; + if (file.Open(path, flags) == net::OK) { + int64 avail = file.Available(); + if (avail > 0 && avail < INT_MAX) { + size_t size = static_cast<size_t>(avail); + raw_data.resize(size); + char* data = reinterpret_cast<char*>(&(raw_data.front())); + if (file.ReadUntilComplete(data, size) == avail) { + // Decode the PNG. + int image_width = 0; + int image_height = 0; + + if (!PNGDecoder::Decode(&raw_data.front(), raw_data.size(), + PNGDecoder::FORMAT_BGRA, &png_data, + &image_width, &image_height)) { + NOTREACHED() << "Unable to decode theme image resource " << id; + return NULL; + } + + return PNGDecoder::CreateSkBitmapFromBGRAFormat(png_data, + image_width, + image_height); + } + } + } else { + // TODO(glen): File no-longer exists, we're out of date. We should + // clear the theme (or maybe just the pref that points to this + // image). + return NULL; + } + } + } + + return NULL; +} + +skia::HSL BrowserThemeProvider::GetTint(int id) { + switch (id) { + case TINT_FRAME: + return (tints_.find(kTintFrame) != tints_.end()) ? + tints_[kTintFrame] : kDefaultTintFrame; + case TINT_FRAME_INACTIVE: + return (tints_.find(kTintFrameInactive) != tints_.end()) ? + tints_[kTintFrameInactive] : kDefaultTintFrameInactive; + case TINT_FRAME_INCOGNITO: + return (tints_.count(kTintFrameIncognito)) ? + tints_[kTintFrameIncognito] : kDefaultTintFrameIncognito; + case TINT_FRAME_INCOGNITO_INACTIVE: + return (tints_.count(kTintFrameIncognitoInactive)) ? + tints_[kTintFrameIncognitoInactive] : + kDefaultTintFrameIncognitoInactive; + case TINT_BUTTONS: + return (tints_.find(kTintButtons) != tints_.end()) ? + tints_[kTintButtons] : + kDefaultTintButtons; + case TINT_BACKGROUND_TAB: + return (tints_.find(kTintBackgroundTab) != tints_.end()) ? + tints_[kTintBackgroundTab] : + kDefaultTintBackgroundTab; + default: + NOTREACHED() << "Unknown tint requested"; + } + skia::HSL result = {-1, -1, -1}; + return result; +} + +SkBitmap BrowserThemeProvider::TintBitmap(const SkBitmap& bitmap, int hsl_id) { + return skia::ImageOperations::CreateHSLShiftedBitmap(bitmap, GetTint(hsl_id)); +} + +void BrowserThemeProvider::SetImageData(DictionaryValue* images_value, + FilePath images_path) { + images_.clear(); + + if (images_value) { + DictionaryValue::key_iterator iter = images_value->begin_keys(); + while (iter != images_value->end_keys()) { + std::string val; + if (images_value->GetString(*iter, &val)) { + int id = ThemeResourcesUtil::GetId(WideToUTF8(*iter)); + if (id != -1) { + if (!images_path.empty()) { + images_[id] = WideToUTF8(images_path.AppendASCII(val) + .ToWStringHack()); + } else { + images_[id] = val; + } + } + } + ++iter; + } + } +} + +void BrowserThemeProvider::SetColorData(DictionaryValue* colors_value) { + colors_.clear(); + + if (colors_value) { + DictionaryValue::key_iterator iter = colors_value->begin_keys(); + while (iter != colors_value->end_keys()) { + ListValue* color_list; + if (colors_value->GetList(*iter, &color_list) && + color_list->GetSize() == 3) { + int r, g, b; + color_list->GetInteger(0, &r); + color_list->GetInteger(1, &g); + color_list->GetInteger(2, &b); + colors_[WideToUTF8(*iter)] = SkColorSetRGB(r, g, b); + } + ++iter; + } + } +} + +void BrowserThemeProvider::SetTintData(DictionaryValue* tints_value) { + tints_.clear(); + + if (tints_value) { + DictionaryValue::key_iterator iter = tints_value->begin_keys(); + while (iter != tints_value->end_keys()) { + ListValue* tint_list; + if (tints_value->GetList(*iter, &tint_list) && + tint_list->GetSize() == 3) { + skia::HSL hsl = { -1, -1, -1 }; + // TODO(glen): Make this work with integer values. + tint_list->GetReal(0, &hsl.h); + tint_list->GetReal(1, &hsl.s); + tint_list->GetReal(2, &hsl.l); + tints_[WideToUTF8(*iter)] = hsl; + } + ++iter; + } + } +} + +void BrowserThemeProvider::GenerateFrameColors() { + // Generate any secondary frame colors that weren't provided. + skia::HSL frame_hsl = { 0, 0, 0 }; + skia::SkColorToHSL(GetColor(COLOR_FRAME), frame_hsl); + + if (colors_.find(kColorFrame) == colors_.end()) + colors_[kColorFrame] = HSLShift(frame_hsl, GetTint(TINT_FRAME)); + if (colors_.find(kColorFrameInactive) == colors_.end()) { + colors_[kColorFrameInactive] = + HSLShift(frame_hsl, GetTint(TINT_FRAME_INACTIVE)); + } + if (colors_.find(kColorFrameIncognito) == colors_.end()) { + colors_[kColorFrameIncognito] = + HSLShift(frame_hsl, GetTint(TINT_FRAME_INCOGNITO)); + } + if (colors_.find(kColorFrameIncognitoInactive) == colors_.end()) { + colors_[kColorFrameIncognitoInactive] = + HSLShift(frame_hsl, GetTint(TINT_FRAME_INCOGNITO_INACTIVE)); + } +} + +void BrowserThemeProvider::GenerateFrameImages() { + std::map<const int, int>::iterator iter = frame_tints_.begin(); + while (iter != frame_tints_.end()) { + int id = iter->first; + SkBitmap* frame; + // If there's no frame image provided for the specified id, then load + // the default provided frame. If that's not provided, skip this whole + // thing and just use the default images. + int base_id = (id == IDR_THEME_FRAME_INCOGNITO || + id == IDR_THEME_FRAME_INCOGNITO_INACTIVE) ? + IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; + + if (images_.find(id) != images_.end()) { + frame = LoadThemeBitmap(id); + } else if (base_id != id && images_.find(base_id) != images_.end()) { + frame = LoadThemeBitmap(base_id); + } else { + if (id == IDR_THEME_FRAME_INCOGNITO || + id == IDR_THEME_FRAME_INCOGNITO_INACTIVE) + frame = rb_.GetBitmapNamed(IDR_THEME_FRAME_INCOGNITO); + else + frame = rb_.GetBitmapNamed(IDR_THEME_FRAME); + } + + if (frame) { + SkBitmap* tinted = new SkBitmap(TintBitmap(*frame, frame_tints_[id])); + image_cache_[id] = tinted; + } + ++iter; + } +} + +SkBitmap* BrowserThemeProvider::GenerateBitmap(int id) { + if (id == IDR_THEME_TAB_BACKGROUND || + id == IDR_THEME_TAB_BACKGROUND_INCOGNITO) { + // The requested image is a background tab. Get a frame to create the + // tab against. As themes don't use the glass frame, we don't have to + // worry about compositing them together, as our default theme provides + // the necessary bitmaps. + SkBitmap* frame; + if (id == IDR_THEME_TAB_BACKGROUND) + frame = image_cache_.find(IDR_THEME_FRAME)->second; + else + frame = image_cache_.find(IDR_THEME_FRAME_INCOGNITO)->second; + + if (frame) { + SkBitmap blurred = + skia::ImageOperations::CreateBlurredBitmap(*frame, 5); + SkBitmap* bg_tab = + new SkBitmap(TintBitmap(blurred, TINT_BACKGROUND_TAB)); + return bg_tab; + } + } + + return NULL; +} + +void BrowserThemeProvider::NotifyThemeChanged() { + // TODO(glen): If we're in glass and IDR_THEME_FRAME has been provided, + // swap us back to opaque frame. + + // Redraw! + for (BrowserList::const_iterator browser = BrowserList::begin(); + browser != BrowserList::end(); ++browser) { + (*browser)->window()->UserChangedTheme(); + } +} + +void BrowserThemeProvider::LoadThemePrefs() { + PrefService* prefs = profile_->GetPrefs(); + + // Our prefs already have the extension path baked in, so we don't need + // to provide it. + SetImageData(prefs->GetMutableDictionary(prefs::kCurrentThemeImages), + FilePath()); + SetColorData(prefs->GetMutableDictionary(prefs::kCurrentThemeColors)); + SetTintData(prefs->GetMutableDictionary(prefs::kCurrentThemeTints)); + GenerateFrameColors(); + GenerateFrameImages(); + + // TODO(glen): Figure out if any custom prefs were loaded, and if so + // UMA-log the fact that a theme was loaded. + if (prefs->HasPrefPath(prefs::kCurrentThemeImages) || + prefs->HasPrefPath(prefs::kCurrentThemeColors) || + prefs->HasPrefPath(prefs::kCurrentThemeTints)) { + UserMetrics::RecordAction(L"Themes_loaded", profile_); + } +} + +void BrowserThemeProvider::SaveImageData(DictionaryValue* images_value) { + // Save our images data. + DictionaryValue* pref_images = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeImages); + pref_images->Clear(); + + DictionaryValue::key_iterator iter = images_value->begin_keys(); + while (iter != images_value->end_keys()) { + std::string val; + if (images_value->GetString(*iter, &val)) { + int id = ThemeResourcesUtil::GetId(WideToUTF8(*iter)); + if (id != -1) + pref_images->SetString(*iter, images_[id]); + } + ++iter; + } +} + +void BrowserThemeProvider::SaveColorData() { + // Save our color data. + DictionaryValue* pref_colors = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeColors); + pref_colors->Clear(); + if (colors_.size()) { + ColorMap::iterator iter = colors_.begin(); + while (iter != colors_.end()) { + SkColor rgb = (*iter).second; + ListValue* rgb_list = new ListValue(); + rgb_list->Set(0, Value::CreateIntegerValue(SkColorGetR(rgb))); + rgb_list->Set(1, Value::CreateIntegerValue(SkColorGetG(rgb))); + rgb_list->Set(2, Value::CreateIntegerValue(SkColorGetB(rgb))); + pref_colors->Set(UTF8ToWide((*iter).first), rgb_list); + ++iter; + } + } +} + +void BrowserThemeProvider::SaveTintData() { + // Save our tint data. + DictionaryValue* pref_tints = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeTints); + pref_tints->Clear(); + if (tints_.size()) { + TintMap::iterator iter = tints_.begin(); + while (iter != tints_.end()) { + skia::HSL hsl = (*iter).second; + ListValue* hsl_list = new ListValue(); + hsl_list->Set(0, Value::CreateRealValue(hsl.h)); + hsl_list->Set(1, Value::CreateRealValue(hsl.s)); + hsl_list->Set(2, Value::CreateRealValue(hsl.l)); + pref_tints->Set(UTF8ToWide((*iter).first), hsl_list); + ++iter; + } + } +} |