diff options
author | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-11 22:09:55 +0000 |
---|---|---|
committer | erg@google.com <erg@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-11 22:09:55 +0000 |
commit | 75cbb35fa7fb6ce0a511612ec91998c053247be8 (patch) | |
tree | 5abee27b2c5f2c60e5017aa6c00b43d9fe2376f4 /chrome | |
parent | 9fa8c2f5e9581c2e521fd28aadedc4c77b9b2150 (diff) | |
download | chromium_src-75cbb35fa7fb6ce0a511612ec91998c053247be8.zip chromium_src-75cbb35fa7fb6ce0a511612ec91998c053247be8.tar.gz chromium_src-75cbb35fa7fb6ce0a511612ec91998c053247be8.tar.bz2 |
Revert "Completely redo how themes are stored on disk and processed at install
time," as it fails valgrind tests.
This reverts commit 86faccd1028937a69ccc718718fd48c06c0cd471 (r34379).
Review URL: http://codereview.chromium.org/490025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34385 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
28 files changed, 1541 insertions, 1757 deletions
diff --git a/chrome/browser/browser_theme_pack.cc b/chrome/browser/browser_theme_pack.cc deleted file mode 100644 index f30bd6b..0000000 --- a/chrome/browser/browser_theme_pack.cc +++ /dev/null @@ -1,864 +0,0 @@ -// 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 "chrome/browser/browser_theme_pack.h" - -#include <climits> - -#include "app/gfx/codec/png_codec.h" -#include "app/gfx/skbitmap_operations.h" -#include "base/data_pack.h" -#include "base/logging.h" -#include "base/stl_util-inl.h" -#include "base/string_util.h" -#include "base/values.h" -#include "chrome/browser/browser_theme_provider.h" -#include "chrome/browser/chrome_thread.h" -#include "chrome/browser/theme_resources_util.h" -#include "chrome/common/extensions/extension.h" -#include "grit/app_resources.h" -#include "grit/theme_resources.h" -#include "net/base/file_stream.h" -#include "net/base/net_errors.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkUnPreMultiply.h" - -namespace { - -// Version number of the current theme pack. We just throw out and rebuild -// theme packs that aren't int-equal to this. -const int kThemePackVersion = 1; - -// IDs that are in the DataPack won't clash with the positive integer -// int32_t. kHeaderID should always have the maximum value because we want the -// "header" to be written last. That way we can detect whether the pack was -// successfully written and ignore and regenerate if it was only partially -// written (i.e. chrome crashed on a different thread while writing the pack). -const int kHeaderID = UINT_MAX - 1; -const int kTintsID = UINT_MAX - 2; -const int kColorsID = UINT_MAX - 3; -const int kDisplayPropertiesID = UINT_MAX - 4; - -// Static size of the tint/color/display property arrays that are mmapped. -const int kTintArraySize = 6; -const int kColorArraySize = 19; -const int kDisplayPropertySize = 3; - -// The sum of kFrameBorderThickness and kNonClientRestoredExtraThickness from -// OpaqueBrowserFrameView. -const int kRestoredTabVerticalOffset = 15; - - -struct StringToIntTable { - const char* key; - int id; -}; - -// Strings used by themes to identify tints in the JSON. -StringToIntTable kTintTable[] = { - { "buttons", BrowserThemeProvider::TINT_BUTTONS }, - { "frame", BrowserThemeProvider::TINT_FRAME }, - { "frame_inactive", BrowserThemeProvider::TINT_FRAME_INACTIVE }, - { "frame_incognito_inactive", - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE }, - { "background_tab", BrowserThemeProvider::TINT_BACKGROUND_TAB }, - { NULL, 0 } -}; - -// Strings used by themes to identify colors in the JSON. -StringToIntTable kColorTable[] = { - { "frame", BrowserThemeProvider::COLOR_FRAME }, - { "frame_inactive", BrowserThemeProvider::COLOR_FRAME_INACTIVE }, - { "frame_incognito", BrowserThemeProvider::COLOR_FRAME_INCOGNITO }, - { "frame_incognito_inactive", - BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE }, - { "toolbar", BrowserThemeProvider::COLOR_TOOLBAR }, - { "tab_text", BrowserThemeProvider::COLOR_TAB_TEXT }, - { "tab_background_text", BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT }, - { "bookmark_text", BrowserThemeProvider::COLOR_BOOKMARK_TEXT }, - { "ntp_background", BrowserThemeProvider::COLOR_NTP_BACKGROUND }, - { "ntp_text", BrowserThemeProvider::COLOR_NTP_TEXT }, - { "ntp_link", BrowserThemeProvider::COLOR_NTP_LINK }, - { "ntp_link_underline", BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE }, - { "ntp_header", BrowserThemeProvider::COLOR_NTP_HEADER }, - { "ntp_section", BrowserThemeProvider::COLOR_NTP_SECTION }, - { "ntp_section_text", BrowserThemeProvider::COLOR_NTP_SECTION_TEXT }, - { "ntp_section_link", BrowserThemeProvider::COLOR_NTP_SECTION_LINK }, - { "ntp_section_link_underline", - BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE }, - { "control_background", BrowserThemeProvider::COLOR_CONTROL_BACKGROUND }, - { "button_background", BrowserThemeProvider::COLOR_BUTTON_BACKGROUND }, - { NULL, 0 } -}; - -// Strings used by themes to identify display properties keys in JSON. -StringToIntTable kDisplayProperties[] = { - { "ntp_background_alignment", - BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT }, - { "ntp_background_repeat", BrowserThemeProvider::NTP_BACKGROUND_TILING }, - { "ntp_logo_alternate", BrowserThemeProvider::NTP_LOGO_ALTERNATE }, - { NULL, 0 } -}; - -// Strings used by the tiling values in JSON. -StringToIntTable kTilingStrings[] = { - { "no-repeat", BrowserThemeProvider::NO_REPEAT }, - { "repeat-x", BrowserThemeProvider::REPEAT_X }, - { "repeat-y", BrowserThemeProvider::REPEAT_Y }, - { "repeat", BrowserThemeProvider::REPEAT }, - { NULL, 0 } -}; - -int GetIntForString(const std::string& key, StringToIntTable* table) { - for (int i = 0; table[i].key != NULL; ++i) { - if (base::strcasecmp(key.c_str(), table[i].key) == 0) { - return table[i].id; - } - } - - return -1; -} - -struct IntToIntTable { - int key; - int value; -}; - -// Mapping used in GenerateFrameImages() to associate frame images with the -// tint ID that should maybe be applied to it. -IntToIntTable kFrameTintMap[] = { - { IDR_THEME_FRAME, BrowserThemeProvider::TINT_FRAME }, - { IDR_THEME_FRAME_INACTIVE, BrowserThemeProvider::TINT_FRAME_INACTIVE }, - { IDR_THEME_FRAME_OVERLAY, BrowserThemeProvider::TINT_FRAME }, - { IDR_THEME_FRAME_OVERLAY_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INACTIVE }, - { IDR_THEME_FRAME_INCOGNITO, BrowserThemeProvider::TINT_FRAME_INCOGNITO }, - { IDR_THEME_FRAME_INCOGNITO_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE } -}; - -// Mapping used in GenerateTabBackgroundImages() to associate what frame image -// goes with which tab background. -IntToIntTable kTabBackgroundMap[] = { - { IDR_THEME_TAB_BACKGROUND, IDR_THEME_FRAME }, - { IDR_THEME_TAB_BACKGROUND_INCOGNITO, IDR_THEME_FRAME_INCOGNITO } -}; - -// A list of images that don't need tinting or any other modification and can -// be byte-copied directly into the finished DataPack. This should contain all -// themeable image IDs that aren't in kFrameTintMap or kTabBackgroundMap. -const int kPreloadIDs[] = { - IDR_THEME_TOOLBAR, - IDR_THEME_NTP_BACKGROUND, - IDR_THEME_BUTTON_BACKGROUND, - IDR_THEME_NTP_ATTRIBUTION, - IDR_THEME_WINDOW_CONTROL_BACKGROUND -}; - -// The image resources that will be tinted by the 'button' tint value. -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_NOBORDER, IDR_STAR_NOBORDER_CENTER, IDR_STAR_D, IDR_STAR_H, - IDR_STAR_P, - IDR_STARRED, IDR_STARRED_NOBORDER, IDR_STARRED_NOBORDER_CENTER, IDR_STARRED_H, - IDR_STARRED_P, - IDR_GO, IDR_GO_NOBORDER, IDR_GO_NOBORDER_CENTER, IDR_GO_H, IDR_GO_P, - IDR_STOP, IDR_STOP_NOBORDER, IDR_STOP_NOBORDER_CENTER, IDR_STOP_H, IDR_STOP_P, - IDR_MENU_BOOKMARK, - IDR_MENU_PAGE, IDR_MENU_PAGE_RTL, - IDR_MENU_CHROME, IDR_MENU_CHROME_RTL, - IDR_MENU_DROPARROW, - IDR_THROBBER, IDR_THROBBER_WAITING, IDR_THROBBER_LIGHT, - IDR_LOCATIONBG -}; - -// Returns a piece of memory with the contents of the file |path|. -RefCountedMemory* ReadFileData(const FilePath& path) { - 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); - std::vector<unsigned char> raw_data; - raw_data.resize(size); - char* data = reinterpret_cast<char*>(&(raw_data.front())); - if (file.ReadUntilComplete(data, size) == avail) - return RefCountedBytes::TakeVector(&raw_data); - } - } - } - - return NULL; -} - -// Does error checking for invalid incoming data while trying to read an -// floaing point value. -bool ValidRealValue(ListValue* tint_list, int index, double* out) { - if (tint_list->GetReal(index, out)) - return true; - - int value = 0; - if (tint_list->GetInteger(index, &value)) { - *out = value; - return true; - } - - return false; -} - -} // namespace - -BrowserThemePack::~BrowserThemePack() { - if (!data_pack_.get()) { - delete header_; - delete [] tints_; - delete [] colors_; - delete [] display_properties_; - } - - STLDeleteValues(&image_cache_); -} - -// static -BrowserThemePack* BrowserThemePack::BuildFromExtension(Extension* extension) { - DCHECK(extension); - DCHECK(extension->IsTheme()); - - BrowserThemePack* pack = new BrowserThemePack; - pack->BuildHeader(extension); - pack->BuildTintsFromJSON(extension->GetThemeTints()); - pack->BuildColorsFromJSON(extension->GetThemeColors()); - pack->BuildDisplayPropertiesFromJSON(extension->GetThemeDisplayProperties()); - - // Builds the images. (Image building is dependent on tints). - std::map<int, FilePath> file_paths; - pack->ParseImageNamesFromJSON(extension->GetThemeImages(), - extension->path(), - &file_paths); - - pack->LoadRawBitmapsTo(file_paths, &pack->image_cache_); - - pack->GenerateFrameImages(&pack->image_cache_); - -#if !defined(OS_MACOSX) - // OSX uses its own special buttons that are PDFs that do odd sorts of vector - // graphics tricks. Other platforms use bitmaps and we must pre-tint them. - pack->GenerateTintedButtons( - pack->GetTintInternal(BrowserThemeProvider::TINT_BUTTONS), - &pack->image_cache_); -#endif - - pack->GenerateTabBackgroundImages(&pack->image_cache_); - - // Repack all the images from |image_cache_| into |image_memory_| for - // writing to the data pack - pack->RepackImageCacheToImageMemory(); - - // The BrowserThemePack is now in a consistent state. - return pack; -} - -// static -scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromDataPack( - FilePath path, const std::string& expected_id) { - scoped_refptr<BrowserThemePack> pack = new BrowserThemePack; - pack->data_pack_.reset(new base::DataPack); - - if (!pack->data_pack_->Load(path)) { - LOG(ERROR) << "Failed to load theme data pack."; - return NULL; - } - - base::StringPiece pointer; - if (!pack->data_pack_->GetStringPiece(kHeaderID, &pointer)) - return NULL; - pack->header_ = reinterpret_cast<BrowserThemePackHeader*>(const_cast<char*>( - pointer.data())); - - if (pack->header_->version != kThemePackVersion) - return NULL; - // TODO(erg): Check endianess once DataPack works on the other endian. - std::string theme_id(reinterpret_cast<char*>(pack->header_->theme_id), - Extension::kIdSize); - std::string truncated_id = expected_id.substr(0, Extension::kIdSize); - if (theme_id != truncated_id) { - DLOG(ERROR) << "Wrong id: " << theme_id << " vs " << expected_id; - return NULL; - } - - if (!pack->data_pack_->GetStringPiece(kTintsID, &pointer)) - return NULL; - pack->tints_ = reinterpret_cast<TintEntry*>(const_cast<char*>( - pointer.data())); - - if (!pack->data_pack_->GetStringPiece(kColorsID, &pointer)) - return NULL; - pack->colors_ = - reinterpret_cast<ColorPair*>(const_cast<char*>(pointer.data())); - - if (!pack->data_pack_->GetStringPiece(kDisplayPropertiesID, &pointer)) - return NULL; - pack->display_properties_ = reinterpret_cast<DisplayPropertyPair*>( - const_cast<char*>(pointer.data())); - - return pack; -} - -bool BrowserThemePack::WriteToDisk(FilePath path) const { - std::map<uint32, base::StringPiece> resources; - - resources[kHeaderID] = base::StringPiece( - reinterpret_cast<const char*>(header_), sizeof(BrowserThemePackHeader)); - resources[kTintsID] = base::StringPiece( - reinterpret_cast<const char*>(tints_), sizeof(TintEntry[kTintArraySize])); - resources[kColorsID] = base::StringPiece( - reinterpret_cast<const char*>(colors_), - sizeof(ColorPair[kColorArraySize])); - resources[kDisplayPropertiesID] = base::StringPiece( - reinterpret_cast<const char*>(display_properties_), - sizeof(DisplayPropertyPair[kDisplayPropertySize])); - - for (RawImages::const_iterator it = image_memory_.begin(); - it != image_memory_.end(); ++it) { - resources[it->first] = base::StringPiece( - reinterpret_cast<const char*>(it->second->front()), it->second->size()); - } - - return base::DataPack::WritePack(path, resources); -} - -bool BrowserThemePack::GetTint(int id, color_utils::HSL* hsl) const { - if (tints_) { - for (int i = 0; i < kTintArraySize; ++i) { - if (tints_[i].id == id) { - hsl->h = tints_[i].h; - hsl->s = tints_[i].s; - hsl->l = tints_[i].l; - return true; - } - } - } - - return false; -} - -bool BrowserThemePack::GetColor(int id, SkColor* color) const { - if (colors_) { - for (int i = 0; i < kColorArraySize; ++i) { - if (colors_[i].id == id) { - *color = colors_[i].color; - return true; - } - } - } - - return false; -} - -bool BrowserThemePack::GetDisplayProperty(int id, int* result) const { - if (display_properties_) { - for (int i = 0; i < kDisplayPropertySize; ++i) { - if (display_properties_[i].id == id) { - *result = display_properties_[i].property; - return true; - } - } - } - - return false; -} - -SkBitmap* BrowserThemePack::GetBitmapNamed(int id) const { - // Check our cache of prepared images, first. - ImageCache::const_iterator image_iter = image_cache_.find(id); - if (image_iter != image_cache_.end()) - return image_iter->second; - - scoped_refptr<RefCountedMemory> memory; - if (data_pack_.get()) { - memory = data_pack_->GetStaticMemory(id); - } else { - RawImages::const_iterator it = image_memory_.find(id); - if (it != image_memory_.end()) { - memory = it->second; - } - } - - if (memory.get()) { - // Decode the PNG. - SkBitmap bitmap; - if (!gfx::PNGCodec::Decode(memory->front(), memory->size(), - &bitmap)) { - NOTREACHED() << "Unable to decode theme image resource " << id - << " from saved DataPack."; - return NULL; - } - - SkBitmap* ret = new SkBitmap(bitmap); - image_cache_[id] = ret; - - return ret; - } - - return NULL; -} - -RefCountedMemory* BrowserThemePack::GetRawData(int id) const { - RefCountedMemory* memory = NULL; - - if (data_pack_.get()) { - memory = data_pack_->GetStaticMemory(id); - } else { - RawImages::const_iterator it = image_memory_.find(id); - if (it != image_memory_.end()) { - memory = it->second; - } - } - - return memory; -} - -bool BrowserThemePack::HasCustomImage(int id) const { - if (data_pack_.get()) { - base::StringPiece ignored; - return data_pack_->GetStringPiece(id, &ignored); - } else { - return image_memory_.count(id) > 0; - } -} - -// private: - -BrowserThemePack::BrowserThemePack() - : header_(NULL), - tints_(NULL), - colors_(NULL), - display_properties_(NULL) { -} - -void BrowserThemePack::BuildHeader(Extension* extension) { - header_ = new BrowserThemePackHeader; - header_->version = kThemePackVersion; - - // TODO(erg): Need to make this endian safe on other computers. Prerequisite - // is that base::DataPack removes this same check. -#if defined(__BYTE_ORDER) - // Linux check - COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, - datapack_assumes_little_endian); -#elif defined(__BIG_ENDIAN__) - // Mac check - #error DataPack assumes little endian -#endif - header_->little_endian = 1; - - const std::string& id = extension->id(); - memcpy(header_->theme_id, id.c_str(), Extension::kIdSize); -} - -void BrowserThemePack::BuildTintsFromJSON(DictionaryValue* tints_value) { - tints_ = new TintEntry[kTintArraySize]; - for (int i = 0; i < kTintArraySize; ++i) { - tints_[i].id = -1; - tints_[i].h = -1; - tints_[i].s = -1; - tints_[i].l = -1; - } - - // Parse the incoming data from |tints_value| into an intermediary structure. - std::map<int, color_utils::HSL> temp_tints; - for (DictionaryValue::key_iterator iter(tints_value->begin_keys()); - iter != tints_value->end_keys(); ++iter) { - ListValue* tint_list; - if (tints_value->GetList(*iter, &tint_list) && - (tint_list->GetSize() == 3)) { - color_utils::HSL hsl = { -1, -1, -1 }; - - if (ValidRealValue(tint_list, 0, &hsl.h) && - ValidRealValue(tint_list, 1, &hsl.s) && - ValidRealValue(tint_list, 2, &hsl.l)) { - - int id = GetIntForString(WideToUTF8(*iter), kTintTable); - if (id != -1) { - temp_tints[id] = hsl; - } - } - } - } - - // Copy data from the intermediary data structure to the array. - int count = 0; - for (std::map<int, color_utils::HSL>::const_iterator it = - temp_tints.begin(); it != temp_tints.end() && count < kTintArraySize; - ++it, ++count) { - tints_[count].id = it->first; - tints_[count].h = it->second.h; - tints_[count].s = it->second.s; - tints_[count].l = it->second.l; - } -} - -void BrowserThemePack::BuildColorsFromJSON(DictionaryValue* colors_value) { - colors_ = new ColorPair[kColorArraySize]; - for (int i = 0; i < kColorArraySize; ++i) { - colors_[i].id = -1; - colors_[i].color = SkColorSetRGB(0, 0, 0); - } - - std::map<int, SkColor> temp_colors; - ReadColorsFromJSON(colors_value, &temp_colors); - GenerateMissingColors(&temp_colors); - - // Copy data from the intermediary data structure to the array. - int count = 0; - for (std::map<int, SkColor>::const_iterator it = temp_colors.begin(); - it != temp_colors.end() && count < kColorArraySize; ++it, ++count) { - colors_[count].id = it->first; - colors_[count].color = it->second; - } -} - -void BrowserThemePack::ReadColorsFromJSON( - DictionaryValue* colors_value, - std::map<int, SkColor>* temp_colors) { - // Parse the incoming data from |colors_value| into an intermediary structure. - for (DictionaryValue::key_iterator iter(colors_value->begin_keys()); - iter != colors_value->end_keys(); ++iter) { - ListValue* color_list; - if (colors_value->GetList(*iter, &color_list) && - ((color_list->GetSize() == 3) || (color_list->GetSize() == 4))) { - SkColor color = SK_ColorWHITE; - int r, g, b; - if (color_list->GetInteger(0, &r) && - color_list->GetInteger(1, &g) && - color_list->GetInteger(2, &b)) { - if (color_list->GetSize() == 4) { - double alpha; - int alpha_int; - if (color_list->GetReal(3, &alpha)) { - color = SkColorSetARGB(static_cast<int>(alpha * 255), r, g, b); - } else if (color_list->GetInteger(3, &alpha_int) && - (alpha_int == 0 || alpha_int == 1)) { - color = SkColorSetARGB(alpha_int ? 255 : 0, r, g, b); - } else { - // Invalid entry for part 4. - continue; - } - } else { - color = SkColorSetRGB(r, g, b); - } - - int id = GetIntForString(WideToUTF8(*iter), kColorTable); - if (id != -1) { - (*temp_colors)[id] = color; - } - } - } - } -} - -void BrowserThemePack::GenerateMissingColors( - std::map<int, SkColor>* colors) { - // Generate link colors, if missing. (See GetColor()). - if (!colors->count(BrowserThemeProvider::COLOR_NTP_HEADER) && - colors->count(BrowserThemeProvider::COLOR_NTP_SECTION)) { - (*colors)[BrowserThemeProvider::COLOR_NTP_HEADER] = - (*colors)[BrowserThemeProvider::COLOR_NTP_SECTION]; - } - - if (!colors->count(BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE) && - colors->count(BrowserThemeProvider::COLOR_NTP_SECTION_LINK)) { - SkColor color_section_link = - (*colors)[BrowserThemeProvider::COLOR_NTP_SECTION_LINK]; - (*colors)[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = - SkColorSetA(color_section_link, SkColorGetA(color_section_link) / 3); - } - - if (!colors->count(BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE) && - colors->count(BrowserThemeProvider::COLOR_NTP_LINK)) { - SkColor color_link = (*colors)[BrowserThemeProvider::COLOR_NTP_LINK]; - (*colors)[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = - SkColorSetA(color_link, SkColorGetA(color_link) / 3); - } - - // Generate frame colors, if missing. (See GenerateFrameColors()). - SkColor frame; - std::map<int, SkColor>::const_iterator it = - colors->find(BrowserThemeProvider::COLOR_FRAME); - if (it != colors->end()) { - frame = it->second; - } else { - frame = BrowserThemeProvider::GetDefaultColor( - BrowserThemeProvider::COLOR_FRAME); - } - - if (!colors->count(BrowserThemeProvider::COLOR_FRAME)) { - (*colors)[BrowserThemeProvider::COLOR_FRAME] = - HSLShift(frame, GetTintInternal(BrowserThemeProvider::TINT_FRAME)); - } - if (!colors->count(BrowserThemeProvider::COLOR_FRAME_INACTIVE)) { - (*colors)[BrowserThemeProvider::COLOR_FRAME_INACTIVE] = - HSLShift(frame, GetTintInternal( - BrowserThemeProvider::TINT_FRAME_INACTIVE)); - } - if (!colors->count(BrowserThemeProvider::COLOR_FRAME_INCOGNITO)) { - (*colors)[BrowserThemeProvider::COLOR_FRAME_INCOGNITO] = - HSLShift(frame, GetTintInternal( - BrowserThemeProvider::TINT_FRAME_INCOGNITO)); - } - if (!colors->count(BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE)) { - (*colors)[BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE] = - HSLShift(frame, GetTintInternal( - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE)); - } -} - -void BrowserThemePack::BuildDisplayPropertiesFromJSON( - DictionaryValue* display_properties_value) { - display_properties_ = new DisplayPropertyPair[kDisplayPropertySize]; - for (int i = 0; i < kDisplayPropertySize; ++i) { - display_properties_[i].id = -1; - display_properties_[i].property = 0; - } - - std::map<int, int> temp_properties; - for (DictionaryValue::key_iterator iter( - display_properties_value->begin_keys()); - iter != display_properties_value->end_keys(); ++iter) { - int property_id = GetIntForString(WideToUTF8(*iter), kDisplayProperties); - switch (property_id) { - case BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT: { - std::string val; - if (display_properties_value->GetString(*iter, &val)) { - temp_properties[BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT] = - BrowserThemeProvider::StringToAlignment(val); - } - break; - } - case BrowserThemeProvider::NTP_BACKGROUND_TILING: { - std::string val; - if (display_properties_value->GetString(*iter, &val)) { - temp_properties[BrowserThemeProvider::NTP_BACKGROUND_TILING] = - GetIntForString(val, kTilingStrings); - } - break; - } - case BrowserThemeProvider::NTP_LOGO_ALTERNATE: { - int val = 0; - if (display_properties_value->GetInteger(*iter, &val)) - temp_properties[BrowserThemeProvider::NTP_LOGO_ALTERNATE] = val; - break; - } - } - } - - // Copy data from the intermediary data structure to the array. - int count = 0; - for (std::map<int, int>::const_iterator it = temp_properties.begin(); - it != temp_properties.end() && count < kDisplayPropertySize; - ++it, ++count) { - display_properties_[count].id = it->first; - display_properties_[count].property = it->second; - } -} - -void BrowserThemePack::ParseImageNamesFromJSON( - DictionaryValue* images_value, - FilePath images_path, - std::map<int, FilePath>* file_paths) const { - for (DictionaryValue::key_iterator iter(images_value->begin_keys()); - iter != images_value->end_keys(); ++iter) { - std::string val; - if (images_value->GetString(*iter, &val)) { - int id = ThemeResourcesUtil::GetId(WideToUTF8(*iter)); - if (id != -1) - (*file_paths)[id] = images_path.AppendASCII(val); - } - } -} - -void BrowserThemePack::LoadRawBitmapsTo( - const std::map<int, FilePath>& file_paths, - ImageCache* raw_bitmaps) { - for (std::map<int, FilePath>::const_iterator it = file_paths.begin(); - it != file_paths.end(); ++it) { - scoped_refptr<RefCountedMemory> raw_data(ReadFileData(it->second)); - int id = it->first; - - // Some images need to go directly into |image_memory_|. No modification is - // necessary or desirable. - bool is_copyable = false; - for (size_t i = 0; i < arraysize(kPreloadIDs); ++i) { - if (kPreloadIDs[i] == id) { - is_copyable = true; - break; - } - } - - if (is_copyable) { - image_memory_[id] = raw_data; - } else if (raw_data.get() && raw_data->size()) { - // Decode the PNG. - SkBitmap bitmap; - if (gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(), - &bitmap)) { - (*raw_bitmaps)[it->first] = new SkBitmap(bitmap); - } else { - NOTREACHED() << "Unable to decode theme image resource " << it->first; - } - } - } -} - -void BrowserThemePack::GenerateFrameImages(ImageCache* bitmaps) const { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - - // Create all the output bitmaps in a separate cache and move them back into - // the input bitmaps because there can be name collisions. - ImageCache temp_output; - - for (size_t i = 0; i < arraysize(kFrameTintMap); ++i) { - int id = kFrameTintMap[i].key; - scoped_ptr<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; - - if (id == IDR_THEME_FRAME_INCOGNITO_INACTIVE) { - base_id = bitmaps->count(IDR_THEME_FRAME_INCOGNITO) ? - IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; - } else if (id == IDR_THEME_FRAME_OVERLAY_INACTIVE) { - base_id = IDR_THEME_FRAME_OVERLAY; - } else if (id == IDR_THEME_FRAME_INACTIVE) { - base_id = IDR_THEME_FRAME; - } else if (id == IDR_THEME_FRAME_INCOGNITO && - !bitmaps->count(IDR_THEME_FRAME_INCOGNITO)) { - base_id = IDR_THEME_FRAME; - } else { - base_id = id; - } - - if (bitmaps->count(id)) { - frame.reset(new SkBitmap(*(*bitmaps)[id])); - } else if (base_id != id && bitmaps->count(base_id)) { - frame.reset(new SkBitmap(*(*bitmaps)[base_id])); - } else if (base_id == IDR_THEME_FRAME_OVERLAY && - bitmaps->count(IDR_THEME_FRAME)) { - // If there is no theme overlay, don't tint the default frame, - // because it will overwrite the custom frame image when we cache and - // reload from disk. - frame.reset(NULL); - } else { - // If the theme doesn't specify an image, then apply the tint to - // the default frame. - frame.reset(new SkBitmap(*rb.GetBitmapNamed(IDR_THEME_FRAME))); - } - - if (frame.get()) { - temp_output[id] = new SkBitmap( - SkBitmapOperations::CreateHSLShiftedBitmap( - *frame, GetTintInternal(kFrameTintMap[i].value))); - } - } - - MergeImageCaches(temp_output, bitmaps); -} - -void BrowserThemePack::GenerateTintedButtons( - color_utils::HSL button_tint, - ImageCache* processed_bitmaps) const { - if (button_tint.h != -1 || button_tint.s != -1 || button_tint.l != -1) { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - for (size_t i = 0; i < arraysize(kToolbarButtonIDs); ++i) { - scoped_ptr<SkBitmap> button( - new SkBitmap(*rb.GetBitmapNamed(kToolbarButtonIDs[i]))); - (*processed_bitmaps)[kToolbarButtonIDs[i]] = new SkBitmap( - SkBitmapOperations::CreateHSLShiftedBitmap(*button, button_tint)); - } - } -} - -void BrowserThemePack::GenerateTabBackgroundImages(ImageCache* bitmaps) const { - ImageCache temp_output; - for (size_t i = 0; i < arraysize(kTabBackgroundMap); ++i) { - int id = kTabBackgroundMap[i].key; - int base_id = kTabBackgroundMap[i].value; - - // We only need to generate the background tab images if we were provided - // with a IDR_THEME_FRAME. - ImageCache::const_iterator it = bitmaps->find(base_id); - if (it != bitmaps->end()) { - SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap( - *(it->second), GetTintInternal( - BrowserThemeProvider::TINT_BACKGROUND_TAB)); - int vertical_offset = bitmaps->count(id) ? kRestoredTabVerticalOffset : 0; - SkBitmap* bg_tab = new SkBitmap(SkBitmapOperations::CreateTiledBitmap( - bg_tint, 0, vertical_offset, bg_tint.width(), bg_tint.height())); - - // If they've provided a custom image, overlay it. - ImageCache::const_iterator overlay_it = bitmaps->find(id); - if (overlay_it != bitmaps->end()) { - SkBitmap* overlay = overlay_it->second; - SkCanvas canvas(*bg_tab); - for (int x = 0; x < bg_tab->width(); x += overlay->width()) - canvas.drawBitmap(*overlay, static_cast<SkScalar>(x), 0, NULL); - } - - temp_output[id] = bg_tab; - } - } - - MergeImageCaches(temp_output, bitmaps); -} - -void BrowserThemePack::RepackImageCacheToImageMemory() { - // TODO(erg): This shouldn't be done on the main thread. This should be done - // during the WriteToDisk() method, but will require some tricky locking. - for (ImageCache::const_iterator it = image_cache_.begin(); - it != image_cache_.end(); ++it) { - std::vector<unsigned char> image_data; - if (!gfx::PNGCodec::EncodeBGRASkBitmap(*(it->second), false, &image_data)) { - NOTREACHED() << "Image file for resource " << it->first - << " could not be encoded."; - } else { - image_memory_[it->first] = RefCountedBytes::TakeVector(&image_data); - } - } -} - -void BrowserThemePack::MergeImageCaches( - const ImageCache& source, ImageCache* destination) const { - - for (ImageCache::const_iterator it = source.begin(); it != source.end(); - ++it) { - ImageCache::const_iterator bitmap_it = destination->find(it->first); - if (bitmap_it != destination->end()) - delete bitmap_it->second; - - (*destination)[it->first] = it->second; - } -} - -color_utils::HSL BrowserThemePack::GetTintInternal(int id) const { - if (tints_) { - for (int i = 0; i < kTintArraySize; ++i) { - if (tints_[i].id == id) { - color_utils::HSL hsl; - hsl.h = tints_[i].h; - hsl.s = tints_[i].s; - hsl.l = tints_[i].l; - return hsl; - } - } - } - - return BrowserThemeProvider::GetDefaultTint(id); -} diff --git a/chrome/browser/browser_theme_pack.h b/chrome/browser/browser_theme_pack.h deleted file mode 100644 index 4287617..0000000 --- a/chrome/browser/browser_theme_pack.h +++ /dev/null @@ -1,191 +0,0 @@ -// 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. - -#ifndef CHROME_BROWSER_BROWSER_THEME_PACK_H_ -#define CHROME_BROWSER_BROWSER_THEME_PACK_H_ - -#include <map> -#include <string> - -#include "app/gfx/color_utils.h" -#include "base/basictypes.h" -#include "base/scoped_ptr.h" -#include "base/ref_counted.h" -#include "chrome/common/extensions/extension.h" - -class BrowserThemeProviderTest; -namespace base { -class DataPack; -} -class DictionaryValue; -class FilePath; -class RefCountedMemory; - -// An optimized representation of a theme, backed by a mmapped DataPack. -// -// The idea is to pre-process all images (tinting, compositing, etc) at theme -// install time, save all the PNG-ified data into an mmappable file so we don't -// suffer multiple file system access times, therefore solving two of the -// problems with the previous implementation. -// -// A note on const-ness. All public, non-static methods are const. We do this -// because once we've constructed a BrowserThemePack through the -// BuildFromExtension() interface, we WriteToDisk() on a thread other than the -// UI thread that consumes a BrowserThemePack. -class BrowserThemePack : public base::RefCountedThreadSafe<BrowserThemePack> { - public: - ~BrowserThemePack(); - - // Builds the theme pack from all data from |extension|. This is often done - // on a separate thread as it takes so long. - static BrowserThemePack* BuildFromExtension(Extension* extension); - - // Builds the theme pack from a previously WriteToDisk(). This operation - // should be relatively fast, as it should be an mmap() and some pointer - // swizzling. Returns NULL on any error attempting to read |path|. - static scoped_refptr<BrowserThemePack> BuildFromDataPack( - FilePath path, const std::string& expected_id); - - // Builds a data pack on disk at |path| for future quick loading by - // BuildFromDataPack(). Often (but not always) called from the file thread; - // implementation should be threadsafe because neither thread will write to - // |image_memory_| and the worker thread will keep a reference to prevent - // destruction. - bool WriteToDisk(FilePath path) const; - - // Returns data from the pack, or the default value if |id| doesn't - // exist. These methods should only be called from the UI thread. (But this - // isn't enforced because of unit tests). - bool GetTint(int id, color_utils::HSL* hsl) const; - bool GetColor(int id, SkColor* color) const; - bool GetDisplayProperty(int id, int* result) const; - SkBitmap* GetBitmapNamed(int id) const; - RefCountedMemory* GetRawData(int id) const; - - // Whether this theme provides an image for |id|. - bool HasCustomImage(int id) const; - - private: - friend class BrowserThemePackTest; - - // Cached images. We cache all retrieved and generated bitmaps and keep - // track of the pointers. We own these and will delete them when we're done - // using them. - typedef std::map<int, SkBitmap*> ImageCache; - - // The raw PNG memory associated with a certain id. - typedef std::map<int, scoped_refptr<RefCountedMemory> > RawImages; - - // Default. Everything is empty. - BrowserThemePack(); - - // Builds a header ready to write to disk. - void BuildHeader(Extension* extension); - - // Transforms the JSON tint values into their final versions in the |tints_| - // array. - void BuildTintsFromJSON(DictionaryValue* tints_value); - - // Transforms the JSON color values into their final versions in the - // |colors_| array and also fills in unspecified colors based on tint values. - void BuildColorsFromJSON(DictionaryValue* color_value); - - // Implementation details of BuildColorsFromJSON(). - void ReadColorsFromJSON(DictionaryValue* colors_value, - std::map<int, SkColor>* temp_colors); - void GenerateMissingColors(std::map<int, SkColor>* temp_colors); - - // Transforms the JSON display properties into |display_properties_|. - void BuildDisplayPropertiesFromJSON(DictionaryValue* display_value); - - // Parses the image names out of an extension. - void ParseImageNamesFromJSON(DictionaryValue* images_value, - FilePath images_path, - std::map<int, FilePath>* file_paths) const; - - // Loads the unmodified bitmaps packed in the extension to SkBitmaps. - void LoadRawBitmapsTo(const std::map<int, FilePath>& file_paths, - ImageCache* raw_bitmaps); - - // Creates tinted and composited frame images. Source and destination is - // |bitmaps|. - void GenerateFrameImages(ImageCache* bitmaps) const; - - // Generates button images tinted with |button_tint| and places them in - // processed_bitmaps. - void GenerateTintedButtons(color_utils::HSL button_tint, - ImageCache* processed_bitmaps) const; - - // Generates the semi-transparent tab background images, putting the results - // in |bitmaps|. Must be called after GenerateFrameImages(). - void GenerateTabBackgroundImages(ImageCache* bitmaps) const; - - // Takes all the SkBitmaps in |image_cache_|, encodes them as PNGs and places - // them in |image_memory_|. - void RepackImageCacheToImageMemory(); - - // Takes all images in |source| and puts them in |destination|, freeing any - // image already in |destination| that |source| would overwrite. - void MergeImageCaches(const ImageCache& source, - ImageCache* destination) const; - - // Retrieves the tint OR the default tint. Unlike the public interface, we - // always need to return a reasonable tint here, instead of partially - // querying if the tint exists. - color_utils::HSL GetTintInternal(int id) const; - - // Data pack, if we have one. - scoped_ptr<base::DataPack> data_pack_; - - // All structs written to disk need to be packed; no alignment tricks here, - // please. -#pragma pack(push,1) - // Header that is written to disk. - struct BrowserThemePackHeader { - // Numeric version to make sure we're compatible in the future. - int32 version; - - // 1 if little_endian. 0 if big_endian. On mismatch, abort load. - int32 little_endian; - - // theme_id without NULL terminator. - uint8 theme_id[16]; - } *header_; - - // The remaining structs represent individual entries in an array. For the - // following three structs, BrowserThemePack will either allocate an array or - // will point directly to mmapped data. - struct TintEntry { - int32 id; - double h; - double s; - double l; - } *tints_; - - struct ColorPair { - int32 id; - SkColor color; - } *colors_; - - struct DisplayPropertyPair { - int32 id; - int32 property; - } *display_properties_; -#pragma pack(pop) - - // References to raw PNG data. This map isn't touched when |data_pack_| is - // non-NULL; |image_memory_| is only filled during BuildFromExtension(). Any - // image data that needs to be written to the DataPack during WriteToDisk() - // needs to be in |image_memory_|. - RawImages image_memory_; - - // Tinted (or otherwise prepared) images for passing out. BrowserThemePack - // owns all these images. This cache isn't touched when we write our data to - // a DataPack. - mutable ImageCache image_cache_; - - DISALLOW_COPY_AND_ASSIGN(BrowserThemePack); -}; - -#endif // CHROME_BROWSER_BROWSER_THEME_PACK_H_ diff --git a/chrome/browser/browser_theme_pack_unittest.cc b/chrome/browser/browser_theme_pack_unittest.cc deleted file mode 100644 index 1c9081c..0000000 --- a/chrome/browser/browser_theme_pack_unittest.cc +++ /dev/null @@ -1,335 +0,0 @@ -// 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 "chrome/browser/browser_theme_pack.h" - -#include "app/gfx/color_utils.h" -#include "app/theme_provider.h" -#include "base/file_util.h" -#include "base/json/json_reader.h" -#include "base/path_service.h" -#include "base/scoped_temp_dir.h" -#include "base/values.h" -#include "chrome/browser/theme_resources_util.h" -#include "chrome/browser/browser_theme_provider.h" -#include "chrome/common/chrome_paths.h" -#include "chrome/common/json_value_serializer.h" -#include "grit/theme_resources.h" -#include "testing/gtest/include/gtest/gtest.h" - -class BrowserThemePackTest : public ::testing::Test { - public: - BrowserThemePackTest() : theme_pack_(new BrowserThemePack) { } - - // Transformation for link underline colors. - SkColor BuildThirdOpacity(SkColor color_link) { - return SkColorSetA(color_link, SkColorGetA(color_link) / 3); - } - - void GenerateDefaultFrameColor(std::map<int, SkColor>* colors, - int color, int tint) { - (*colors)[color] = HSLShift( - BrowserThemeProvider::GetDefaultColor( - BrowserThemeProvider::COLOR_FRAME), - BrowserThemeProvider::GetDefaultTint(tint)); - } - - // Returns a mapping from each COLOR_* constant to the default value for this - // constant. Callers get this map, and then modify expected values and then - // run the resulting thing through VerifyColorMap(). - std::map<int, SkColor> GetDefaultColorMap() { - std::map<int, SkColor> colors; - for (int i = BrowserThemeProvider::COLOR_FRAME; - i <= BrowserThemeProvider::COLOR_BUTTON_BACKGROUND; ++i) { - colors[i] = BrowserThemeProvider::GetDefaultColor(i); - } - - GenerateDefaultFrameColor(&colors, BrowserThemeProvider::COLOR_FRAME, - BrowserThemeProvider::TINT_FRAME); - GenerateDefaultFrameColor(&colors, - BrowserThemeProvider::COLOR_FRAME_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INACTIVE); - GenerateDefaultFrameColor(&colors, - BrowserThemeProvider::COLOR_FRAME_INCOGNITO, - BrowserThemeProvider::TINT_FRAME_INCOGNITO); - GenerateDefaultFrameColor( - &colors, - BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE); - - return colors; - } - - void VerifyColorMap(const std::map<int, SkColor>& color_map) { - for (std::map<int, SkColor>::const_iterator it = color_map.begin(); - it != color_map.end(); ++it) { - SkColor color = BrowserThemeProvider::GetDefaultColor(it->first); - theme_pack_->GetColor(it->first, &color); - EXPECT_EQ(it->second, color) << "Color id = " << it->first; - } - } - - void LoadColorJSON(const std::string& json) { - scoped_ptr<Value> value(base::JSONReader::Read(json, false)); - ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); - theme_pack_->BuildColorsFromJSON( - static_cast<DictionaryValue*>(value.get())); - } - - void LoadTintJSON(const std::string& json) { - scoped_ptr<Value> value(base::JSONReader::Read(json, false)); - ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); - theme_pack_->BuildTintsFromJSON( - static_cast<DictionaryValue*>(value.get())); - } - - void LoadDisplayPropertiesJSON(const std::string& json) { - scoped_ptr<Value> value(base::JSONReader::Read(json, false)); - ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); - theme_pack_->BuildDisplayPropertiesFromJSON( - static_cast<DictionaryValue*>(value.get())); - } - - void ParseImageNames(const std::string& json, - std::map<int, FilePath>* out_file_paths) { - scoped_ptr<Value> value(base::JSONReader::Read(json, false)); - ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); - theme_pack_->ParseImageNamesFromJSON( - static_cast<DictionaryValue*>(value.get()), - FilePath(), out_file_paths); - } - - FilePath GetStarGazingPath() { - FilePath test_path; - if (!PathService::Get(chrome::DIR_TEST_DATA, &test_path)) { - NOTREACHED(); - return test_path; - } - - test_path = test_path.AppendASCII("profiles"); - test_path = test_path.AppendASCII("complex_theme"); - test_path = test_path.AppendASCII("Default"); - test_path = test_path.AppendASCII("Extensions"); - test_path = test_path.AppendASCII("mblmlcbknbnfebdfjnolmcapmdofhmme"); - test_path = test_path.AppendASCII("1.1"); - return FilePath(test_path); - } - - // Verifies the data in star gazing. We do this multiple times for different - // BrowserThemePack objects to make sure it works in generated and mmapped - // mode correctly. - void VerifyStarGazing(BrowserThemePack* pack) { - // First check that values we know exist, exist. - SkColor color; - EXPECT_TRUE(pack->GetColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, - &color)); - EXPECT_EQ(SK_ColorBLACK, color); - - EXPECT_TRUE(pack->GetColor(BrowserThemeProvider::COLOR_NTP_BACKGROUND, - &color)); - EXPECT_EQ(SkColorSetRGB(57, 137, 194), color); - - color_utils::HSL expected = { 0.6, 0.553, 0.5 }; - color_utils::HSL actual; - EXPECT_TRUE(pack->GetTint(BrowserThemeProvider::TINT_BUTTONS, &actual)); - EXPECT_DOUBLE_EQ(expected.h, actual.h); - EXPECT_DOUBLE_EQ(expected.s, actual.s); - EXPECT_DOUBLE_EQ(expected.l, actual.l); - - int val; - EXPECT_TRUE(pack->GetDisplayProperty( - BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT, &val)); - EXPECT_EQ(BrowserThemeProvider::ALIGN_TOP, val); - - EXPECT_TRUE(pack->HasCustomImage(IDR_THEME_FRAME)); - - // Make sure we don't have phantom data. - EXPECT_FALSE(pack->GetColor(BrowserThemeProvider::COLOR_CONTROL_BACKGROUND, - &color)); - EXPECT_FALSE(pack->GetTint(BrowserThemeProvider::TINT_FRAME, &actual)); - } - - scoped_refptr<BrowserThemePack> theme_pack_; -}; - - -TEST_F(BrowserThemePackTest, DeriveUnderlineLinkColor) { - // If we specify a link color, but don't specify the underline color, the - // theme provider should create one. - std::string color_json = "{ \"ntp_link\": [128, 128, 128]," - " \"ntp_section_link\": [128, 128, 128] }"; - LoadColorJSON(color_json); - - std::map<int, SkColor> colors = GetDefaultColorMap(); - SkColor link_color = SkColorSetRGB(128, 128, 128); - colors[BrowserThemeProvider::COLOR_NTP_LINK] = link_color; - colors[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = - BuildThirdOpacity(link_color); - colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK] = link_color; - colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = - BuildThirdOpacity(link_color); - - VerifyColorMap(colors); -} - -TEST_F(BrowserThemePackTest, ProvideUnderlineLinkColor) { - // If we specify the underline color, it shouldn't try to generate one.x - std::string color_json = "{ \"ntp_link\": [128, 128, 128]," - " \"ntp_link_underline\": [255, 255, 255]," - " \"ntp_section_link\": [128, 128, 128]," - " \"ntp_section_link_underline\": [255, 255, 255]" - "}"; - LoadColorJSON(color_json); - - std::map<int, SkColor> colors = GetDefaultColorMap(); - SkColor link_color = SkColorSetRGB(128, 128, 128); - SkColor underline_color = SkColorSetRGB(255, 255, 255); - colors[BrowserThemeProvider::COLOR_NTP_LINK] = link_color; - colors[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = underline_color; - colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK] = link_color; - colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = - underline_color; - - VerifyColorMap(colors); -} - -TEST_F(BrowserThemePackTest, UseSectionColorAsNTPHeader) { - std::string color_json = "{ \"ntp_section\": [190, 190, 190] }"; - LoadColorJSON(color_json); - - std::map<int, SkColor> colors = GetDefaultColorMap(); - SkColor ntp_color = SkColorSetRGB(190, 190, 190); - colors[BrowserThemeProvider::COLOR_NTP_HEADER] = ntp_color; - colors[BrowserThemeProvider::COLOR_NTP_SECTION] = ntp_color; - VerifyColorMap(colors); -} - -TEST_F(BrowserThemePackTest, ProvideNtpHeaderColor) { - std::string color_json = "{ \"ntp_header\": [120, 120, 120], " - " \"ntp_section\": [190, 190, 190] }"; - LoadColorJSON(color_json); - - std::map<int, SkColor> colors = GetDefaultColorMap(); - SkColor ntp_header = SkColorSetRGB(120, 120, 120); - SkColor ntp_section = SkColorSetRGB(190, 190, 190); - colors[BrowserThemeProvider::COLOR_NTP_HEADER] = ntp_header; - colors[BrowserThemeProvider::COLOR_NTP_SECTION] = ntp_section; - VerifyColorMap(colors); -} - -TEST_F(BrowserThemePackTest, CanReadTints) { - std::string tint_json = "{ \"buttons\": [ 0.5, 0.5, 0.5 ] }"; - LoadTintJSON(tint_json); - - color_utils::HSL expected = { 0.5, 0.5, 0.5 }; - color_utils::HSL actual = { -1, -1, -1 }; - EXPECT_TRUE(theme_pack_->GetTint( - BrowserThemeProvider::TINT_BUTTONS, &actual)); - EXPECT_DOUBLE_EQ(expected.h, actual.h); - EXPECT_DOUBLE_EQ(expected.s, actual.s); - EXPECT_DOUBLE_EQ(expected.l, actual.l); -} - -TEST_F(BrowserThemePackTest, CanReadDisplayProperties) { - std::string json = "{ \"ntp_background_alignment\": \"bottom\", " - " \"ntp_background_repeat\": \"repeat-x\", " - " \"ntp_logo_alternate\": 0 }"; - LoadDisplayPropertiesJSON(json); - - int out_val; - EXPECT_TRUE(theme_pack_->GetDisplayProperty( - BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT, &out_val)); - EXPECT_EQ(BrowserThemeProvider::ALIGN_BOTTOM, out_val); - - EXPECT_TRUE(theme_pack_->GetDisplayProperty( - BrowserThemeProvider::NTP_BACKGROUND_TILING, &out_val)); - EXPECT_EQ(BrowserThemeProvider::REPEAT_X, out_val); - - EXPECT_TRUE(theme_pack_->GetDisplayProperty( - BrowserThemeProvider::NTP_LOGO_ALTERNATE, &out_val)); - EXPECT_EQ(0, out_val); -} - -TEST_F(BrowserThemePackTest, CanParsePaths) { - std::string tint_json = "{ \"theme_button_background\": \"one\", " - " \"theme_toolbar\": \"two\" }"; - std::map<int, FilePath> out_file_paths; - ParseImageNames(tint_json, &out_file_paths); - - EXPECT_EQ(2u, out_file_paths.size()); - EXPECT_TRUE(FilePath(FILE_PATH_LITERAL("one")) == out_file_paths[ - ThemeResourcesUtil::GetId("theme_button_background")]); - EXPECT_TRUE(FilePath(FILE_PATH_LITERAL("two")) == - out_file_paths[ThemeResourcesUtil::GetId("theme_toolbar")]); -} - -TEST_F(BrowserThemePackTest, InvalidColors) { - std::string invalid_color = "{ \"toolbar\": [\"dog\", \"cat\", [12]], " - " \"sound\": \"woof\" }"; - LoadColorJSON(invalid_color); - std::map<int, SkColor> colors = GetDefaultColorMap(); - VerifyColorMap(colors); -} - -TEST_F(BrowserThemePackTest, InvalidTints) { - std::string invalid_tints = "{ \"buttons\": [ \"dog\", \"cat\", [\"x\"]], " - " \"invalid\": \"entry\" }"; - LoadTintJSON(invalid_tints); - - // We shouldn't have a buttons tint, as it was invalid. - color_utils::HSL actual = { -1, -1, -1 }; - EXPECT_FALSE(theme_pack_->GetTint(BrowserThemeProvider::TINT_BUTTONS, - &actual)); -} - -TEST_F(BrowserThemePackTest, InvalidDisplayProperties) { - std::string invalid_properties = "{ \"ntp_background_alignment\": [15], " - " \"junk\": [15.3] }"; - LoadDisplayPropertiesJSON(invalid_properties); - - int out_val; - EXPECT_FALSE(theme_pack_->GetDisplayProperty( - BrowserThemeProvider::NTP_BACKGROUND_ALIGNMENT, &out_val)); -} - -// TODO(erg): This test should actually test more of the built resources from -// the extension data, but for now, exists so valgrind can test some of the -// tricky memory stuff that BrowserThemePack does. -TEST_F(BrowserThemePackTest, CanBuildAndReadPack) { - ScopedTempDir dir; - ASSERT_TRUE(dir.CreateUniqueTempDir()); - FilePath file = dir.path().Append(FILE_PATH_LITERAL("data.pak")); - - // Part 1: Build the pack from an extension. - { - FilePath star_gazing_path = GetStarGazingPath(); - Extension extension(star_gazing_path); - - FilePath manifest_path = - star_gazing_path.AppendASCII("manifest.json"); - std::string error; - JSONFileValueSerializer serializer(manifest_path); - scoped_ptr<DictionaryValue> valid_value( - static_cast<DictionaryValue*>(serializer.Deserialize(&error))); - EXPECT_EQ("", error); - ASSERT_TRUE(valid_value.get()); - ASSERT_TRUE(extension.InitFromValue(*valid_value, true, &error)); - ASSERT_EQ("", error); - - scoped_refptr<BrowserThemePack> pack = - BrowserThemePack::BuildFromExtension(&extension); - ASSERT_TRUE(pack.get()); - ASSERT_TRUE(pack->WriteToDisk(file)); - VerifyStarGazing(pack.get()); - } - - // Part 2: Try to read back the data pack that we just wrote to disk. - { - scoped_refptr<BrowserThemePack> pack = - BrowserThemePack::BuildFromDataPack( - file, "mblmlcbknbnfebdfjnolmcapmdofhmme"); - ASSERT_TRUE(pack.get()); - VerifyStarGazing(pack.get()); - } -} diff --git a/chrome/browser/browser_theme_provider.cc b/chrome/browser/browser_theme_provider.cc index 5a47772..aceedf1 100644 --- a/chrome/browser/browser_theme_provider.cc +++ b/chrome/browser/browser_theme_provider.cc @@ -13,7 +13,6 @@ #include "base/values.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/browser_process.h" -#include "chrome/browser/browser_theme_pack.h" #include "chrome/browser/browser_window.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/metrics/user_metrics.h" @@ -37,6 +36,50 @@ #include "app/win_util.h" #endif +// Strings used by themes to identify colors for different parts of our UI. +const char* BrowserThemeProvider::kColorFrame = "frame"; +const char* BrowserThemeProvider::kColorFrameInactive = "frame_inactive"; +const char* BrowserThemeProvider::kColorFrameIncognito = "frame_incognito"; +const char* BrowserThemeProvider::kColorFrameIncognitoInactive = + "frame_incognito_inactive"; +const char* BrowserThemeProvider::kColorToolbar = "toolbar"; +const char* BrowserThemeProvider::kColorTabText = "tab_text"; +const char* BrowserThemeProvider::kColorBackgroundTabText = + "tab_background_text"; +const char* BrowserThemeProvider::kColorBookmarkText = "bookmark_text"; +const char* BrowserThemeProvider::kColorNTPBackground = "ntp_background"; +const char* BrowserThemeProvider::kColorNTPText = "ntp_text"; +const char* BrowserThemeProvider::kColorNTPLink = "ntp_link"; +const char* BrowserThemeProvider::kColorNTPLinkUnderline = "ntp_link_underline"; +const char* BrowserThemeProvider::kColorNTPHeader = "ntp_header"; +const char* BrowserThemeProvider::kColorNTPSection = "ntp_section"; +const char* BrowserThemeProvider::kColorNTPSectionText = "ntp_section_text"; +const char* BrowserThemeProvider::kColorNTPSectionLink = "ntp_section_link"; +const char* BrowserThemeProvider::kColorNTPSectionLinkUnderline = + "ntp_section_link_underline"; +const char* BrowserThemeProvider::kColorControlBackground = + "control_background"; +const char* BrowserThemeProvider::kColorButtonBackground = "button_background"; + +// 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. +const char* BrowserThemeProvider::kTintButtons = "buttons"; +const char* BrowserThemeProvider::kTintFrame = "frame"; +const char* BrowserThemeProvider::kTintFrameInactive = "frame_inactive"; +const char* BrowserThemeProvider::kTintFrameIncognito = "frame_incognito"; +const char* BrowserThemeProvider::kTintFrameIncognitoInactive = + "frame_incognito_inactive"; +const char* BrowserThemeProvider::kTintBackgroundTab = "background_tab"; + +// Strings used by themes to identify miscellaneous numerical properties. +const char* BrowserThemeProvider::kDisplayPropertyNTPAlignment = + "ntp_background_alignment"; +const char* BrowserThemeProvider::kDisplayPropertyNTPTiling = + "ntp_background_repeat"; +const char* BrowserThemeProvider::kDisplayPropertyNTPInverseLogo = + "ntp_logo_alternate"; + // Strings used in alignment properties. const char* BrowserThemeProvider::kAlignmentTop = "top"; const char* BrowserThemeProvider::kAlignmentBottom = "bottom"; @@ -49,52 +92,68 @@ const char* BrowserThemeProvider::kTilingRepeatX = "repeat-x"; const char* BrowserThemeProvider::kTilingRepeatY = "repeat-y"; const char* BrowserThemeProvider::kTilingRepeat = "repeat"; -// Saved default values. -const char* BrowserThemeProvider::kDefaultThemeID = ""; - -namespace { - -SkColor TintForUnderline(SkColor input) { - return SkColorSetA(input, SkColorGetA(input) / 3); -} - // Default colors. -const SkColor kDefaultColorFrame = SkColorSetRGB(77, 139, 217); -const SkColor kDefaultColorFrameInactive = SkColorSetRGB(152, 188, 233); -const SkColor kDefaultColorFrameIncognito = SkColorSetRGB(83, 106, 139); -const SkColor kDefaultColorFrameIncognitoInactive = +const SkColor BrowserThemeProvider::kDefaultColorFrame = + SkColorSetRGB(77, 139, 217); +const SkColor BrowserThemeProvider::kDefaultColorFrameInactive = + SkColorSetRGB(152, 188, 233); +const SkColor BrowserThemeProvider::kDefaultColorFrameIncognito = + SkColorSetRGB(83, 106, 139); +const SkColor BrowserThemeProvider::kDefaultColorFrameIncognitoInactive = SkColorSetRGB(126, 139, 156); -const SkColor kDefaultColorToolbar = SkColorSetRGB(210, 225, 246); -const SkColor kDefaultColorTabText = SK_ColorBLACK; -const SkColor kDefaultColorBackgroundTabText = SkColorSetRGB(64, 64, 64); -const SkColor kDefaultColorBookmarkText = SkColorSetRGB(18, 50, 114); +const SkColor BrowserThemeProvider::kDefaultColorToolbar = + SkColorSetRGB(210, 225, 246); +const SkColor BrowserThemeProvider::kDefaultColorTabText = SK_ColorBLACK; +const SkColor BrowserThemeProvider::kDefaultColorBackgroundTabText = + SkColorSetRGB(64, 64, 64); +const SkColor BrowserThemeProvider::kDefaultColorBookmarkText = + SkColorSetRGB(18, 50, 114); #if defined(OS_WIN) -const SkColor kDefaultColorNTPBackground = +const SkColor BrowserThemeProvider::kDefaultColorNTPBackground = color_utils::GetSysSkColor(COLOR_WINDOW); -const SkColor kDefaultColorNTPText = +const SkColor BrowserThemeProvider::kDefaultColorNTPText = color_utils::GetSysSkColor(COLOR_WINDOWTEXT); -const SkColor kDefaultColorNTPLink = +const SkColor BrowserThemeProvider::kDefaultColorNTPLink = color_utils::GetSysSkColor(COLOR_HOTLIGHT); #else // TODO(beng): source from theme provider. -const SkColor kDefaultColorNTPBackground = SK_ColorWHITE; -const SkColor kDefaultColorNTPText = SK_ColorBLACK; -const SkColor kDefaultColorNTPLink = SkColorSetRGB(6, 55, 116); +const SkColor BrowserThemeProvider::kDefaultColorNTPBackground = SK_ColorWHITE; +const SkColor BrowserThemeProvider::kDefaultColorNTPText = SK_ColorBLACK; +const SkColor BrowserThemeProvider::kDefaultColorNTPLink = + SkColorSetRGB(6, 55, 116); #endif -const SkColor kDefaultColorNTPHeader = SkColorSetRGB(75, 140, 220); -const SkColor kDefaultColorNTPSection = SkColorSetRGB(229, 239, 254); -const SkColor kDefaultColorNTPSectionText = SK_ColorBLACK; -const SkColor kDefaultColorNTPSectionLink = SkColorSetRGB(6, 55, 116); -const SkColor kDefaultColorControlBackground = SkColorSetARGB(0, 0, 0, 0); -const SkColor kDefaultColorButtonBackground = SkColorSetARGB(0, 0, 0, 0); +const SkColor BrowserThemeProvider::kDefaultColorNTPHeader = + SkColorSetRGB(75, 140, 220); +const SkColor BrowserThemeProvider::kDefaultColorNTPSection = + SkColorSetRGB(229, 239, 254); +const SkColor BrowserThemeProvider::kDefaultColorNTPSectionText = SK_ColorBLACK; +const SkColor BrowserThemeProvider::kDefaultColorNTPSectionLink = + SkColorSetRGB(6, 55, 116); +const SkColor BrowserThemeProvider::kDefaultColorControlBackground = + SkColorSetARGB(0, 0, 0, 0); +const SkColor BrowserThemeProvider::kDefaultColorButtonBackground = + SkColorSetARGB(0, 0, 0, 0); // Default tints. -const color_utils::HSL kDefaultTintButtons = { -1, -1, -1 }; -const color_utils::HSL kDefaultTintFrame = { -1, -1, -1 }; -const color_utils::HSL kDefaultTintFrameInactive = { -1, -1, 0.75f }; -const color_utils::HSL kDefaultTintFrameIncognito = { -1, 0.2f, 0.35f }; -const color_utils::HSL kDefaultTintFrameIncognitoInactive = { -1, 0.3f, 0.6f }; -const color_utils::HSL kDefaultTintBackgroundTab = { -1, 0.5, 0.75 }; +const color_utils::HSL BrowserThemeProvider::kDefaultTintButtons = + { -1, -1, -1 }; +const color_utils::HSL BrowserThemeProvider::kDefaultTintFrame = { -1, -1, -1 }; +const color_utils::HSL BrowserThemeProvider::kDefaultTintFrameInactive = + { -1, -1, 0.75f }; +const color_utils::HSL BrowserThemeProvider::kDefaultTintFrameIncognito = + { -1, 0.2f, 0.35f }; +const color_utils::HSL + BrowserThemeProvider::kDefaultTintFrameIncognitoInactive = + { -1, 0.3f, 0.6f }; +const color_utils::HSL BrowserThemeProvider::kDefaultTintBackgroundTab = + { -1, 0.5, 0.75 }; + +// Saved default values. +const char* BrowserThemeProvider::kDefaultThemeID = ""; + +Lock BrowserThemeProvider::themed_image_cache_lock_; + +namespace { // Default display properties. const int kDefaultDisplayPropertyNTPAlignment = @@ -107,6 +166,35 @@ const int kDefaultDisplayPropertyNTPInverseLogo = 0; // OpaqueBrowserFrameView. const int kRestoredTabVerticalOffset = 15; +// The image resources that will be tinted by the 'button' tint value. +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_NOBORDER, IDR_STAR_NOBORDER_CENTER, IDR_STAR_D, IDR_STAR_H, + IDR_STAR_P, + IDR_STARRED, IDR_STARRED_NOBORDER, IDR_STARRED_NOBORDER_CENTER, IDR_STARRED_H, + IDR_STARRED_P, + IDR_GO, IDR_GO_NOBORDER, IDR_GO_NOBORDER_CENTER, IDR_GO_H, IDR_GO_P, + IDR_STOP, IDR_STOP_NOBORDER, IDR_STOP_NOBORDER_CENTER, IDR_STOP_H, IDR_STOP_P, + IDR_MENU_BOOKMARK, + IDR_MENU_PAGE, IDR_MENU_PAGE_RTL, + IDR_MENU_CHROME, IDR_MENU_CHROME_RTL, + IDR_MENU_DROPARROW, + IDR_THROBBER, IDR_THROBBER_WAITING, IDR_THROBBER_LIGHT, + IDR_LOCATIONBG +}; + +bool HasButtonImage(int toolbar_button_id) { + static std::map<int, bool> button_images; + if (button_images.empty()) { + for (size_t i = 0; i < arraysize(kToolbarButtonIDs); ++i) + button_images[kToolbarButtonIDs[i]] = true; + } + return button_images.count(toolbar_button_id) > 0; +} + // The image resources we will allow people to theme. const int kThemeableImages[] = { IDR_THEME_FRAME, @@ -134,21 +222,49 @@ bool HasThemeableImage(int themeable_image_id) { return themeable_images.count(themeable_image_id) > 0; } -// Writes the theme pack to disk on a separate thread. -class WritePackToDiskTask : public Task { + +class WriteImagesToDiskTask : public Task { public: - WritePackToDiskTask(BrowserThemePack* pack, const FilePath& path) - : theme_pack_(pack), pack_path_(path) {} + WriteImagesToDiskTask( + const BrowserThemeProvider::ImagesDiskCache& images_disk_cache, + const BrowserThemeProvider::ImageCache& themed_image_cache) : + images_disk_cache_(images_disk_cache), + themed_image_cache_(themed_image_cache) { + } virtual void Run() { - if (!theme_pack_->WriteToDisk(pack_path_)) { - NOTREACHED() << "Could not write theme pack to disk"; + AutoLock lock(BrowserThemeProvider::themed_image_cache_lock_); + for (BrowserThemeProvider::ImagesDiskCache::const_iterator iter( + images_disk_cache_.begin()); iter != images_disk_cache_.end(); + ++iter) { + FilePath image_path = iter->first; + BrowserThemeProvider::ImageCache::const_iterator themed_iter = + themed_image_cache_.find(iter->second); + if (themed_iter != themed_image_cache_.end()) { + SkBitmap* bitmap = themed_iter->second; + std::vector<unsigned char> image_data; + if (!gfx::PNGCodec::EncodeBGRASkBitmap(*bitmap, false, &image_data)) { + NOTREACHED() << "Image file could not be encoded."; + return; + } + const char* image_data_ptr = + reinterpret_cast<const char*>(&image_data[0]); + if (!file_util::WriteFile(image_path, + image_data_ptr, image_data.size())) { + NOTREACHED() << "Image file could not be written to disk."; + return; + } + } else { + NOTREACHED() << "Themed image missing from cache."; + return; + } } } private: - scoped_refptr<BrowserThemePack> theme_pack_; - FilePath pack_path_; + // References to data held in the BrowserThemeProvider. + const BrowserThemeProvider::ImagesDiskCache& images_disk_cache_; + const BrowserThemeProvider::ImageCache& themed_image_cache_; }; } // namespace @@ -159,13 +275,32 @@ bool BrowserThemeProvider::IsThemeableImage(int resource_id) { BrowserThemeProvider::BrowserThemeProvider() : rb_(ResourceBundle::GetSharedInstance()), - profile_(NULL) { + profile_(NULL), + process_images_(false) { // Initialize the themeable image map so we can use it on other threads. HasThemeableImage(0); + resource_names_[IDR_THEME_FRAME] = "theme_frame"; + resource_names_[IDR_THEME_FRAME_INACTIVE] = "theme_frame_inactive"; + resource_names_[IDR_THEME_FRAME_OVERLAY] = "theme_frame_overlay"; + resource_names_[IDR_THEME_FRAME_OVERLAY_INACTIVE] = + "theme_frame_overlay_inactive"; + resource_names_[IDR_THEME_FRAME_INCOGNITO] = "theme_frame_incognito"; + resource_names_[IDR_THEME_FRAME_INCOGNITO_INACTIVE] = + "theme_frame_incognito_inactive"; + resource_names_[IDR_THEME_TAB_BACKGROUND] = "theme_tab_background"; + resource_names_[IDR_THEME_TAB_BACKGROUND_INCOGNITO] = + "theme_tab_background_incognito"; + resource_names_[IDR_THEME_TOOLBAR] = "theme_toolbar"; + resource_names_[IDR_THEME_TAB_BACKGROUND_V] = "theme_tab_background_v"; + resource_names_[IDR_THEME_NTP_BACKGROUND] = "theme_ntp_background"; + resource_names_[IDR_THEME_BUTTON_BACKGROUND] = "theme_button_background"; + resource_names_[IDR_THEME_NTP_ATTRIBUTION] = "theme_ntp_attribution"; + resource_names_[IDR_THEME_WINDOW_CONTROL_BACKGROUND] = + "theme_window_control_background"; } BrowserThemeProvider::~BrowserThemeProvider() { - FreePlatformCaches(); + ClearCaches(); RemoveUnusedThemes(); } @@ -174,37 +309,120 @@ void BrowserThemeProvider::Init(Profile* profile) { DCHECK(CalledOnValidThread()); profile_ = profile; + image_dir_ = profile_->GetPath().Append(chrome::kThemeImagesDirname); + if (!file_util::PathExists(image_dir_)) + file_util::CreateDirectory(image_dir_); + LoadThemePrefs(); } SkBitmap* BrowserThemeProvider::GetBitmapNamed(int id) const { DCHECK(CalledOnValidThread()); - SkBitmap* bitmap = NULL; - - if (theme_pack_.get()) - bitmap = theme_pack_->GetBitmapNamed(id); - - if (!bitmap) - bitmap = rb_.GetBitmapNamed(id); + // First check to see if the Skia image is in the themed cache. The themed + // cache is not changed in this method, so it can remain unlocked. + ImageCache::const_iterator themed_iter = themed_image_cache_.find(id); + if (themed_iter != themed_image_cache_.end()) + return themed_iter->second; + + // If Skia image is not in themed cache, check regular cache, and possibly + // generate and store. + ImageCache::const_iterator image_iter = image_cache_.find(id); + if (image_iter != image_cache_.end()) + return image_iter->second; + + scoped_ptr<SkBitmap> result; + + // Try to load the image from the extension. + result.reset(LoadThemeBitmap(id)); + + // If we still don't have an image, load it from resourcebundle. + if (!result.get()) + result.reset(new SkBitmap(*rb_.GetBitmapNamed(id))); + + if (result.get()) { + // If the requested image is part of the toolbar button set, and we have + // a provided tint for that set, tint it appropriately. + if (HasButtonImage(id) && tints_.count(kTintButtons)) { + SkBitmap* tinted = + new SkBitmap(TintBitmap(*result.release(), TINT_BUTTONS)); + result.reset(tinted); + } - return bitmap; + // 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) const { DCHECK(CalledOnValidThread()); - SkColor color; - if (theme_pack_.get() && theme_pack_->GetColor(id, &color)) - return color; + // Special-case NTP header - if the color isn't provided, we fall back to + // the section color. + if (id == COLOR_NTP_HEADER) { + ColorMap::const_iterator color_iter = colors_.find(kColorNTPHeader); + if (color_iter != colors_.end()) + return color_iter->second; + color_iter = colors_.find(kColorNTPSection); + return (color_iter == colors_.end()) ? + GetDefaultColor(id) : color_iter->second; + } + + // Special case the underline colors to use semi transparent in case not + // defined. + if (id == COLOR_NTP_SECTION_LINK_UNDERLINE) { + ColorMap::const_iterator color_iter = + colors_.find(kColorNTPSectionLinkUnderline); + if (color_iter != colors_.end()) + return color_iter->second; + SkColor color_section_link = GetColor(COLOR_NTP_SECTION_LINK); + return SkColorSetA(color_section_link, SkColorGetA(color_section_link) / 3); + } - return GetDefaultColor(id); + if (id == COLOR_NTP_LINK_UNDERLINE) { + ColorMap::const_iterator color_iter = colors_.find(kColorNTPLinkUnderline); + if (color_iter != colors_.end()) + return color_iter->second; + SkColor color_link = GetColor(COLOR_NTP_LINK); + return SkColorSetA(color_link, SkColorGetA(color_link) / 3); + } + + // TODO(glen): Figure out if we need to tint these. http://crbug.com/11578 + ColorMap::const_iterator color_iter = colors_.find(GetColorKey(id)); + return (color_iter == colors_.end()) ? + GetDefaultColor(id) : color_iter->second; } bool BrowserThemeProvider::GetDisplayProperty(int id, int* result) const { - if (theme_pack_.get()) - return theme_pack_->GetDisplayProperty(id, result); - + switch (id) { + case NTP_BACKGROUND_ALIGNMENT: { + DisplayPropertyMap::const_iterator display_iter = + display_properties_.find(kDisplayPropertyNTPAlignment); + *result = (display_iter == display_properties_.end()) ? + kDefaultDisplayPropertyNTPAlignment : display_iter->second; + return true; + } + case NTP_BACKGROUND_TILING: { + DisplayPropertyMap::const_iterator display_iter = + display_properties_.find(kDisplayPropertyNTPTiling); + *result = (display_iter == display_properties_.end()) ? + kDefaultDisplayPropertyNTPTiling : display_iter->second; + return true; + } + case NTP_LOGO_ALTERNATE: { + DisplayPropertyMap::const_iterator display_iter = + display_properties_.find(kDisplayPropertyNTPInverseLogo); + *result = (display_iter == display_properties_.end()) ? + kDefaultDisplayPropertyNTPInverseLogo : display_iter->second; + return true; + } + default: + NOTREACHED() << "Unknown property requested"; + } return false; } @@ -222,10 +440,15 @@ bool BrowserThemeProvider::HasCustomImage(int id) const { if (!HasThemeableImage(id)) return false; - if (theme_pack_) - return theme_pack_->HasCustomImage(id); - - return false; + // A custom image = base name is NOT equal to resource name. See note in + // SaveThemeBitmap describing the process by which an original image is + // tagged. + ImageMap::const_iterator images_iter = images_.find(id); + ResourceNameMap::const_iterator names_iter = resource_names_.find(id); + if ((images_iter == images_.end()) || (names_iter == resource_names_.end())) + return false; + return !EndsWith(UTF8ToWide(images_iter->second), + UTF8ToWide(names_iter->second), false); } RefCountedMemory* BrowserThemeProvider::GetRawData(int id) const { @@ -235,25 +458,57 @@ RefCountedMemory* BrowserThemeProvider::GetRawData(int id) const { if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0) id = IDR_PRODUCT_LOGO_WHITE; - RefCountedMemory* data = NULL; - if (theme_pack_.get()) - data = theme_pack_->GetRawData(id); + RawDataMap::const_iterator data_iter = raw_data_.find(id); + if (data_iter != raw_data_.end()) + return data_iter->second; + + RefCountedMemory* data = ReadThemeFileData(id); if (!data) data = rb_.LoadDataResourceBytes(id); + if (!data) + return NULL; + raw_data_[id] = data; return data; } void BrowserThemeProvider::SetTheme(Extension* extension) { // Clear our image cache. - FreePlatformCaches(); + ClearCaches(); DCHECK(extension); DCHECK(extension->IsTheme()); + SetImageData(extension->GetThemeImages(), + extension->path()); + SetColorData(extension->GetThemeColors()); + SetTintData(extension->GetThemeTints()); + + // Drop out to default theme if the theme data is empty. + if (images_.empty() && colors_.empty() && tints_.empty()) { + UseDefaultTheme(); + return; + } + + SetDisplayPropertyData(extension->GetThemeDisplayProperties()); + raw_data_.clear(); - BuildFromExtension(extension); + SaveImageData(extension->GetThemeImages()); + SaveColorData(); + SaveTintData(); + SaveDisplayPropertyData(); SaveThemeID(extension->id()); + // Process all images when we first set theme. + process_images_ = true; + + GenerateFrameColors(); + if (ShouldTintFrames()) { + AutoLock lock(themed_image_cache_lock_); + GenerateFrameImages(); + GenerateTabImages(); + } + + WriteImagesToDisk(); NotifyThemeChanged(); UserMetrics::RecordAction("Themes_Installed", profile_); } @@ -288,6 +543,36 @@ std::string BrowserThemeProvider::GetThemeID() const { return WideToUTF8(id); } +RefCountedMemory* BrowserThemeProvider::ReadThemeFileData(int id) const { + ImageMap::const_iterator images_iter = images_.find(id); + if (images_iter != images_.end()) { + // First check to see if we have a registered theme extension and whether + // it can handle this resource. +#if defined(OS_WIN) + FilePath path = FilePath(UTF8ToWide(images_iter->second)); +#else + FilePath path = FilePath(images_iter->second); +#endif + 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); + std::vector<unsigned char> raw_data; + raw_data.resize(size); + char* data = reinterpret_cast<char*>(&(raw_data.front())); + if (file.ReadUntilComplete(data, size) == avail) + return RefCountedBytes::TakeVector(&raw_data); + } + } + } + } + + return NULL; +} + // static std::string BrowserThemeProvider::AlignmentToString(int alignment) { // Convert from an AlignmentProperty back into a string. @@ -361,8 +646,313 @@ int BrowserThemeProvider::StringToTiling(const std::string& tiling) { return BrowserThemeProvider::NO_REPEAT; } -// static -color_utils::HSL BrowserThemeProvider::GetDefaultTint(int id) { +void BrowserThemeProvider::SetColor(const char* key, const SkColor& color) { + colors_[key] = color; +} + +void BrowserThemeProvider::SetTint(const char* key, + const color_utils::HSL& tint) { + tints_[key] = tint; +} + +color_utils::HSL BrowserThemeProvider::GetTint(int id) const { + DCHECK(CalledOnValidThread()); + + TintMap::const_iterator tint_iter = tints_.find(GetTintKey(id)); + return (tint_iter == tints_.end()) ? GetDefaultTint(id) : tint_iter->second; +} + +void BrowserThemeProvider::GenerateFrameColors() { + // Generate any secondary frame colors that weren't provided. + SkColor frame = GetColor(COLOR_FRAME); + + if (!colors_.count(kColorFrame)) + colors_[kColorFrame] = HSLShift(frame, GetTint(TINT_FRAME)); + if (!colors_.count(kColorFrameInactive)) { + colors_[kColorFrameInactive] = + HSLShift(frame, GetTint(TINT_FRAME_INACTIVE)); + } + if (!colors_.count(kColorFrameIncognito)) { + colors_[kColorFrameIncognito] = + HSLShift(frame, GetTint(TINT_FRAME_INCOGNITO)); + } + if (!colors_.count(kColorFrameIncognitoInactive)) { + colors_[kColorFrameIncognitoInactive] = + HSLShift(frame, GetTint(TINT_FRAME_INCOGNITO_INACTIVE)); + } +} + +void BrowserThemeProvider::GenerateFrameImages() const { + // A map of frame image IDs to the tints for those ids. + typedef std::map<int, int> FrameTintMap; + static FrameTintMap frame_tints; + if (frame_tints.empty()) { + frame_tints[IDR_THEME_FRAME] = TINT_FRAME; + frame_tints[IDR_THEME_FRAME_INACTIVE] = TINT_FRAME_INACTIVE; + frame_tints[IDR_THEME_FRAME_OVERLAY] = TINT_FRAME; + frame_tints[IDR_THEME_FRAME_OVERLAY_INACTIVE] = TINT_FRAME_INACTIVE; + frame_tints[IDR_THEME_FRAME_INCOGNITO] = TINT_FRAME_INCOGNITO; + frame_tints[IDR_THEME_FRAME_INCOGNITO_INACTIVE] = + TINT_FRAME_INCOGNITO_INACTIVE; + } + + for (FrameTintMap::const_iterator iter(frame_tints.begin()); + iter != frame_tints.end(); ++iter) { + int id = iter->first; + scoped_ptr<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; + std::string resource_name; + + // If we've already processed the images for this theme, they're all + // waiting on disk -- just load them in. + if (!process_images_) { + frame.reset(LoadThemeBitmap(id)); + if (frame.get()) + themed_image_cache_[id] = new SkBitmap(*frame.get()); + } else { + resource_name = resource_names_.find(id)->second; + if (id == IDR_THEME_FRAME_INCOGNITO_INACTIVE) { + base_id = HasCustomImage(IDR_THEME_FRAME_INCOGNITO) ? + IDR_THEME_FRAME_INCOGNITO : IDR_THEME_FRAME; + } else if (id == IDR_THEME_FRAME_OVERLAY_INACTIVE) { + base_id = IDR_THEME_FRAME_OVERLAY; + } else if (id == IDR_THEME_FRAME_INACTIVE) { + base_id = IDR_THEME_FRAME; + } else if (id == IDR_THEME_FRAME_INCOGNITO && + !HasCustomImage(IDR_THEME_FRAME_INCOGNITO)) { + base_id = IDR_THEME_FRAME; + } else { + base_id = id; + } + + if (HasCustomImage(id)) { + frame.reset(LoadThemeBitmap(id)); + } else if (base_id != id && HasCustomImage(base_id)) { + frame.reset(LoadThemeBitmap(base_id)); + } else if (base_id == IDR_THEME_FRAME_OVERLAY && + HasCustomImage(IDR_THEME_FRAME)) { + // If there is no theme overlay, don't tint the default frame, + // because it will overwrite the custom frame image when we cache and + // reload from disk. + frame.reset(NULL); + } else { + // If the theme doesn't specify an image, then apply the tint to + // the default frame. + frame.reset(new SkBitmap(*rb_.GetBitmapNamed(IDR_THEME_FRAME))); + } + + if (frame.get()) { + SkBitmap* tinted = new SkBitmap(TintBitmap(*frame, iter->second)); + themed_image_cache_[id] = tinted; + SaveThemeBitmap(resource_name, id); + } + } + } +} + +void BrowserThemeProvider::GenerateTabImages() const { + GenerateTabBackgroundBitmap(IDR_THEME_TAB_BACKGROUND); + GenerateTabBackgroundBitmap(IDR_THEME_TAB_BACKGROUND_INCOGNITO); +} + +void BrowserThemeProvider::ClearAllThemeData() { + // Clear our image cache. + ClearCaches(); + + images_.clear(); + colors_.clear(); + tints_.clear(); + display_properties_.clear(); + raw_data_.clear(); + + SaveImageData(NULL); + SaveColorData(); + SaveTintData(); + SaveDisplayPropertyData(); + SaveThemeID(kDefaultThemeID); +} + +void BrowserThemeProvider::LoadThemePrefs() { + process_images_ = false; + PrefService* prefs = profile_->GetPrefs(); + + // 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)) + return; + + // 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)); + SetDisplayPropertyData( + prefs->GetMutableDictionary(prefs::kCurrentThemeDisplayProperties)); + + // If we're not loading the frame from the cached image dir, we are using an + // old preferences file, or the processed images were not saved correctly. + // Force image reprocessing and caching. + ImageMap::const_iterator images_iter = images_.find(IDR_THEME_FRAME); + if (images_iter != images_.end()) { +#if defined(OS_WIN) + FilePath cache_path = FilePath(UTF8ToWide(images_iter->second)); +#else + FilePath cache_path = FilePath(images_iter->second); +#endif + process_images_ = !file_util::ContainsPath(image_dir_, cache_path); + } + + GenerateFrameColors(); + // Scope for AutoLock on themed_image_cache. + { + AutoLock lock(themed_image_cache_lock_); + GenerateFrameImages(); + GenerateTabImages(); + } + + if (process_images_) { + WriteImagesToDisk(); + UserMetrics::RecordAction("Migrated noncached to cached theme.", profile_); + } + UserMetrics::RecordAction("Themes_loaded", profile_); +} + +void BrowserThemeProvider::NotifyThemeChanged() { + // Redraw! + NotificationService* service = NotificationService::current(); + service->Notify(NotificationType::BROWSER_THEME_CHANGED, + Source<BrowserThemeProvider>(this), + NotificationService::NoDetails()); +} + +SkBitmap* BrowserThemeProvider::LoadThemeBitmap(int id) const { + DCHECK(CalledOnValidThread()); + + if (!HasThemeableImage(id)) + return NULL; + + scoped_refptr<RefCountedMemory> raw_data; + + // We special case images related to the NTP so we first try raw data. Why? + // Because the DOMUI stuff uses that interface to return raw PNG data instead + // of the normal theme interface which returns SkBitmaps. GetRawData() also + // caches the PNG data so it opens new tab pages faster. If we didn't try and + // otherwise we would be loading big images twice, once through GetRawData() + // and once here. Ouch. So either we prime the GetRawData() cache for when + // DOMUIThemeSource requests our image, or we take advantage of the already + // loaded data, saving a trip to disk. + if (id == IDR_THEME_NTP_BACKGROUND) + raw_data = GetRawData(id); + + if (!raw_data) + raw_data = ReadThemeFileData(id); + + if (raw_data) { + // Decode the PNG. + SkBitmap bitmap; + if (!gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(), + &bitmap)) { + NOTREACHED() << "Unable to decode theme image resource " << id; + return NULL; + } + + return new SkBitmap(bitmap); + } 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; + } +} + +void BrowserThemeProvider::SaveThemeBitmap(std::string resource_name, + int id) const { + DCHECK(CalledOnValidThread()); + if (!themed_image_cache_.count(id)) { + NOTREACHED(); + return; + } + + // The images_ directory, at this point, contains only the custom images + // provided by the extension. We tag these images "_original" in the prefs + // file so we can distinguish them from images which have been generated and + // saved to disk by the theme caching process (WriteImagesToDisk). This way, + // when we call HasCustomImage, we can check for the "_original" tag to see + // whether an image was originally provided by the extension, or saved + // in the caching process. + if (images_.count(id)) + resource_name.append("_original"); + +#if defined(OS_WIN) + FilePath image_path = image_dir_.Append(UTF8ToWide(resource_name)); +#elif defined(OS_POSIX) + FilePath image_path = image_dir_.Append(resource_name); +#endif + + images_disk_cache_[image_path] = id; +} + +#if defined(OS_WIN) +void BrowserThemeProvider::FreePlatformCaches() { + // Views (Skia) has no platform image cache to clear. +} +#endif + +SkBitmap* BrowserThemeProvider::GenerateTabBackgroundBitmapImpl(int id) const { + int base_id = (id == IDR_THEME_TAB_BACKGROUND) ? + IDR_THEME_FRAME : IDR_THEME_FRAME_INCOGNITO; + // According to Miranda, it is safe to read from the themed_image_cache_ here + // because we only lock to write on the UI thread, and we only lock to read + // on the cache writing thread. + ImageCache::const_iterator themed_iter = themed_image_cache_.find(base_id); + if (themed_iter == themed_image_cache_.end()) + return NULL; + + SkBitmap bg_tint = TintBitmap(*(themed_iter->second), TINT_BACKGROUND_TAB); + int vertical_offset = HasCustomImage(id) ? kRestoredTabVerticalOffset : 0; + SkBitmap* bg_tab = new SkBitmap(SkBitmapOperations::CreateTiledBitmap( + bg_tint, 0, vertical_offset, bg_tint.width(), bg_tint.height())); + + // If they've provided a custom image, overlay it. + if (HasCustomImage(id)) { + SkBitmap* overlay = LoadThemeBitmap(id); + if (overlay) { + SkCanvas canvas(*bg_tab); + for (int x = 0; x < bg_tab->width(); x += overlay->width()) + canvas.drawBitmap(*overlay, static_cast<SkScalar>(x), 0, NULL); + } + } + + return bg_tab; +} + +const std::string BrowserThemeProvider::GetTintKey(int id) const { + switch (id) { + case TINT_FRAME: + return kTintFrame; + case TINT_FRAME_INACTIVE: + return kTintFrameInactive; + case TINT_FRAME_INCOGNITO: + return kTintFrameIncognito; + case TINT_FRAME_INCOGNITO_INACTIVE: + return kTintFrameIncognitoInactive; + case TINT_BUTTONS: + return kTintButtons; + case TINT_BACKGROUND_TAB: + return kTintBackgroundTab; + default: + NOTREACHED() << "Unknown tint requested"; + return ""; + } +} + +color_utils::HSL BrowserThemeProvider::GetDefaultTint(int id) const { switch (id) { case TINT_FRAME: return kDefaultTintFrame; @@ -382,8 +972,53 @@ color_utils::HSL BrowserThemeProvider::GetDefaultTint(int id) { } } -// static -SkColor BrowserThemeProvider::GetDefaultColor(int id) { +const std::string BrowserThemeProvider::GetColorKey(int id) const { + switch (id) { + case COLOR_FRAME: + return kColorFrame; + case COLOR_FRAME_INACTIVE: + return kColorFrameInactive; + case COLOR_FRAME_INCOGNITO: + return kColorFrameIncognito; + case COLOR_FRAME_INCOGNITO_INACTIVE: + return kColorFrameIncognitoInactive; + case COLOR_TOOLBAR: + return kColorToolbar; + case COLOR_TAB_TEXT: + return kColorTabText; + case COLOR_BACKGROUND_TAB_TEXT: + return kColorBackgroundTabText; + case COLOR_BOOKMARK_TEXT: + return kColorBookmarkText; + case COLOR_NTP_BACKGROUND: + return kColorNTPBackground; + case COLOR_NTP_TEXT: + return kColorNTPText; + case COLOR_NTP_LINK: + return kColorNTPLink; + case COLOR_NTP_LINK_UNDERLINE: + return kColorNTPLinkUnderline; + case COLOR_NTP_HEADER: + return kColorNTPHeader; + case COLOR_NTP_SECTION: + return kColorNTPSection; + case COLOR_NTP_SECTION_TEXT: + return kColorNTPSectionText; + case COLOR_NTP_SECTION_LINK: + return kColorNTPSectionLink; + case COLOR_NTP_SECTION_LINK_UNDERLINE: + return kColorNTPSectionLinkUnderline; + case COLOR_CONTROL_BACKGROUND: + return kColorControlBackground; + case COLOR_BUTTON_BACKGROUND: + return kColorButtonBackground; + default: + NOTREACHED() << "Unknown color requested"; + return ""; + } +} + +SkColor BrowserThemeProvider::GetDefaultColor(int id) const { switch (id) { case COLOR_FRAME: return kDefaultColorFrame; @@ -407,8 +1042,6 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) { return kDefaultColorNTPText; case COLOR_NTP_LINK: return kDefaultColorNTPLink; - case COLOR_NTP_LINK_UNDERLINE: - return TintForUnderline(kDefaultColorNTPLink); case COLOR_NTP_HEADER: return kDefaultColorNTPHeader; case COLOR_NTP_SECTION: @@ -417,8 +1050,6 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) { return kDefaultColorNTPSectionText; case COLOR_NTP_SECTION_LINK: return kDefaultColorNTPSectionLink; - case COLOR_NTP_SECTION_LINK_UNDERLINE: - return TintForUnderline(kDefaultColorNTPSectionLink); case COLOR_CONTROL_BACKGROUND: return kDefaultColorControlBackground; case COLOR_BUTTON_BACKGROUND: @@ -429,97 +1060,289 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) { } } -color_utils::HSL BrowserThemeProvider::GetTint(int id) const { - DCHECK(CalledOnValidThread()); +SkBitmap BrowserThemeProvider::TintBitmap(const SkBitmap& bitmap, + int hsl_id) const { + return SkBitmapOperations::CreateHSLShiftedBitmap(bitmap, GetTint(hsl_id)); +} + +void BrowserThemeProvider::SetImageData(DictionaryValue* images_value, + FilePath images_path) { + images_.clear(); - color_utils::HSL hsl; - if (theme_pack_.get() && theme_pack_->GetTint(id, &hsl)) - return hsl; + if (!images_value) + return; - return GetDefaultTint(id); + for (DictionaryValue::key_iterator iter(images_value->begin_keys()); + iter != images_value->end_keys(); ++iter) { + std::string val; + if (images_value->GetStringWithoutPathExpansion(*iter, &val)) { + int id = ThemeResourcesUtil::GetId(WideToUTF8(*iter)); + if (id != -1) { + if (!images_path.empty()) { + images_[id] = + WideToUTF8(images_path.AppendASCII(val).ToWStringHack()); + resource_names_[id] = WideToASCII(*iter); + } else { + images_[id] = val; + } + } + } + } } -void BrowserThemeProvider::ClearAllThemeData() { - // Clear our image cache. - FreePlatformCaches(); - theme_pack_ = NULL; +void BrowserThemeProvider::SetColorData(DictionaryValue* colors_value) { + colors_.clear(); - profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename); - SaveThemeID(kDefaultThemeID); + if (!colors_value) + return; + + for (DictionaryValue::key_iterator iter(colors_value->begin_keys()); + iter != colors_value->end_keys(); ++iter) { + ListValue* color_list; + if (colors_value->GetListWithoutPathExpansion(*iter, &color_list) && + ((color_list->GetSize() == 3) || (color_list->GetSize() == 4))) { + int r, g, b; + color_list->GetInteger(0, &r); + color_list->GetInteger(1, &g); + color_list->GetInteger(2, &b); + if (color_list->GetSize() == 4) { + double alpha; + int alpha_int; + if (color_list->GetReal(3, &alpha)) { + colors_[WideToUTF8(*iter)] = + SkColorSetARGB(static_cast<int>(alpha * 255), r, g, b); + } else if (color_list->GetInteger(3, &alpha_int) && + (alpha_int == 0 || alpha_int == 1)) { + colors_[WideToUTF8(*iter)] = + SkColorSetARGB(alpha_int ? 255 : 0, r, g, b); + } + } else { + colors_[WideToUTF8(*iter)] = SkColorSetRGB(r, g, b); + } + } + } } -void BrowserThemeProvider::LoadThemePrefs() { - PrefService* prefs = profile_->GetPrefs(); +void BrowserThemeProvider::SetTintData(DictionaryValue* tints_value) { + tints_.clear(); - std::string current_id = GetThemeID(); - if (current_id != kDefaultThemeID) { - bool loaded_pack = false; + if (!tints_value) + return; - // If we don't have a file pack, we're updating from an old version. - FilePath path = prefs->GetFilePath(prefs::kCurrentThemePackFilename); - if (path != FilePath()) { - theme_pack_ = BrowserThemePack::BuildFromDataPack(path, current_id); - loaded_pack = theme_pack_.get() != NULL; + for (DictionaryValue::key_iterator iter(tints_value->begin_keys()); + iter != tints_value->end_keys(); ++iter) { + ListValue* tint_list; + if (tints_value->GetListWithoutPathExpansion(*iter, &tint_list) && + (tint_list->GetSize() == 3)) { + color_utils::HSL hsl = { -1, -1, -1 }; + int value = 0; + if (!tint_list->GetReal(0, &hsl.h) && tint_list->GetInteger(0, &value)) + hsl.h = value; + if (!tint_list->GetReal(1, &hsl.s) && tint_list->GetInteger(1, &value)) + hsl.s = value; + if (!tint_list->GetReal(2, &hsl.l) && tint_list->GetInteger(2, &value)) + hsl.l = value; + + tints_[WideToUTF8(*iter)] = hsl; } + } +} - if (loaded_pack) { - UserMetrics::RecordAction("Themes.Loaded", profile_); +void BrowserThemeProvider::SetDisplayPropertyData( + DictionaryValue* display_properties_value) { + display_properties_.clear(); + + if (!display_properties_value) + return; + + for (DictionaryValue::key_iterator iter( + display_properties_value->begin_keys()); + iter != display_properties_value->end_keys(); ++iter) { + // New tab page alignment and background tiling. + if (base::strcasecmp(WideToUTF8(*iter).c_str(), + kDisplayPropertyNTPAlignment) == 0) { + std::string val; + if (display_properties_value->GetStringWithoutPathExpansion(*iter, + &val)) { + display_properties_[kDisplayPropertyNTPAlignment] = + StringToAlignment(val); + } + } else if (base::strcasecmp(WideToUTF8(*iter).c_str(), + kDisplayPropertyNTPTiling) == 0) { + std::string val; + if (display_properties_value->GetStringWithoutPathExpansion(*iter, &val)) + display_properties_[kDisplayPropertyNTPTiling] = StringToTiling(val); + } + if (base::strcasecmp(WideToUTF8(*iter).c_str(), + kDisplayPropertyNTPInverseLogo) == 0) { + int val = 0; + if (display_properties_value->GetIntegerWithoutPathExpansion(*iter, &val)) + display_properties_[kDisplayPropertyNTPInverseLogo] = val; + } + } +} + +SkBitmap* BrowserThemeProvider::GenerateTabBackgroundBitmap(int id) const { + 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. + if (!process_images_) { + scoped_ptr<SkBitmap> frame; + frame.reset(LoadThemeBitmap(id)); + if (frame.get()) + themed_image_cache_[id] = new SkBitmap(*frame.get()); } else { - // TODO(erg): We need to pop up a dialog informing the user that their - // theme is being migrated. - ExtensionsService* service = profile_->GetExtensionsService(); - if (service) { - Extension* extension = service->GetExtensionById(current_id, false); - if (extension) { - DLOG(ERROR) << "Migrating theme"; - BuildFromExtension(extension); - UserMetrics::RecordAction("Themes.Migrated", profile_); - } else { - DLOG(ERROR) << "Theme is mysteriously gone."; - ClearAllThemeData(); - UserMetrics::RecordAction("Themes.Gone", profile_); - } + SkBitmap* bg_tab = GenerateTabBackgroundBitmapImpl(id); + + if (bg_tab) { + std::string resource_name((id == IDR_THEME_TAB_BACKGROUND) ? + "theme_tab_background" : "theme_tab_background_incognito"); + themed_image_cache_[id] = bg_tab; + SaveThemeBitmap(resource_name, id); + return bg_tab; } } } + return NULL; } -void BrowserThemeProvider::NotifyThemeChanged() { - // Redraw! - NotificationService* service = NotificationService::current(); - service->Notify(NotificationType::BROWSER_THEME_CHANGED, - Source<BrowserThemeProvider>(this), - NotificationService::NoDetails()); +void BrowserThemeProvider::SaveImageData(DictionaryValue* images_value) const { + // Save our images data. + DictionaryValue* pref_images = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeImages); + pref_images->Clear(); + + if (!images_value) + return; + + for (DictionaryValue::key_iterator iter(images_value->begin_keys()); + iter != images_value->end_keys(); ++iter) { + std::string val; + if (images_value->GetStringWithoutPathExpansion(*iter, &val)) { + int id = ThemeResourcesUtil::GetId(WideToUTF8(*iter)); + if (id != -1) { + pref_images->SetWithoutPathExpansion(*iter, + Value::CreateStringValue(images_.find(id)->second)); + } + } + } } -#if defined(OS_WIN) -void BrowserThemeProvider::FreePlatformCaches() { - // Views (Skia) has no platform image cache to clear. +void BrowserThemeProvider::SaveColorData() const { + // Save our color data. + DictionaryValue* pref_colors = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeColors); + pref_colors->Clear(); + + if (colors_.empty()) + return; + + for (ColorMap::const_iterator iter(colors_.begin()); iter != colors_.end(); + ++iter) { + SkColor rgba = iter->second; + ListValue* rgb_list = new ListValue(); + rgb_list->Set(0, Value::CreateIntegerValue(SkColorGetR(rgba))); + rgb_list->Set(1, Value::CreateIntegerValue(SkColorGetG(rgba))); + rgb_list->Set(2, Value::CreateIntegerValue(SkColorGetB(rgba))); + if (SkColorGetA(rgba) != 255) + rgb_list->Set(3, Value::CreateRealValue(SkColorGetA(rgba) / 255.0)); + pref_colors->Set(UTF8ToWide(iter->first), rgb_list); + } } -#endif -void BrowserThemeProvider::SavePackName(const FilePath& pack_path) { - profile_->GetPrefs()->SetFilePath( - prefs::kCurrentThemePackFilename, pack_path); +void BrowserThemeProvider::SaveTintData() const { + // Save our tint data. + DictionaryValue* pref_tints = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeTints); + pref_tints->Clear(); + + if (tints_.empty()) + return; + + for (TintMap::const_iterator iter(tints_.begin()); iter != tints_.end(); + ++iter) { + color_utils::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); + } +} + +void BrowserThemeProvider::SaveDisplayPropertyData() const { + // Save our display property data. + DictionaryValue* pref_display_properties = + profile_->GetPrefs()-> + GetMutableDictionary(prefs::kCurrentThemeDisplayProperties); + pref_display_properties->Clear(); + + if (display_properties_.empty()) + return; + + for (DisplayPropertyMap::const_iterator iter(display_properties_.begin()); + iter != display_properties_.end(); ++iter) { + if (base::strcasecmp(iter->first.c_str(), + kDisplayPropertyNTPAlignment) == 0) { + pref_display_properties->SetString(UTF8ToWide(iter->first), + AlignmentToString(iter->second)); + } else if (base::strcasecmp(iter->first.c_str(), + kDisplayPropertyNTPTiling) == 0) { + pref_display_properties->SetString(UTF8ToWide(iter->first), + TilingToString(iter->second)); + } + if (base::strcasecmp(iter->first.c_str(), + kDisplayPropertyNTPInverseLogo) == 0) { + pref_display_properties->SetInteger(UTF8ToWide(iter->first), + iter->second); + } + } +} + +void BrowserThemeProvider::SaveCachedImageData() const { + DictionaryValue* pref_images = + profile_->GetPrefs()->GetMutableDictionary(prefs::kCurrentThemeImages); + + for (ImagesDiskCache::const_iterator it(images_disk_cache_.begin()); + it != images_disk_cache_.end(); ++it) { + std::wstring disk_path = it->first.ToWStringHack(); + std::string pref_name = resource_names_.find(it->second)->second; + pref_images->SetString(UTF8ToWide(pref_name), WideToUTF8(disk_path)); + } + profile_->GetPrefs()->ScheduleSavePersistentPrefs(); } void BrowserThemeProvider::SaveThemeID(const std::string& id) { profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, UTF8ToWide(id)); } -void BrowserThemeProvider::BuildFromExtension(Extension* extension) { - scoped_refptr<BrowserThemePack> pack = - BrowserThemePack::BuildFromExtension(extension); - if (!pack.get()) { - NOTREACHED() << "Could not load theme."; - return; +void BrowserThemeProvider::ClearCaches() { + FreePlatformCaches(); + STLDeleteValues(&image_cache_); + + // Scope for AutoLock on themed_image_cache. + { + AutoLock lock(themed_image_cache_lock_); + STLDeleteValues(&themed_image_cache_); } - // Write the packed file to disk. - FilePath pack_path = extension->path().Append(chrome::kThemePackFilename); - ChromeThread::PostTask(ChromeThread::IO, FROM_HERE, - new WritePackToDiskTask(pack, pack_path)); + images_disk_cache_.clear(); +} + +void BrowserThemeProvider::WriteImagesToDisk() const { + g_browser_process->file_thread()->message_loop()->PostTask(FROM_HERE, + new WriteImagesToDiskTask(images_disk_cache_, themed_image_cache_)); + SaveCachedImageData(); +} - SavePackName(pack_path); - theme_pack_ = pack; +bool BrowserThemeProvider::ShouldTintFrames() const { + return (HasCustomImage(IDR_THEME_FRAME) || + tints_.count(GetTintKey(TINT_BACKGROUND_TAB)) || + tints_.count(GetTintKey(TINT_FRAME)) || + tints_.count(GetTintKey(TINT_FRAME_INACTIVE)) || + tints_.count(GetTintKey(TINT_FRAME_INCOGNITO)) || + tints_.count(GetTintKey(TINT_FRAME_INCOGNITO_INACTIVE))); } diff --git a/chrome/browser/browser_theme_provider.h b/chrome/browser/browser_theme_provider.h index a06d7ee..04117b4 100644 --- a/chrome/browser/browser_theme_provider.h +++ b/chrome/browser/browser_theme_provider.h @@ -22,13 +22,48 @@ class Profile; class DictionaryValue; class PrefService; class BrowserThemeProviderTest; -class BrowserThemePack; class BrowserThemeProvider : public NonThreadSafe, public ThemeProvider { public: // Public constants used in BrowserThemeProvider and its subclasses: + // Strings used by themes to identify colors for different parts of our UI. + static const char* kColorFrame; + static const char* kColorFrameInactive; + static const char* kColorFrameIncognito; + static const char* kColorFrameIncognitoInactive; + static const char* kColorToolbar; + static const char* kColorTabText; + static const char* kColorBackgroundTabText; + static const char* kColorBookmarkText; + static const char* kColorNTPBackground; + static const char* kColorNTPText; + static const char* kColorNTPLink; + static const char* kColorNTPLinkUnderline; + static const char* kColorNTPHeader; + static const char* kColorNTPSection; + static const char* kColorNTPSectionText; + static const char* kColorNTPSectionLink; + static const char* kColorNTPSectionLinkUnderline; + static const char* kColorControlBackground; + static const char* kColorButtonBackground; + + // 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 char* kTintButtons; + static const char* kTintFrame; + static const char* kTintFrameInactive; + static const char* kTintFrameIncognito; + static const char* kTintFrameIncognitoInactive; + static const char* kTintBackgroundTab; + + // Strings used by themes to identify miscellaneous numerical properties. + static const char* kDisplayPropertyNTPAlignment; + static const char* kDisplayPropertyNTPTiling; + static const char* kDisplayPropertyNTPInverseLogo; + // Strings used in alignment properties. static const char* kAlignmentTop; static const char* kAlignmentBottom; @@ -41,11 +76,38 @@ class BrowserThemeProvider : public NonThreadSafe, static const char* kTilingRepeatY; static const char* kTilingRepeat; + // Default colors. + static const SkColor kDefaultColorFrame; + static const SkColor kDefaultColorFrameInactive; + static const SkColor kDefaultColorFrameIncognito; + static const SkColor kDefaultColorFrameIncognitoInactive; + static const SkColor kDefaultColorToolbar; + static const SkColor kDefaultColorTabText; + static const SkColor kDefaultColorBackgroundTabText; + static const SkColor kDefaultColorBookmarkText; + static const SkColor kDefaultColorNTPBackground; + static const SkColor kDefaultColorNTPText; + static const SkColor kDefaultColorNTPLink; + static const SkColor kDefaultColorNTPHeader; + static const SkColor kDefaultColorNTPSection; + static const SkColor kDefaultColorNTPSectionText; + static const SkColor kDefaultColorNTPSectionLink; + static const SkColor kDefaultColorControlBackground; + static const SkColor kDefaultColorButtonBackground; + + static const color_utils::HSL kDefaultTintButtons; + static const color_utils::HSL kDefaultTintFrame; + static const color_utils::HSL kDefaultTintFrameInactive; + static const color_utils::HSL kDefaultTintFrameIncognito; + static const color_utils::HSL kDefaultTintFrameIncognitoInactive; + static const color_utils::HSL kDefaultTintBackgroundTab; + static const char* kDefaultThemeID; // Returns true if the image is themeable. Safe to call on any thread. static bool IsThemeableImage(int resource_id); + BrowserThemeProvider(); virtual ~BrowserThemeProvider(); @@ -128,6 +190,10 @@ class BrowserThemeProvider : public NonThreadSafe, // locally customized.) std::string GetThemeID() const; + // Reads the image data from the theme file into the specified vector. Returns + // true on success. + RefCountedMemory* ReadThemeFileData(int id) const; + // Convert a bitfield alignment into a string like "top left". Public so that // it can be used to generate CSS values. Takes a bitfield of AlignmentMasks. static std::string AlignmentToString(int alignment); @@ -143,21 +209,44 @@ class BrowserThemeProvider : public NonThreadSafe, // Parse tiling values from something like "no-repeat" into a Tiling value. static int StringToTiling(const std::string& tiling); - // Returns the default tint for the given tint |id| TINT_* enum value. - static color_utils::HSL GetDefaultTint(int id); - - // Returns the default color for the given color |id| COLOR_* enum value. - static SkColor GetDefaultColor(int id); + // Lock on write to themed_image_cache_ in UI thread; lock on all cache + // access in File thread. This allows the File thread and UI thread to + // both read themed images at the same time, while preventing simultaneous + // File thread read and UI thread write. + static Lock themed_image_cache_lock_; // Save the images to be written to disk, mapping file path to id. typedef std::map<FilePath, int> ImagesDiskCache; + // Cached images. We cache all retrieved and generated bitmaps and keep + // track of the pointers. We own these and will delete them when we're done + // using them. + typedef std::map<int, SkBitmap*> ImageCache; + protected: + // Sets an individual color value. + void SetColor(const char* id, const SkColor& color); + + // Sets an individual tint value. + void SetTint(const char* id, const color_utils::HSL& tint); + // Get the specified tint - |id| is one of the TINT_* enum values. color_utils::HSL GetTint(int id) const; + // Generate any frame colors that weren't specified. + void GenerateFrameColors(); + + // Generate any frame images that weren't specified. The resulting images + // will be stored in our cache and written to disk. If images have already + // been generated and cached, load them from disk. + void GenerateFrameImages() const; + + // Generate any tab images that weren't specified. The resulting images + // will be stored in our cache. + void GenerateTabImages() const; + // Clears all the override fields and saves the dictionary. - virtual void ClearAllThemeData(); + void ClearAllThemeData(); // Load theme data from preferences. virtual void LoadThemePrefs(); @@ -165,33 +254,116 @@ class BrowserThemeProvider : public NonThreadSafe, // Let all the browser views know that themes have changed. virtual void NotifyThemeChanged(); + // Loads a bitmap from the theme, which may be tinted or + // otherwise modified, or an application default. + virtual SkBitmap* LoadThemeBitmap(int id) const; + + // Save the modified bitmap at image_cache_[id]. + virtual void SaveThemeBitmap(std::string resource_name, int id) const; + // Clears the platform-specific caches. Do not call directly; it's called // from ClearCaches(). virtual void FreePlatformCaches(); + // The implementation of GenerateTabBackgroundBitmap(). That function also + // must be locked and touches caches; this function only deals with image + // generation. + SkBitmap* GenerateTabBackgroundBitmapImpl(int id) const; + Profile* profile() { return profile_; } + // Subclasses may need us to not use the on-disk image cache. The GTK + // interface needs to generate some images itself. + void force_process_images() { process_images_ = true; } + private: friend class BrowserThemeProviderTest; - // Saves the filename of the cached theme pack. - void SavePackName(const FilePath& pack_path); + typedef std::map<const int, std::string> ImageMap; + typedef std::map<const std::string, SkColor> ColorMap; + typedef std::map<const std::string, color_utils::HSL> TintMap; + typedef std::map<const std::string, int> DisplayPropertyMap; + typedef std::map<const int, scoped_refptr<RefCountedMemory> > RawDataMap; + typedef std::map<const int, std::string> ResourceNameMap; + + // Returns the string key for the given tint |id| TINT_* enum value. + const std::string GetTintKey(int id) const; + + // Returns the default tint for the given tint |id| TINT_* enum value. + color_utils::HSL GetDefaultTint(int id) const; + + // Returns the string key for the given color |id| COLOR_* enum value. + const std::string GetColorKey(int id) const; + + // Returns the default color for the given color |id| COLOR_* enum value. + SkColor GetDefaultColor(int id) const; + + // Tint |bitmap| with the tint specified by |hsl_id| + SkBitmap TintBitmap(const SkBitmap& bitmap, int hsl_id) const; + + // The following load data from specified dictionaries (either from + // preferences or from an extension manifest) and update our theme + // data appropriately. + // Allow any ResourceBundle image to be overridden. |images| should + // contain keys defined in ThemeResourceMap, and values as paths to + // the images on-disk. + void SetImageData(DictionaryValue* images, FilePath images_path); + + // Set our theme colors. The keys of |colors| are any of the kColor* + // constants, and the values are a three-item list containing 8-bit + // RGB values. + void SetColorData(DictionaryValue* colors); + + // Set tint data for our images and colors. The keys of |tints| are + // any of the kTint* contstants, and the values are a three-item list + // containing real numbers in the range 0-1 (and -1 for 'null'). + void SetTintData(DictionaryValue* tints); + + // Set miscellaneous display properties. While these can be defined as + // strings, they are currently stored as integers. + void SetDisplayPropertyData(DictionaryValue* display_properties); + + // Create any images that aren't pregenerated (e.g. background tab images). + SkBitmap* GenerateTabBackgroundBitmap(int id) const; + + // Save our data - when saving images we need the original dictionary + // from the extension because it contains the text ids that we want to save. + void SaveImageData(DictionaryValue* images) const; + void SaveColorData() const; + void SaveTintData() const; + void SaveDisplayPropertyData() const; + + // Save the paths of data we have written to disk in prefs. + void SaveCachedImageData() const; // Save the id of the last theme installed. void SaveThemeID(const std::string& id); - // Implementation of SetTheme() (and the fallback from LoadThemePrefs() in - // case we don't have a theme pack). - void BuildFromExtension(Extension* extension); + // Frees generated images and clears the image cache. + void ClearCaches(); // Remove preference values for themes that are no longer in use. void RemoveUnusedThemes(); + // Encode image at image_cache_[id] as PNG and write to disk. + void WriteImagesToDisk() const; + + // Do we have a custom frame image or custom tints? + bool ShouldTintFrames() const; + #if defined(OS_LINUX) // Loads an image and flips it horizontally if |rtl_enabled| is true. GdkPixbuf* GetPixbufImpl(int id, bool rtl_enabled) const; #endif + mutable ImageCache image_cache_; + + // Keep images generated for theme cache in their own place, so we can lock + // them on WRITE from UI thread and READ from file thread. Read from UI + // thread will be allowed unlocked, because no other thread has write + // access to the cache. + mutable ImageCache themed_image_cache_; + #if defined(OS_LINUX) typedef std::map<int, GdkPixbuf*> GdkPixbufMap; mutable GdkPixbufMap gdk_pixbufs_; @@ -202,10 +374,25 @@ class BrowserThemeProvider : public NonThreadSafe, mutable NSColorMap nscolor_cache_; #endif + mutable ImagesDiskCache images_disk_cache_; + ResourceBundle& rb_; Profile* profile_; - scoped_refptr<BrowserThemePack> theme_pack_; + ImageMap images_; + ColorMap colors_; + TintMap tints_; + mutable RawDataMap raw_data_; + DisplayPropertyMap display_properties_; + + // Reverse of theme_resources_map, so we can cache images properly. + ResourceNameMap resource_names_; + + // If true, process all images; if false, just load from disk. + bool process_images_; + + // Where we will store our generated images. + FilePath image_dir_; DISALLOW_COPY_AND_ASSIGN(BrowserThemeProvider); }; diff --git a/chrome/browser/browser_theme_provider_mac.mm b/chrome/browser/browser_theme_provider_mac.mm index ec3d4a8..65c2182 100644 --- a/chrome/browser/browser_theme_provider_mac.mm +++ b/chrome/browser/browser_theme_provider_mac.mm @@ -8,7 +8,6 @@ #include "app/gfx/color_utils.h" #include "base/logging.h" -#include "chrome/browser/browser_theme_pack.h" #include "skia/ext/skia_utils_mac.h" namespace { @@ -76,8 +75,9 @@ NSColor* BrowserThemeProvider::GetNSColor(int id) const { if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - SkColor sk_color; - if (theme_pack_.get() && theme_pack_->GetColor(id, &sk_color)) { + ColorMap::const_iterator color_iter = colors_.find(GetColorKey(id)); + if (color_iter != colors_.end()) { + const SkColor& sk_color = color_iter->second; NSColor* color = [NSColor colorWithCalibratedRed:SkColorGetR(sk_color)/255.0 green:SkColorGetG(sk_color)/255.0 @@ -102,8 +102,9 @@ NSColor* BrowserThemeProvider::GetNSColorTint(int id) const { if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - color_utils::HSL tint; - if (theme_pack_.get() && theme_pack_->GetTint(id, &tint)) { + TintMap::const_iterator tint_iter = tints_.find(GetTintKey(id)); + if (tint_iter != tints_.end()) { + color_utils::HSL tint = tint_iter->second; CGFloat hue, saturation, brightness; HSLToHSB(tint, &hue, &saturation, &brightness); diff --git a/chrome/browser/browser_theme_provider_unittest.cc b/chrome/browser/browser_theme_provider_unittest.cc index 533c7af..6556a56 100644 --- a/chrome/browser/browser_theme_provider_unittest.cc +++ b/chrome/browser/browser_theme_provider_unittest.cc @@ -8,7 +8,87 @@ #include "base/values.h" #include "base/json/json_reader.h" -TEST(BrowserThemeProviderTest, AlignmentConversion) { +class BrowserThemeProviderTest : public ::testing::Test { + public: + // Transformation for link underline colors. + SkColor BuildThirdOpacity(SkColor color_link) { + return SkColorSetA(color_link, SkColorGetA(color_link) / 3); + } + + // Returns a mapping from each COLOR_* constant to the default value for this + // constant. Callers get this map, and then modify expected values and then + // run the resulting thing through VerifyColorMap(). + std::map<int, SkColor> GetDefaultColorMap() { + std::map<int, SkColor> colors; + colors[BrowserThemeProvider::COLOR_FRAME] = + BrowserThemeProvider::kDefaultColorFrame; + colors[BrowserThemeProvider::COLOR_FRAME_INACTIVE] = + BrowserThemeProvider::kDefaultColorFrameInactive; + colors[BrowserThemeProvider::COLOR_FRAME_INCOGNITO] = + BrowserThemeProvider::kDefaultColorFrameIncognito; + colors[BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE] = + BrowserThemeProvider::kDefaultColorFrameIncognitoInactive; + colors[BrowserThemeProvider::COLOR_TOOLBAR] = + BrowserThemeProvider::kDefaultColorToolbar; + colors[BrowserThemeProvider::COLOR_TAB_TEXT] = + BrowserThemeProvider::kDefaultColorTabText; + colors[BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT] = + BrowserThemeProvider::kDefaultColorBackgroundTabText; + colors[BrowserThemeProvider::COLOR_BOOKMARK_TEXT] = + BrowserThemeProvider::kDefaultColorBookmarkText; + colors[BrowserThemeProvider::COLOR_NTP_BACKGROUND] = + BrowserThemeProvider::kDefaultColorNTPBackground; + colors[BrowserThemeProvider::COLOR_NTP_TEXT] = + BrowserThemeProvider::kDefaultColorNTPText; + colors[BrowserThemeProvider::COLOR_NTP_LINK] = + BrowserThemeProvider::kDefaultColorNTPLink; + colors[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = + BuildThirdOpacity(BrowserThemeProvider::kDefaultColorNTPLink); + colors[BrowserThemeProvider::COLOR_NTP_HEADER] = + BrowserThemeProvider::kDefaultColorNTPHeader; + colors[BrowserThemeProvider::COLOR_NTP_SECTION] = + BrowserThemeProvider::kDefaultColorNTPSection; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_TEXT] = + BrowserThemeProvider::kDefaultColorNTPSectionText; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK] = + BrowserThemeProvider::kDefaultColorNTPSectionLink; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = + BuildThirdOpacity(BrowserThemeProvider::kDefaultColorNTPSectionLink); + colors[BrowserThemeProvider::COLOR_CONTROL_BACKGROUND] = + BrowserThemeProvider::kDefaultColorControlBackground; + colors[BrowserThemeProvider::COLOR_BUTTON_BACKGROUND] = + BrowserThemeProvider::kDefaultColorButtonBackground; + + return colors; + } + + void VerifyColorMap(const std::map<int, SkColor>& color_map) { + for (std::map<int, SkColor>::const_iterator it = color_map.begin(); + it != color_map.end(); ++it) { + EXPECT_EQ(it->second, provider_.GetColor(it->first)); + } + } + + void LoadColorJSON(const std::string& json) { + scoped_ptr<Value> value(base::JSONReader::Read(json, false)); + ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); + provider_.SetColorData(static_cast<DictionaryValue*>(value.get())); + } + + void LoadTintJSON(const std::string& json) { + scoped_ptr<Value> value(base::JSONReader::Read(json, false)); + ASSERT_TRUE(value->IsType(Value::TYPE_DICTIONARY)); + provider_.SetTintData(static_cast<DictionaryValue*>(value.get())); + } + + void GenerateFrameColors() { + provider_.GenerateFrameColors(); + } + + BrowserThemeProvider provider_; +}; + +TEST_F(BrowserThemeProviderTest, AlignmentConversion) { // Verify that we get out what we put in. std::string top_left = "top left"; int alignment = BrowserThemeProvider::StringToAlignment(top_left); @@ -33,7 +113,7 @@ TEST(BrowserThemeProviderTest, AlignmentConversion) { EXPECT_EQ("", BrowserThemeProvider::AlignmentToString(alignment)); } -TEST(BrowserThemeProviderTest, AlignmentConversionInput) { +TEST_F(BrowserThemeProviderTest, AlignmentConversionInput) { // Verify that we output in an expected format. int alignment = BrowserThemeProvider::StringToAlignment("right bottom"); EXPECT_EQ("bottom right", BrowserThemeProvider::AlignmentToString(alignment)); @@ -50,3 +130,98 @@ TEST(BrowserThemeProviderTest, AlignmentConversionInput) { alignment = BrowserThemeProvider::StringToAlignment("new zealandtop"); EXPECT_EQ("", BrowserThemeProvider::AlignmentToString(alignment)); } + +TEST_F(BrowserThemeProviderTest, ColorSanityCheck) { + // Make sure that BrowserThemeProvider returns all the default colors if it + // isn't provided any color overrides. + std::map<int, SkColor> colors = GetDefaultColorMap(); + VerifyColorMap(colors); +} + +TEST_F(BrowserThemeProviderTest, DeriveUnderlineLinkColor) { + // If we specify a link color, but don't specify the underline color, the + // theme provider should create one. + std::string color_json = "{ \"ntp_link\": [128, 128, 128, 1]," + " \"ntp_section_link\": [128, 128, 128, 1] }"; + LoadColorJSON(color_json); + + std::map<int, SkColor> colors = GetDefaultColorMap(); + SkColor link_color = SkColorSetARGB(255, 128, 128, 128); + colors[BrowserThemeProvider::COLOR_NTP_LINK] = link_color; + colors[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = + BuildThirdOpacity(link_color); + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK] = link_color; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = + BuildThirdOpacity(link_color); + + VerifyColorMap(colors); +} + +TEST_F(BrowserThemeProviderTest, ProvideUnderlineLinkColor) { + // If we specify the underline color, it shouldn't try to generate one.x + std::string color_json = "{ \"ntp_link\": [128, 128, 128]," + " \"ntp_link_underline\": [255, 255, 255]," + " \"ntp_section_link\": [128, 128, 128]," + " \"ntp_section_link_underline\": [255, 255, 255]" + "}"; + LoadColorJSON(color_json); + + std::map<int, SkColor> colors = GetDefaultColorMap(); + SkColor link_color = SkColorSetRGB(128, 128, 128); + SkColor underline_color = SkColorSetRGB(255, 255, 255); + colors[BrowserThemeProvider::COLOR_NTP_LINK] = link_color; + colors[BrowserThemeProvider::COLOR_NTP_LINK_UNDERLINE] = underline_color; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK] = link_color; + colors[BrowserThemeProvider::COLOR_NTP_SECTION_LINK_UNDERLINE] = + underline_color; + + VerifyColorMap(colors); +} + +TEST_F(BrowserThemeProviderTest, UseSectionColorAsNTPHeader) { + std::string color_json = "{ \"ntp_section\": [190, 190, 190] }"; + LoadColorJSON(color_json); + + std::map<int, SkColor> colors = GetDefaultColorMap(); + SkColor ntp_color = SkColorSetRGB(190, 190, 190); + colors[BrowserThemeProvider::COLOR_NTP_HEADER] = ntp_color; + colors[BrowserThemeProvider::COLOR_NTP_SECTION] = ntp_color; + VerifyColorMap(colors); +} + +TEST_F(BrowserThemeProviderTest, ProvideNtpHeaderColor) { + std::string color_json = "{ \"ntp_header\": [120, 120, 120], " + " \"ntp_section\": [190, 190, 190] }"; + LoadColorJSON(color_json); + + std::map<int, SkColor> colors = GetDefaultColorMap(); + SkColor ntp_header = SkColorSetRGB(120, 120, 120); + SkColor ntp_section = SkColorSetRGB(190, 190, 190); + colors[BrowserThemeProvider::COLOR_NTP_HEADER] = ntp_header; + colors[BrowserThemeProvider::COLOR_NTP_SECTION] = ntp_section; + VerifyColorMap(colors); +} + +TEST_F(BrowserThemeProviderTest, DefaultTintingDefaultColors) { + // Default tints for buttons and frames...are no tints! So make sure that + // when we try to generate frame colors, we end up with the same. + GenerateFrameColors(); + + std::map<int, SkColor> colors = GetDefaultColorMap(); + colors[BrowserThemeProvider::COLOR_FRAME] = + HSLShift(BrowserThemeProvider::kDefaultColorFrame, + BrowserThemeProvider::kDefaultTintFrame); + colors[BrowserThemeProvider::COLOR_FRAME_INACTIVE] = + HSLShift(BrowserThemeProvider::kDefaultColorFrame, + BrowserThemeProvider::kDefaultTintFrameInactive); + colors[BrowserThemeProvider::COLOR_FRAME_INCOGNITO] = + HSLShift(BrowserThemeProvider::kDefaultColorFrame, + BrowserThemeProvider::kDefaultTintFrameIncognito); + colors[BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE] = + HSLShift(BrowserThemeProvider::kDefaultColorFrame, + BrowserThemeProvider::kDefaultTintFrameIncognitoInactive); + VerifyColorMap(colors); +} + +// TODO(erg): Test more tinting combinations. For example, with non-default +// colors or when providing tints. diff --git a/chrome/browser/cocoa/download_shelf_controller.mm b/chrome/browser/cocoa/download_shelf_controller.mm index ae9395b..5d494fc 100644 --- a/chrome/browser/cocoa/download_shelf_controller.mm +++ b/chrome/browser/cocoa/download_shelf_controller.mm @@ -114,12 +114,10 @@ const NSTimeInterval kDownloadShelfCloseDuration = 0.12; BOOL useDefaultColor = YES; if (bridge_.get() && bridge_->browser() && bridge_->browser()->profile()) { ThemeProvider* provider = bridge_->browser()->profile()->GetThemeProvider(); - if (provider) { + if (provider) useDefaultColor = provider->GetColor( BrowserThemeProvider::COLOR_BOOKMARK_TEXT) == - BrowserThemeProvider::GetDefaultColor( - BrowserThemeProvider::COLOR_BOOKMARK_TEXT); - } + BrowserThemeProvider::kDefaultColorBookmarkText; } NSColor* color = useDefaultColor ? diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc index caea07d..879cbb2 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc @@ -27,9 +27,7 @@ ThemeInstalledInfoBarDelegate::ThemeInstalledInfoBarDelegate( void ThemeInstalledInfoBarDelegate::InfoBarClosed() { ExtensionsService* service = profile_->GetExtensionsService(); - // Only delete the theme if we've installed a new theme and not the same - // theme on top of the current one. - if (service && previous_theme_id_ != new_theme_id_) { + if (service) { std::string uninstall_id; if (was_canceled_) uninstall_id = new_theme_id_; diff --git a/chrome/browser/gtk/download_shelf_gtk.cc b/chrome/browser/gtk/download_shelf_gtk.cc index 6d94b9f..8601855 100644 --- a/chrome/browser/gtk/download_shelf_gtk.cc +++ b/chrome/browser/gtk/download_shelf_gtk.cc @@ -201,8 +201,7 @@ void DownloadShelfGtk::Observe(NotificationType type, // bad for some dark themes. bool use_default_color = theme_provider_->GetColor( BrowserThemeProvider::COLOR_BOOKMARK_TEXT) == - BrowserThemeProvider::GetDefaultColor( - BrowserThemeProvider::COLOR_BOOKMARK_TEXT); + BrowserThemeProvider::kDefaultColorBookmarkText; GdkColor bookmark_color = theme_provider_->GetGdkColor( BrowserThemeProvider::COLOR_BOOKMARK_TEXT); gtk_chrome_link_button_set_normal_color( diff --git a/chrome/browser/gtk/gtk_theme_provider.cc b/chrome/browser/gtk/gtk_theme_provider.cc index 9330f30..4f35110 100644 --- a/chrome/browser/gtk/gtk_theme_provider.cc +++ b/chrome/browser/gtk/gtk_theme_provider.cc @@ -8,8 +8,6 @@ #include "app/gfx/color_utils.h" #include "app/gfx/gtk_util.h" -#include "app/gfx/skbitmap_operations.h" -#include "base/stl_util-inl.h" #include "chrome/browser/metrics/user_metrics.h" #include "chrome/browser/profile.h" #include "chrome/browser/gtk/cairo_cached_surface.h" @@ -59,26 +57,6 @@ SkColor GdkToSkColor(GdkColor* color) { color->blue >> 8); } -// A list of images that we provide while in gtk mode. -const int kThemeImages[] = { - IDR_THEME_TOOLBAR, - IDR_THEME_TAB_BACKGROUND, - IDR_THEME_TAB_BACKGROUND_INCOGNITO, - IDR_THEME_FRAME, - IDR_THEME_FRAME_INACTIVE, - IDR_THEME_FRAME_INCOGNITO, - IDR_THEME_FRAME_INCOGNITO_INACTIVE, -}; - -bool IsOverridableImage(int id) { - for (size_t i = 0; i < arraysize(kThemeImages); ++i) { - if (kThemeImages[i] == id) - return true; - } - - return false; -} - } // namespace GtkWidget* GtkThemeProvider::icon_widget_ = NULL; @@ -106,9 +84,9 @@ GtkThemeProvider::~GtkThemeProvider() { gtk_widget_destroy(fake_window_); fake_label_.Destroy(); - // We have to call this because FreePlatformCached() in ~BrowserThemeProvider - // doesn't call the right virutal FreePlatformCaches. - FreePlatformCaches(); + // We have to call this because ClearCaches in ~BrowserThemeProvider doesn't + // call the right virutal FreePlatformCaches. + FreePerDisplaySurfaces(); // Disconnect from the destroy signal of any redisual widgets in // |chrome_buttons_|. @@ -125,39 +103,6 @@ void GtkThemeProvider::Init(Profile* profile) { BrowserThemeProvider::Init(profile); } -SkBitmap* GtkThemeProvider::GetBitmapNamed(int id) const { - if (use_gtk_ && IsOverridableImage(id)) { - // Try to get our cached version: - ImageCache::const_iterator it = gtk_images_.find(id); - if (it != gtk_images_.end()) - return it->second; - - // We haven't built this image yet: - SkBitmap* bitmap = GenerateGtkThemeBitmap(id); - gtk_images_[id] = bitmap; - return bitmap; - } - - return BrowserThemeProvider::GetBitmapNamed(id); -} - -SkColor GtkThemeProvider::GetColor(int id) const { - if (use_gtk_) { - ColorMap::const_iterator it = colors_.find(id); - if (it != colors_.end()) - return it->second; - } - - return BrowserThemeProvider::GetColor(id); -} - -bool GtkThemeProvider::HasCustomImage(int id) const { - if (use_gtk_) - return IsOverridableImage(id); - - return BrowserThemeProvider::HasCustomImage(id); -} - void GtkThemeProvider::InitThemesFor(NotificationObserver* observer) { observer->Observe(NotificationType::BROWSER_THEME_CHANGED, Source<ThemeProvider>(this), @@ -400,10 +345,40 @@ void GtkThemeProvider::NotifyThemeChanged() { } } +SkBitmap* GtkThemeProvider::LoadThemeBitmap(int id) const { + if (use_gtk_) { + if (id == IDR_THEME_TOOLBAR) { + GtkStyle* style = gtk_rc_get_style(fake_window_); + GdkColor* color = &style->bg[GTK_STATE_NORMAL]; + SkBitmap* bitmap = new SkBitmap; + bitmap->setConfig(SkBitmap::kARGB_8888_Config, + kToolbarImageWidth, kToolbarImageHeight); + bitmap->allocPixels(); + bitmap->eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8); + return bitmap; + } + if ((id == IDR_THEME_TAB_BACKGROUND) || + (id == IDR_THEME_TAB_BACKGROUND_INCOGNITO)) + return GenerateTabBackgroundBitmapImpl(id); + } + + return BrowserThemeProvider::LoadThemeBitmap(id); +} + +void GtkThemeProvider::SaveThemeBitmap(const std::string resource_name, + int id) const { + if (!use_gtk_) { + // Prevent us from writing out our mostly unused resources in gtk theme + // mode. Simply preventing us from writing this data out in gtk mode isn't + // the best design, but this would probably be a very invasive change on + // all three platforms otherwise. + BrowserThemeProvider::SaveThemeBitmap(resource_name, id); + } +} + void GtkThemeProvider::FreePlatformCaches() { BrowserThemeProvider::FreePlatformCaches(); FreePerDisplaySurfaces(); - STLDeleteValues(>k_images_); } // static @@ -500,35 +475,34 @@ void GtkThemeProvider::LoadGtkValues() { } } - SetThemeTintFromGtk(BrowserThemeProvider::TINT_BUTTONS, &button_color); - SetThemeTintFromGtk(BrowserThemeProvider::TINT_FRAME, &frame_color); - SetThemeTintFromGtk(BrowserThemeProvider::TINT_FRAME_INCOGNITO, &frame_color); - SetThemeTintFromGtk(BrowserThemeProvider::TINT_BACKGROUND_TAB, &frame_color); - - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_FRAME, &frame_color); - BuildTintedFrameColor(BrowserThemeProvider::COLOR_FRAME_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INACTIVE); - BuildTintedFrameColor(BrowserThemeProvider::COLOR_FRAME_INCOGNITO, - BrowserThemeProvider::TINT_FRAME_INCOGNITO); - BuildTintedFrameColor(BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE, - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE); - + SetThemeColorFromGtk(kColorFrame, &frame_color); // Skip COLOR_FRAME_INACTIVE and the incognito colors, as they will be - // autogenerated from tints. TODO(erg): Still true? - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_TOOLBAR, &toolbar_color); - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_TAB_TEXT, &label_color); - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_BOOKMARK_TEXT, &label_color); - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_CONTROL_BACKGROUND, + // autogenerated from tints. + SetThemeColorFromGtk(kColorToolbar, &toolbar_color); + SetThemeColorFromGtk(kColorTabText, &label_color); + SetThemeColorFromGtk(kColorBookmarkText, &label_color); + SetThemeColorFromGtk(kColorControlBackground, &window_style->bg[GTK_STATE_NORMAL]); - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND, + SetThemeColorFromGtk(kColorButtonBackground, &window_style->bg[GTK_STATE_NORMAL]); + SetThemeTintFromGtk(kTintButtons, &button_color, + kDefaultTintButtons); + SetThemeTintFromGtk(kTintFrame, &frame_color, + kDefaultTintFrame); + SetThemeTintFromGtk(kTintFrameIncognito, + &frame_color, + kDefaultTintFrameIncognito); + SetThemeTintFromGtk(kTintBackgroundTab, + &frame_color, + kDefaultTintBackgroundTab); + // The inactive frame color never occurs naturally in the theme, as it is a // tinted version of |frame_color|. We generate another color based on the // background tab color, with the lightness and saturation moved in the // opposite direction. (We don't touch the hue, since there should be subtle // hints of the color in the text.) - color_utils::HSL inactive_tab_text_hsl = tints_[TINT_BACKGROUND_TAB]; + color_utils::HSL inactive_tab_text_hsl = GetTint(TINT_BACKGROUND_TAB); if (inactive_tab_text_hsl.l < 0.5) inactive_tab_text_hsl.l = kDarkInactiveLuminance; else @@ -539,26 +513,32 @@ void GtkThemeProvider::LoadGtkValues() { else inactive_tab_text_hsl.s = kLightInactiveSaturation; - colors_[BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT] = - color_utils::HSLToSkColor(inactive_tab_text_hsl, 255); + SetColor(kColorBackgroundTabText, + color_utils::HSLToSkColor(inactive_tab_text_hsl, 255)); // The inactive color/tint is special: We *must* use the exact insensitive // color for all inactive windows, otherwise we end up neon pink half the // time. - SetThemeColorFromGtk(BrowserThemeProvider::COLOR_FRAME_INACTIVE, - &inactive_frame_color); - SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INACTIVE, - &inactive_frame_color); - SetTintToExactColor(BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE, - &inactive_frame_color); + SetThemeColorFromGtk(kColorFrameInactive, &inactive_frame_color); + SetThemeTintFromGtk(kTintFrameInactive, &inactive_frame_color, + kExactColor); + SetThemeTintFromGtk(kTintFrameIncognitoInactive, &inactive_frame_color, + kExactColor); + + force_process_images(); + GenerateFrameColors(); + AutoLock lock(themed_image_cache_lock_); + GenerateFrameImages(); } -void GtkThemeProvider::SetThemeColorFromGtk(int id, GdkColor* color) { - colors_[id] = GdkToSkColor(color); +void GtkThemeProvider::SetThemeColorFromGtk(const char* id, GdkColor* color) { + SetColor(id, GdkToSkColor(color)); } -void GtkThemeProvider::SetThemeTintFromGtk(int id, GdkColor* color) { - color_utils::HSL default_tint = GetDefaultTint(id); +void GtkThemeProvider::SetThemeTintFromGtk( + const char* id, + GdkColor* color, + const color_utils::HSL& default_tint) { color_utils::HSL hsl; color_utils::SkColorToHSL(GdkToSkColor(color), &hsl); @@ -567,19 +547,7 @@ void GtkThemeProvider::SetThemeTintFromGtk(int id, GdkColor* color) { if (default_tint.l != -1) hsl.l = default_tint.l; - - tints_[id] = hsl; -} - -void GtkThemeProvider::BuildTintedFrameColor(int color_id, int tint_id) { - colors_[color_id] = HSLShift(colors_[BrowserThemeProvider::COLOR_FRAME], - tints_[tint_id]); -} - -void GtkThemeProvider::SetTintToExactColor(int id, GdkColor* color) { - color_utils::HSL hsl; - color_utils::SkColorToHSL(GdkToSkColor(color), &hsl); - tints_[id] = hsl; + SetTint(id, hsl); } void GtkThemeProvider::FreePerDisplaySurfaces() { @@ -593,55 +561,6 @@ void GtkThemeProvider::FreePerDisplaySurfaces() { per_display_surfaces_.clear(); } -SkBitmap* GtkThemeProvider::GenerateGtkThemeBitmap(int id) const { - switch (id) { - case IDR_THEME_TOOLBAR: { - GtkStyle* style = gtk_rc_get_style(fake_window_); - GdkColor* color = &style->bg[GTK_STATE_NORMAL]; - SkBitmap* bitmap = new SkBitmap; - bitmap->setConfig(SkBitmap::kARGB_8888_Config, - kToolbarImageWidth, kToolbarImageHeight); - bitmap->allocPixels(); - bitmap->eraseRGB(color->red >> 8, color->green >> 8, color->blue >> 8); - return bitmap; - } - case IDR_THEME_TAB_BACKGROUND: - return GenerateTabImage(IDR_THEME_FRAME); - case IDR_THEME_TAB_BACKGROUND_INCOGNITO: - return GenerateTabImage(IDR_THEME_FRAME_INCOGNITO); - case IDR_THEME_FRAME: - return GenerateFrameImage(BrowserThemeProvider::TINT_FRAME); - case IDR_THEME_FRAME_INACTIVE: - return GenerateFrameImage(BrowserThemeProvider::TINT_FRAME_INACTIVE); - case IDR_THEME_FRAME_INCOGNITO: - return GenerateFrameImage(BrowserThemeProvider::TINT_FRAME_INCOGNITO); - case IDR_THEME_FRAME_INCOGNITO_INACTIVE: { - return GenerateFrameImage( - BrowserThemeProvider::TINT_FRAME_INCOGNITO_INACTIVE); - } - } - - NOTREACHED() << "Invalid bitmap request " << id; - return NULL; -} - -SkBitmap* GtkThemeProvider::GenerateFrameImage(int tint_id) const { - ResourceBundle& rb = ResourceBundle::GetSharedInstance(); - scoped_ptr<SkBitmap> frame(new SkBitmap(*rb.GetBitmapNamed(IDR_THEME_FRAME))); - TintMap::const_iterator it = tints_.find(tint_id); - DCHECK(it != tints_.end()); - return new SkBitmap(SkBitmapOperations::CreateHSLShiftedBitmap(*frame, - it->second)); -} - -SkBitmap* GtkThemeProvider::GenerateTabImage(int base_id) const { - SkBitmap* base_image = GetBitmapNamed(base_id); - SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap( - *base_image, GetTint(BrowserThemeProvider::TINT_BACKGROUND_TAB)); - return new SkBitmap(SkBitmapOperations::CreateTiledBitmap( - bg_tint, 0, 0, bg_tint.width(), bg_tint.height())); -} - void GtkThemeProvider::OnDestroyChromeButton(GtkWidget* button, GtkThemeProvider* provider) { std::vector<GtkWidget*>::iterator it = diff --git a/chrome/browser/gtk/gtk_theme_provider.h b/chrome/browser/gtk/gtk_theme_provider.h index 44fed99..60d72e7 100644 --- a/chrome/browser/gtk/gtk_theme_provider.h +++ b/chrome/browser/gtk/gtk_theme_provider.h @@ -40,9 +40,6 @@ class GtkThemeProvider : public BrowserThemeProvider, // Sets that we aren't using the system theme, then calls // BrowserThemeProvider's implementation. virtual void Init(Profile* profile); - virtual SkBitmap* GetBitmapNamed(int id) const; - virtual SkColor GetColor(int id) const; - virtual bool HasCustomImage(int id) const; virtual void SetTheme(Extension* extension); virtual void UseDefaultTheme(); virtual void SetNativeTheme(); @@ -89,17 +86,24 @@ class GtkThemeProvider : public BrowserThemeProvider, static GdkPixbuf* GetFolderIcon(bool native); static GdkPixbuf* GetDefaultFavicon(bool native); - private: - typedef std::map<int, SkColor> ColorMap; - typedef std::map<int, color_utils::HSL> TintMap; - typedef std::map<int, SkBitmap*> ImageCache; + protected: + // Possibly creates a theme specific version of theme_toolbar_default. + // (minimally acceptable version right now, which is just a fill of the bg + // color; this should instead invoke gtk_draw_box(...) for complex theme + // engines.) + virtual SkBitmap* LoadThemeBitmap(int id) const; + private: // Load theme data from preferences, possibly picking colors from GTK. virtual void LoadThemePrefs(); // Let all the browser views know that themes have changed. virtual void NotifyThemeChanged(); + // If use_gtk_ is true, completely ignores this call. Otherwise passes it to + // the superclass. + virtual void SaveThemeBitmap(const std::string resource_name, int id) const; + // Additionally frees the CairoCachedSurfaces. virtual void FreePlatformCaches(); @@ -111,10 +115,9 @@ class GtkThemeProvider : public BrowserThemeProvider, void LoadGtkValues(); // Sets the underlying theme colors/tints from a GTK color. - void SetThemeColorFromGtk(int id, GdkColor* color); - void SetThemeTintFromGtk(int id, GdkColor* color); - void BuildTintedFrameColor(int color_id, int tint_id); - void SetTintToExactColor(int id, GdkColor* color); + void SetThemeColorFromGtk(const char* id, GdkColor* color); + void SetThemeTintFromGtk(const char* id, GdkColor* color, + const color_utils::HSL& default_tint); // Split out from FreePlatformCaches so it can be called in our destructor; // FreePlatformCaches() is called from the BrowserThemeProvider's destructor, @@ -122,16 +125,6 @@ class GtkThemeProvider : public BrowserThemeProvider, // points to GtkThemeProvider's version. void FreePerDisplaySurfaces(); - // Lazily generates each bitmap used in the gtk theme. - SkBitmap* GenerateGtkThemeBitmap(int id) const; - - // Tints IDR_THEME_FRAME based based on |tint_id|. Used during lazy - // generation of the gtk theme bitmaps. - SkBitmap* GenerateFrameImage(int tint_id) const; - - // Takes the base frame image |base_id| and tints it with |tint_id|. - SkBitmap* GenerateTabImage(int base_id) const; - // A notification from the GtkChromeButton GObject destructor that we should // remove it from our internal list. static void OnDestroyChromeButton(GtkWidget* button, @@ -149,15 +142,6 @@ class GtkThemeProvider : public BrowserThemeProvider, // them of theme changes. std::vector<GtkWidget*> chrome_buttons_; - // Tints and colors calculated by LoadGtkValues() that are given to the - // caller while |use_gtk_| is true. - ColorMap colors_; - TintMap tints_; - - // Image cache of lazily created images, created when requested by - // GetBitmapNamed(). - mutable ImageCache gtk_images_; - // Cairo surfaces for each GdkDisplay. typedef std::map<int, CairoCachedSurface*> CairoCachedSurfaceMap; typedef std::map<GdkDisplay*, CairoCachedSurfaceMap> PerDisplaySurfaceMap; diff --git a/chrome/browser/gtk/gtk_theme_provider_unittest.cc b/chrome/browser/gtk/gtk_theme_provider_unittest.cc index 93012ba..c3b85cb 100644 --- a/chrome/browser/gtk/gtk_theme_provider_unittest.cc +++ b/chrome/browser/gtk/gtk_theme_provider_unittest.cc @@ -35,6 +35,11 @@ class GtkThemeProviderTest : public testing::Test { provider_ = GtkThemeProvider::GetFrom(&profile_); } + void UseThemeProvider(GtkThemeProvider* provider) { + profile_.UseThemeProvider(provider); + provider_ = GtkThemeProvider::GetFrom(&profile_); + } + protected: TestingProfile profile_; @@ -46,11 +51,42 @@ TEST_F(GtkThemeProviderTest, DefaultValues) { BuildProvider(); // Test that we get the default theme colors back when in normal mode. - for (int i = BrowserThemeProvider::COLOR_FRAME; - i <= BrowserThemeProvider::COLOR_BUTTON_BACKGROUND; ++i) { - EXPECT_EQ(provider_->GetColor(i), BrowserThemeProvider::GetDefaultColor(i)) - << "Wrong default color for " << i; - } + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_FRAME), + BrowserThemeProvider::kDefaultColorFrame); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_FRAME_INACTIVE), + BrowserThemeProvider::kDefaultColorFrameInactive); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_FRAME_INCOGNITO), + BrowserThemeProvider::kDefaultColorFrameIncognito); + EXPECT_EQ(provider_->GetColor( + BrowserThemeProvider::COLOR_FRAME_INCOGNITO_INACTIVE), + BrowserThemeProvider::kDefaultColorFrameIncognitoInactive); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_TOOLBAR), + BrowserThemeProvider::kDefaultColorToolbar); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_TAB_TEXT), + BrowserThemeProvider::kDefaultColorTabText); + EXPECT_EQ(provider_->GetColor( + BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT), + BrowserThemeProvider::kDefaultColorBackgroundTabText); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT), + BrowserThemeProvider::kDefaultColorBookmarkText); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_BACKGROUND), + BrowserThemeProvider::kDefaultColorNTPBackground); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_TEXT), + BrowserThemeProvider::kDefaultColorNTPText); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_LINK), + BrowserThemeProvider::kDefaultColorNTPLink); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_HEADER), + BrowserThemeProvider::kDefaultColorNTPHeader); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_SECTION), + BrowserThemeProvider::kDefaultColorNTPSection); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_SECTION_TEXT), + BrowserThemeProvider::kDefaultColorNTPSectionText); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_NTP_SECTION_LINK), + BrowserThemeProvider::kDefaultColorNTPSectionLink); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_CONTROL_BACKGROUND), + BrowserThemeProvider::kDefaultColorControlBackground); + EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND), + BrowserThemeProvider::kDefaultColorButtonBackground); } TEST_F(GtkThemeProviderTest, UsingGtkValues) { @@ -68,3 +104,32 @@ TEST_F(GtkThemeProviderTest, UsingGtkValues) { EXPECT_EQ(provider_->GetColor(BrowserThemeProvider::COLOR_TAB_TEXT), GdkToSkColor(&label_color)); } + +// Helper class to GtkThemeProviderTest.UsingGtkFrame. +class ImageVerifierGtkThemeProvider : public GtkThemeProvider { + public: + ImageVerifierGtkThemeProvider() : theme_toolbar_(NULL) { } + + virtual SkBitmap* LoadThemeBitmap(int id) const { + if (id != IDR_THEME_TOOLBAR) + return GtkThemeProvider::LoadThemeBitmap(id); + theme_toolbar_ = GtkThemeProvider::LoadThemeBitmap(id); + return theme_toolbar_; + } + + mutable SkBitmap* theme_toolbar_; +}; + +TEST_F(GtkThemeProviderTest, InjectsToolbar) { + SetUseGtkTheme(true); + ImageVerifierGtkThemeProvider* verifier_provider = + new ImageVerifierGtkThemeProvider; + UseThemeProvider(verifier_provider); + + // Make sure the image we get from the public BrowserThemeProvider interface + // is the one we injected through GtkThemeProvider. + SkBitmap* image = provider_->GetBitmapNamed(IDR_THEME_TOOLBAR); + EXPECT_TRUE(verifier_provider->theme_toolbar_); + EXPECT_TRUE(image); + EXPECT_EQ(verifier_provider->theme_toolbar_, image); +} diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index 3f5deee..22444f4 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -150,7 +150,6 @@ void Profile::RegisterUserPrefs(PrefService* prefs) { #if defined(OS_LINUX) prefs->RegisterBooleanPref(prefs::kUsesSystemTheme, false); #endif - prefs->RegisterFilePathPref(prefs::kCurrentThemePackFilename, FilePath()); prefs->RegisterStringPref(prefs::kCurrentThemeID, UTF8ToWide(BrowserThemeProvider::kDefaultThemeID)); prefs->RegisterDictionaryPref(prefs::kCurrentThemeImages); diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 5ac8be1..21ab8e9 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -188,8 +188,6 @@ 'browser/browser_process_impl.h', 'browser/browser_shutdown.cc', 'browser/browser_shutdown.h', - 'browser/browser_theme_pack.cc', - 'browser/browser_theme_pack.h', 'browser/browser_theme_provider_gtk.cc', 'browser/browser_theme_provider_mac.mm', 'browser/browser_theme_provider.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 801b6be..af198977 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -526,7 +526,6 @@ 'browser/bookmarks/bookmark_table_model_unittest.cc', 'browser/bookmarks/bookmark_utils_unittest.cc', 'browser/browser_commands_unittest.cc', - 'browser/browser_theme_pack_unittest.cc', 'browser/browser_theme_provider_unittest.cc', 'browser/browser_unittest.cc', 'browser/chrome_browser_application_mac_unittest.mm', diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 1b85a19..1a936b5 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -84,7 +84,7 @@ const FilePath::CharType kOffTheRecordMediaCacheDirname[] = FPL("Incognito Media Cache"); const FilePath::CharType kAppCacheDirname[] = FPL("Application Cache"); const wchar_t kChromePluginDataDirname[] = L"Plugin Data"; -const FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak"); +const FilePath::CharType kThemeImagesDirname[] = FPL("Cached Theme Images"); const FilePath::CharType kCookieFilename[] = FPL("Cookies"); const FilePath::CharType kExtensionsCookieFilename[] = FPL("Extension Cookies"); const FilePath::CharType kHistoryFilename[] = FPL("History"); diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index ea79fd8..4473f32 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -40,7 +40,7 @@ extern const FilePath::CharType kMediaCacheDirname[]; extern const FilePath::CharType kOffTheRecordMediaCacheDirname[]; extern const FilePath::CharType kAppCacheDirname[]; extern const wchar_t kChromePluginDataDirname[]; -extern const FilePath::CharType kThemePackFilename[]; +extern const FilePath::CharType kThemeImagesDirname[]; extern const FilePath::CharType kCookieFilename[]; extern const FilePath::CharType kExtensionsCookieFilename[]; extern const FilePath::CharType kHistoryFilename[]; diff --git a/chrome/common/pref_names.cc b/chrome/common/pref_names.cc index 6c1edf9..acd77db 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -250,7 +250,6 @@ const wchar_t kPrintingPageFooterRight[] = L"printing.page.footer.right"; // GTK specific preference on whether we should match the system GTK theme. const wchar_t kUsesSystemTheme[] = L"extensions.theme.use_system"; #endif -const wchar_t kCurrentThemePackFilename[] = L"extensions.theme.pack"; const wchar_t kCurrentThemeID[] = L"extensions.theme.id"; const wchar_t kCurrentThemeImages[] = L"extensions.theme.images"; const wchar_t kCurrentThemeColors[] = L"extensions.theme.colors"; diff --git a/chrome/common/pref_names.h b/chrome/common/pref_names.h index 8229a6f..d3095b1 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -101,7 +101,6 @@ extern const wchar_t kPrintingPageFooterRight[]; #if defined(OS_LINUX) extern const wchar_t kUsesSystemTheme[]; #endif -extern const wchar_t kCurrentThemePackFilename[]; extern const wchar_t kCurrentThemeID[]; extern const wchar_t kCurrentThemeImages[]; extern const wchar_t kCurrentThemeColors[]; diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactive b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactive Binary files differnew file mode 100644 index 0000000..c1099db --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactive diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito Binary files differnew file mode 100644 index 0000000..5d73cfd --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactive b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactive Binary files differnew file mode 100644 index 0000000..78b69a4 --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactive diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_original b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_original Binary files differnew file mode 100644 index 0000000..fbd9a95 --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_original diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognito b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognito Binary files differnew file mode 100644 index 0000000..be17ca5 --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognito diff --git a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_original b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_original Binary files differnew file mode 100644 index 0000000..90cc6b4 --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_original diff --git a/chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak b/chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak Binary files differdeleted file mode 100644 index 0a76cb9..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak +++ /dev/null diff --git a/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate b/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate index 7c3eb03..f869914 100644 --- a/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate +++ b/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate @@ -50,8 +50,39 @@ } }, "theme": { + "colors": { + "bookmark_text": [ 0, 0, 0 ], + "frame": [ 57, 137, 194 ], + "ntp_background": [ 57, 137, 194 ], + "ntp_link": [ 0, 0, 0 ], + "ntp_section": [ 131, 138, 146, 0.8 ], + "ntp_section_link": [ 255, 255, 255 ], + "ntp_section_text": [ 255, 255, 255 ], + "ntp_text": [ 0, 0, 0 ], + "tab_background_text": [ 0, 0, 0 ], + "tab_text": [ 0, 0, 0 ], + "toolbar": [ 204, 208, 212 ] + }, "id": "mblmlcbknbnfebdfjnolmcapmdofhmme", - "pack": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak" + "images": { + "theme_button_background": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/i/agxjaHJvbWV0aGVtZXNyDAsSBEZpbGUYwsgCDA", + "theme_frame": "$1/Default/Cached Theme Images/theme_frame_original", + "theme_frame_inactive": "$1/Default/Cached Theme Images/theme_frame_inactive", + "theme_frame_incognito": "$1/Default/Cached Theme Images/theme_frame_incognito", + "theme_frame_incognito_inactive": "$1/Default/Cached Theme Images/theme_frame_incognito_inactive", + "theme_ntp_background": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/i/agxjaHJvbWV0aGVtZXNyDAsSBEZpbGUYj9gCDA", + "theme_tab_background": "$1/Default/Cached Theme Images/theme_tab_background_original", + "theme_tab_background_incognito": "$1/Default/Cached Theme Images/theme_tab_background_incognito", + "theme_toolbar": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/i/agxjaHJvbWV0aGVtZXNyDAsSBEZpbGUYnMgCDA", + "theme_window_control_background": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/i/agxjaHJvbWV0aGVtZXNyDAsSBEZpbGUYjNgCDA" + }, + "properties": { + "ntp_background_alignment": "top", + "ntp_background_repeat": "no-repeat" + }, + "tints": { + "buttons": [ 0.6, 0.553, 0.5 ] + } } }, "profile": { |