diff options
33 files changed, 1911 insertions, 1567 deletions
diff --git a/base/base.gyp b/base/base.gyp index e0a0fe9..28cf048 100644 --- a/base/base.gyp +++ b/base/base.gyp @@ -185,7 +185,6 @@ '../third_party/icu/icu.gyp:icudata', ], 'sources!': [ - 'data_pack_unittest.cc', 'file_descriptor_shuffle_unittest.cc', ], }, { # OS != "win" diff --git a/base/base.gypi b/base/base.gypi index c19b343..77a919e 100644 --- a/base/base.gypi +++ b/base/base.gypi @@ -331,7 +331,6 @@ '../chrome/third_party/wtl/include', ], 'sources!': [ - 'data_pack.cc', 'event_recorder_stubs.cc', 'file_descriptor_shuffle.cc', 'message_pump_libevent.cc', diff --git a/base/data_pack.cc b/base/data_pack.cc index 27054d3..1078d0a 100644 --- a/base/data_pack.cc +++ b/base/data_pack.cc @@ -14,17 +14,22 @@ // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings namespace { -static const uint32_t kFileFormatVersion = 1; + +// A word is four bytes. +static const size_t kWord = 4; + +static const uint32 kFileFormatVersion = 1; // Length of file header: version and entry count. -static const size_t kHeaderLength = 2 * sizeof(uint32_t); +static const size_t kHeaderLength = 2 * sizeof(uint32); +#pragma pack(push,1) struct DataPackEntry { - uint32_t resource_id; - uint32_t file_offset; - uint32_t length; + uint32 resource_id; + uint32 file_offset; + uint32 length; static int CompareById(const void* void_key, const void* void_entry) { - uint32_t key = *reinterpret_cast<const uint32_t*>(void_key); + uint32 key = *reinterpret_cast<const uint32*>(void_key); const DataPackEntry* entry = reinterpret_cast<const DataPackEntry*>(void_entry); if (key < entry->resource_id) { @@ -35,7 +40,10 @@ struct DataPackEntry { return 0; } } -} __attribute((packed)); +}; +#pragma pack(pop) + +COMPILE_ASSERT(sizeof(DataPackEntry) == 12, size_of_header_must_be_twelve); } // anonymous namespace @@ -50,12 +58,13 @@ DataPack::~DataPack() { bool DataPack::Load(const FilePath& path) { mmap_.reset(new file_util::MemoryMappedFile); if (!mmap_->Initialize(path)) { - PCHECK(false) << "Failed to mmap " << path.value(); + DLOG(ERROR) << "Failed to mmap datapack"; + return false; } // Parse the header of the file. - // First uint32_t: version; second: resource count. - const uint32* ptr = reinterpret_cast<const uint32_t*>(mmap_->data()); + // First uint32: version; second: resource count. + const uint32* ptr = reinterpret_cast<const uint32*>(mmap_->data()); uint32 version = ptr[0]; if (version != kFileFormatVersion) { LOG(ERROR) << "Bad data pack version: got " << version << ", expected " @@ -89,7 +98,7 @@ bool DataPack::Load(const FilePath& path) { return true; } -bool DataPack::GetStringPiece(uint32_t resource_id, StringPiece* data) { +bool DataPack::GetStringPiece(uint32 resource_id, StringPiece* data) { // It won't be hard to make this endian-agnostic, but it's not worth // bothering to do right now. #if defined(__BYTE_ORDER) @@ -105,7 +114,6 @@ bool DataPack::GetStringPiece(uint32_t resource_id, StringPiece* data) { bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_, sizeof(DataPackEntry), DataPackEntry::CompareById)); if (!target) { - LOG(ERROR) << "No resource found with id: " << resource_id; return false; } @@ -113,7 +121,7 @@ bool DataPack::GetStringPiece(uint32_t resource_id, StringPiece* data) { return true; } -RefCountedStaticMemory* DataPack::GetStaticMemory(uint32_t resource_id) { +RefCountedStaticMemory* DataPack::GetStaticMemory(uint32 resource_id) { base::StringPiece piece; if (!GetStringPiece(resource_id, &piece)) return NULL; @@ -122,4 +130,67 @@ RefCountedStaticMemory* DataPack::GetStaticMemory(uint32_t resource_id) { reinterpret_cast<const unsigned char*>(piece.data()), piece.length()); } +// static +bool DataPack::WritePack(const FilePath& path, + const std::map<uint32, StringPiece>& resources) { + FILE* file = file_util::OpenFile(path, "wb"); + if (!file) + return false; + + if (fwrite(&kFileFormatVersion, 1, kWord, file) != kWord) { + LOG(ERROR) << "Failed to write file version"; + file_util::CloseFile(file); + return false; + } + + // Note: the python version of this function explicitly sorted keys, but + // std::map is a sorted associative container, we shouldn't have to do that. + uint32 entry_count = resources.size(); + if (fwrite(&entry_count, 1, kWord, file) != kWord) { + LOG(ERROR) << "Failed to write entry count"; + file_util::CloseFile(file); + return false; + } + + // Each entry is 3 uint32s. + uint32 index_length = entry_count * 3 * kWord; + uint32 data_offset = kHeaderLength + index_length; + for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); + it != resources.end(); ++it) { + if (fwrite(&it->first, 1, kWord, file) != kWord) { + LOG(ERROR) << "Failed to write id for " << it->first; + file_util::CloseFile(file); + return false; + } + + if (fwrite(&data_offset, 1, kWord, file) != kWord) { + LOG(ERROR) << "Failed to write offset for " << it->first; + file_util::CloseFile(file); + return false; + } + + uint32 len = it->second.length(); + if (fwrite(&len, 1, kWord, file) != kWord) { + LOG(ERROR) << "Failed to write length for " << it->first; + file_util::CloseFile(file); + return false; + } + + data_offset += len; + } + + for (std::map<uint32, StringPiece>::const_iterator it = resources.begin(); + it != resources.end(); ++it) { + if (fwrite(it->second.data(), it->second.length(), 1, file) != 1) { + LOG(ERROR) << "Failed to write data for " << it->first; + file_util::CloseFile(file); + return false; + } + } + + file_util::CloseFile(file); + + return true; +} + } // namespace base diff --git a/base/data_pack.h b/base/data_pack.h index 7c9abde..8938491 100644 --- a/base/data_pack.h +++ b/base/data_pack.h @@ -9,6 +9,8 @@ #ifndef BASE_DATA_PACK_H_ #define BASE_DATA_PACK_H_ +#include <map> + #include "base/basictypes.h" #include "base/ref_counted_memory.h" #include "base/scoped_ptr.h" @@ -33,12 +35,16 @@ class DataPack { // Get resource by id |resource_id|, filling in |data|. // The data is owned by the DataPack object and should not be modified. // Returns false if the resource id isn't found. - bool GetStringPiece(uint32_t resource_id, StringPiece* data); + bool GetStringPiece(uint32 resource_id, StringPiece* data); // Like GetStringPiece(), but returns a reference to memory. This interface // is used for image data, while the StringPiece interface is usually used // for localization strings. - RefCountedStaticMemory* GetStaticMemory(uint32_t resource_id); + RefCountedStaticMemory* GetStaticMemory(uint32 resource_id); + + // Writes a pack file containing |resources| to |path|. + static bool WritePack(const FilePath& path, + const std::map<uint32, StringPiece>& resources); private: // The memory-mapped data. diff --git a/base/data_pack_unittest.cc b/base/data_pack_unittest.cc index a62acf0..d089b28 100644 --- a/base/data_pack_unittest.cc +++ b/base/data_pack_unittest.cc @@ -5,24 +5,20 @@ #include "base/data_pack.h" #include "base/file_path.h" +#include "base/file_util.h" #include "base/path_service.h" +#include "base/scoped_temp_dir.h" #include "base/string_piece.h" #include "testing/gtest/include/gtest/gtest.h" -class DataPackTest : public testing::Test { - public: - DataPackTest() { - PathService::Get(base::DIR_SOURCE_ROOT, &data_path_); - data_path_ = data_path_.Append( +TEST(DataPackTest, Load) { + FilePath data_path; + PathService::Get(base::DIR_SOURCE_ROOT, &data_path); + data_path = data_path.Append( FILE_PATH_LITERAL("base/data/data_pack_unittest/sample.pak")); - } - FilePath data_path_; -}; - -TEST_F(DataPackTest, Load) { base::DataPack pack; - ASSERT_TRUE(pack.Load(data_path_)); + ASSERT_TRUE(pack.Load(data_path)); base::StringPiece data; ASSERT_TRUE(pack.GetStringPiece(4, &data)); @@ -39,3 +35,39 @@ TEST_F(DataPackTest, Load) { // Try looking up an invalid key. ASSERT_FALSE(pack.GetStringPiece(140, &data)); } + +TEST(DataPackTest, Write) { + ScopedTempDir dir; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + FilePath file = dir.path().Append(FILE_PATH_LITERAL("data.pak")); + + std::string one("one"); + std::string two("two"); + std::string three("three"); + std::string four("four"); + std::string fifteen("fifteen"); + + std::map<uint32, base::StringPiece> resources; + resources.insert(std::make_pair(1, base::StringPiece(one))); + resources.insert(std::make_pair(2, base::StringPiece(two))); + resources.insert(std::make_pair(15, base::StringPiece(fifteen))); + resources.insert(std::make_pair(3, base::StringPiece(three))); + resources.insert(std::make_pair(4, base::StringPiece(four))); + ASSERT_TRUE(base::DataPack::WritePack(file, resources)); + + // Now try to read the data back in. + base::DataPack pack; + ASSERT_TRUE(pack.Load(file)); + + base::StringPiece data; + ASSERT_TRUE(pack.GetStringPiece(1, &data)); + EXPECT_EQ(one, data); + ASSERT_TRUE(pack.GetStringPiece(2, &data)); + EXPECT_EQ(two, data); + ASSERT_TRUE(pack.GetStringPiece(3, &data)); + EXPECT_EQ(three, data); + ASSERT_TRUE(pack.GetStringPiece(4, &data)); + EXPECT_EQ(four, data); + ASSERT_TRUE(pack.GetStringPiece(15, &data)); + EXPECT_EQ(fifteen, data); +} diff --git a/chrome/browser/browser_theme_pack.cc b/chrome/browser/browser_theme_pack.cc new file mode 100644 index 0000000..f30bd6b --- /dev/null +++ b/chrome/browser/browser_theme_pack.cc @@ -0,0 +1,864 @@ +// 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 new file mode 100644 index 0000000..4287617 --- /dev/null +++ b/chrome/browser/browser_theme_pack.h @@ -0,0 +1,191 @@ +// 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 new file mode 100644 index 0000000..1c9081c --- /dev/null +++ b/chrome/browser/browser_theme_pack_unittest.cc @@ -0,0 +1,335 @@ +// 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 aceedf1..daf7b68 100644 --- a/chrome/browser/browser_theme_provider.cc +++ b/chrome/browser/browser_theme_provider.cc @@ -13,6 +13,7 @@ #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" @@ -36,50 +37,6 @@ #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"; @@ -92,68 +49,52 @@ 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 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 = +const SkColor kDefaultColorFrame = SkColorSetRGB(77, 139, 217); +const SkColor kDefaultColorFrameInactive = SkColorSetRGB(152, 188, 233); +const SkColor kDefaultColorFrameIncognito = SkColorSetRGB(83, 106, 139); +const SkColor kDefaultColorFrameIncognitoInactive = SkColorSetRGB(126, 139, 156); -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); +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); #if defined(OS_WIN) -const SkColor BrowserThemeProvider::kDefaultColorNTPBackground = +const SkColor kDefaultColorNTPBackground = color_utils::GetSysSkColor(COLOR_WINDOW); -const SkColor BrowserThemeProvider::kDefaultColorNTPText = +const SkColor kDefaultColorNTPText = color_utils::GetSysSkColor(COLOR_WINDOWTEXT); -const SkColor BrowserThemeProvider::kDefaultColorNTPLink = +const SkColor kDefaultColorNTPLink = color_utils::GetSysSkColor(COLOR_HOTLIGHT); #else // TODO(beng): source from theme provider. -const SkColor BrowserThemeProvider::kDefaultColorNTPBackground = SK_ColorWHITE; -const SkColor BrowserThemeProvider::kDefaultColorNTPText = SK_ColorBLACK; -const SkColor BrowserThemeProvider::kDefaultColorNTPLink = - SkColorSetRGB(6, 55, 116); +const SkColor kDefaultColorNTPBackground = SK_ColorWHITE; +const SkColor kDefaultColorNTPText = SK_ColorBLACK; +const SkColor kDefaultColorNTPLink = SkColorSetRGB(6, 55, 116); #endif -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); +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); // Default tints. -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 { +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 }; // Default display properties. const int kDefaultDisplayPropertyNTPAlignment = @@ -166,35 +107,6 @@ 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, @@ -222,49 +134,21 @@ bool HasThemeableImage(int themeable_image_id) { return themeable_images.count(themeable_image_id) > 0; } - -class WriteImagesToDiskTask : public Task { +// Writes the theme pack to disk on a separate thread. +class WritePackToDiskTask : public Task { public: - 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) { - } + WritePackToDiskTask(BrowserThemePack* pack, const FilePath& path) + : theme_pack_(pack), pack_path_(path) {} virtual void Run() { - 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; - } + if (!theme_pack_->WriteToDisk(pack_path_)) { + NOTREACHED() << "Could not write theme pack to disk"; } } private: - // References to data held in the BrowserThemeProvider. - const BrowserThemeProvider::ImagesDiskCache& images_disk_cache_; - const BrowserThemeProvider::ImageCache& themed_image_cache_; + scoped_refptr<BrowserThemePack> theme_pack_; + FilePath pack_path_; }; } // namespace @@ -275,32 +159,13 @@ bool BrowserThemeProvider::IsThemeableImage(int resource_id) { BrowserThemeProvider::BrowserThemeProvider() : rb_(ResourceBundle::GetSharedInstance()), - profile_(NULL), - process_images_(false) { + profile_(NULL) { // 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() { - ClearCaches(); + FreePlatformCaches(); RemoveUnusedThemes(); } @@ -309,121 +174,38 @@ 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()); - // 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); - } + SkBitmap* bitmap = NULL; - // We loaded successfully. Cache the bitmap. - image_cache_[id] = result.get(); - return result.release(); - } else { - NOTREACHED() << "Failed to load a requested image"; - return NULL; - } + if (theme_pack_.get()) + bitmap = theme_pack_->GetBitmapNamed(id); + + if (!bitmap) + bitmap = rb_.GetBitmapNamed(id); + + return bitmap; } SkColor BrowserThemeProvider::GetColor(int id) const { DCHECK(CalledOnValidThread()); - // 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); - } - - 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); - } + SkColor color; + if (theme_pack_.get() && theme_pack_->GetColor(id, &color)) + return color; - // 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; + return GetDefaultColor(id); } bool BrowserThemeProvider::GetDisplayProperty(int id, int* result) const { - 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; + if (theme_pack_.get()) + return theme_pack_->GetDisplayProperty(id, result); + + return GetDefaultDisplayProperty(id, result); } bool BrowserThemeProvider::ShouldUseNativeFrame() const { @@ -440,15 +222,10 @@ bool BrowserThemeProvider::HasCustomImage(int id) const { if (!HasThemeableImage(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); + if (theme_pack_) + return theme_pack_->HasCustomImage(id); + + return false; } RefCountedMemory* BrowserThemeProvider::GetRawData(int id) const { @@ -458,57 +235,25 @@ RefCountedMemory* BrowserThemeProvider::GetRawData(int id) const { if (id == IDR_PRODUCT_LOGO && ntp_alternate != 0) id = IDR_PRODUCT_LOGO_WHITE; - RawDataMap::const_iterator data_iter = raw_data_.find(id); - if (data_iter != raw_data_.end()) - return data_iter->second; - - RefCountedMemory* data = ReadThemeFileData(id); + RefCountedMemory* data = NULL; + if (theme_pack_.get()) + data = theme_pack_->GetRawData(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. - ClearCaches(); + FreePlatformCaches(); 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(); - SaveImageData(extension->GetThemeImages()); - SaveColorData(); - SaveTintData(); - SaveDisplayPropertyData(); + BuildFromExtension(extension); 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_); } @@ -543,36 +288,6 @@ 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. @@ -646,313 +361,8 @@ int BrowserThemeProvider::StringToTiling(const std::string& tiling) { return BrowserThemeProvider::NO_REPEAT; } -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 { +// static +color_utils::HSL BrowserThemeProvider::GetDefaultTint(int id) { switch (id) { case TINT_FRAME: return kDefaultTintFrame; @@ -972,53 +382,8 @@ color_utils::HSL BrowserThemeProvider::GetDefaultTint(int id) const { } } -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 { +// static +SkColor BrowserThemeProvider::GetDefaultColor(int id) { switch (id) { case COLOR_FRAME: return kDefaultColorFrame; @@ -1042,6 +407,8 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) const { 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: @@ -1050,6 +417,8 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) const { 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: @@ -1060,289 +429,114 @@ SkColor BrowserThemeProvider::GetDefaultColor(int id) const { } } -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(); - - 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) { - if (!images_path.empty()) { - images_[id] = - WideToUTF8(images_path.AppendASCII(val).ToWStringHack()); - resource_names_[id] = WideToASCII(*iter); - } else { - images_[id] = val; - } - } - } +// static +bool BrowserThemeProvider::GetDefaultDisplayProperty(int id, int* result) { + switch (id) { + case NTP_BACKGROUND_ALIGNMENT: + *result = kDefaultDisplayPropertyNTPAlignment; + return true; + case NTP_BACKGROUND_TILING: + *result = kDefaultDisplayPropertyNTPTiling; + return true; + case NTP_LOGO_ALTERNATE: + *result = kDefaultDisplayPropertyNTPInverseLogo; + return true; } + + return false; } -void BrowserThemeProvider::SetColorData(DictionaryValue* colors_value) { - colors_.clear(); +color_utils::HSL BrowserThemeProvider::GetTint(int id) const { + DCHECK(CalledOnValidThread()); - if (!colors_value) - return; + color_utils::HSL hsl; + if (theme_pack_.get() && theme_pack_->GetTint(id, &hsl)) + return hsl; - 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); - } - } - } + return GetDefaultTint(id); } -void BrowserThemeProvider::SetTintData(DictionaryValue* tints_value) { - tints_.clear(); - - if (!tints_value) - return; +void BrowserThemeProvider::ClearAllThemeData() { + // Clear our image cache. + FreePlatformCaches(); + theme_pack_ = 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; - } - } + profile_->GetPrefs()->ClearPref(prefs::kCurrentThemePackFilename); + SaveThemeID(kDefaultThemeID); } -void BrowserThemeProvider::SetDisplayPropertyData( - DictionaryValue* display_properties_value) { - display_properties_.clear(); +void BrowserThemeProvider::LoadThemePrefs() { + PrefService* prefs = profile_->GetPrefs(); - if (!display_properties_value) - return; + std::string current_id = GetThemeID(); + if (current_id != kDefaultThemeID) { + bool loaded_pack = false; - 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; + // 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; } - } -} -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()); + if (loaded_pack) { + UserMetrics::RecordAction("Themes.Loaded", profile_); } else { - 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::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)); + // 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_); + } } } } } -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); - } -} - -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::NotifyThemeChanged() { + // Redraw! + NotificationService* service = NotificationService::current(); + service->Notify(NotificationType::BROWSER_THEME_CHANGED, + Source<BrowserThemeProvider>(this), + NotificationService::NoDetails()); } -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); - } - } +#if defined(OS_WIN) +void BrowserThemeProvider::FreePlatformCaches() { + // Views (Skia) has no platform image cache to clear. } +#endif -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::SavePackName(const FilePath& pack_path) { + profile_->GetPrefs()->SetFilePath( + prefs::kCurrentThemePackFilename, pack_path); } void BrowserThemeProvider::SaveThemeID(const std::string& id) { profile_->GetPrefs()->SetString(prefs::kCurrentThemeID, UTF8ToWide(id)); } -void BrowserThemeProvider::ClearCaches() { - FreePlatformCaches(); - STLDeleteValues(&image_cache_); - - // Scope for AutoLock on themed_image_cache. - { - AutoLock lock(themed_image_cache_lock_); - STLDeleteValues(&themed_image_cache_); +void BrowserThemeProvider::BuildFromExtension(Extension* extension) { + scoped_refptr<BrowserThemePack> pack = + BrowserThemePack::BuildFromExtension(extension); + if (!pack.get()) { + NOTREACHED() << "Could not load theme."; + return; } - 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(); -} + // 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)); -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))); + SavePackName(pack_path); + theme_pack_ = pack; } diff --git a/chrome/browser/browser_theme_provider.h b/chrome/browser/browser_theme_provider.h index 04117b4..62736b4 100644 --- a/chrome/browser/browser_theme_provider.h +++ b/chrome/browser/browser_theme_provider.h @@ -22,48 +22,13 @@ 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; @@ -76,38 +41,11 @@ 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(); @@ -190,10 +128,6 @@ 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); @@ -209,44 +143,25 @@ class BrowserThemeProvider : public NonThreadSafe, // Parse tiling values from something like "no-repeat" into a Tiling value. static int StringToTiling(const std::string& tiling); - // 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_; + // 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); + + // Returns true and sets |result| to the requested default property, if |id| + // is valid. + static bool GetDefaultDisplayProperty(int id, int* result); // 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. - void ClearAllThemeData(); + virtual void ClearAllThemeData(); // Load theme data from preferences. virtual void LoadThemePrefs(); @@ -254,116 +169,33 @@ 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; - 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; + // Saves the filename of the cached theme pack. + void SavePackName(const FilePath& pack_path); // Save the id of the last theme installed. void SaveThemeID(const std::string& id); - // Frees generated images and clears the image cache. - void ClearCaches(); + // Implementation of SetTheme() (and the fallback from LoadThemePrefs() in + // case we don't have a theme pack). + void BuildFromExtension(Extension* extension); // 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_; @@ -374,25 +206,10 @@ class BrowserThemeProvider : public NonThreadSafe, mutable NSColorMap nscolor_cache_; #endif - mutable ImagesDiskCache images_disk_cache_; - ResourceBundle& rb_; Profile* profile_; - 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_; + scoped_refptr<BrowserThemePack> theme_pack_; DISALLOW_COPY_AND_ASSIGN(BrowserThemeProvider); }; diff --git a/chrome/browser/browser_theme_provider_mac.mm b/chrome/browser/browser_theme_provider_mac.mm index 65c2182..ec3d4a8 100644 --- a/chrome/browser/browser_theme_provider_mac.mm +++ b/chrome/browser/browser_theme_provider_mac.mm @@ -8,6 +8,7 @@ #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 { @@ -75,9 +76,8 @@ NSColor* BrowserThemeProvider::GetNSColor(int id) const { if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - ColorMap::const_iterator color_iter = colors_.find(GetColorKey(id)); - if (color_iter != colors_.end()) { - const SkColor& sk_color = color_iter->second; + SkColor sk_color; + if (theme_pack_.get() && theme_pack_->GetColor(id, &sk_color)) { NSColor* color = [NSColor colorWithCalibratedRed:SkColorGetR(sk_color)/255.0 green:SkColorGetG(sk_color)/255.0 @@ -102,9 +102,8 @@ NSColor* BrowserThemeProvider::GetNSColorTint(int id) const { if (nscolor_iter != nscolor_cache_.end()) return nscolor_iter->second; - TintMap::const_iterator tint_iter = tints_.find(GetTintKey(id)); - if (tint_iter != tints_.end()) { - color_utils::HSL tint = tint_iter->second; + color_utils::HSL tint; + if (theme_pack_.get() && theme_pack_->GetTint(id, &tint)) { 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 6556a56..533c7af 100644 --- a/chrome/browser/browser_theme_provider_unittest.cc +++ b/chrome/browser/browser_theme_provider_unittest.cc @@ -8,87 +8,7 @@ #include "base/values.h" #include "base/json/json_reader.h" -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) { +TEST(BrowserThemeProviderTest, AlignmentConversion) { // Verify that we get out what we put in. std::string top_left = "top left"; int alignment = BrowserThemeProvider::StringToAlignment(top_left); @@ -113,7 +33,7 @@ TEST_F(BrowserThemeProviderTest, AlignmentConversion) { EXPECT_EQ("", BrowserThemeProvider::AlignmentToString(alignment)); } -TEST_F(BrowserThemeProviderTest, AlignmentConversionInput) { +TEST(BrowserThemeProviderTest, AlignmentConversionInput) { // Verify that we output in an expected format. int alignment = BrowserThemeProvider::StringToAlignment("right bottom"); EXPECT_EQ("bottom right", BrowserThemeProvider::AlignmentToString(alignment)); @@ -130,98 +50,3 @@ TEST_F(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 5d494fc..ae9395b 100644 --- a/chrome/browser/cocoa/download_shelf_controller.mm +++ b/chrome/browser/cocoa/download_shelf_controller.mm @@ -114,10 +114,12 @@ 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::kDefaultColorBookmarkText; + BrowserThemeProvider::GetDefaultColor( + BrowserThemeProvider::COLOR_BOOKMARK_TEXT); + } } NSColor* color = useDefaultColor ? diff --git a/chrome/browser/extensions/theme_installed_infobar_delegate.cc b/chrome/browser/extensions/theme_installed_infobar_delegate.cc index 879cbb2..caea07d 100644 --- a/chrome/browser/extensions/theme_installed_infobar_delegate.cc +++ b/chrome/browser/extensions/theme_installed_infobar_delegate.cc @@ -27,7 +27,9 @@ ThemeInstalledInfoBarDelegate::ThemeInstalledInfoBarDelegate( void ThemeInstalledInfoBarDelegate::InfoBarClosed() { ExtensionsService* service = profile_->GetExtensionsService(); - if (service) { + // 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_) { 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 8601855..6d94b9f 100644 --- a/chrome/browser/gtk/download_shelf_gtk.cc +++ b/chrome/browser/gtk/download_shelf_gtk.cc @@ -201,7 +201,8 @@ void DownloadShelfGtk::Observe(NotificationType type, // bad for some dark themes. bool use_default_color = theme_provider_->GetColor( BrowserThemeProvider::COLOR_BOOKMARK_TEXT) == - BrowserThemeProvider::kDefaultColorBookmarkText; + BrowserThemeProvider::GetDefaultColor( + BrowserThemeProvider::COLOR_BOOKMARK_TEXT); 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 4f35110..9330f30 100644 --- a/chrome/browser/gtk/gtk_theme_provider.cc +++ b/chrome/browser/gtk/gtk_theme_provider.cc @@ -8,6 +8,8 @@ #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" @@ -57,6 +59,26 @@ 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; @@ -84,9 +106,9 @@ GtkThemeProvider::~GtkThemeProvider() { gtk_widget_destroy(fake_window_); fake_label_.Destroy(); - // We have to call this because ClearCaches in ~BrowserThemeProvider doesn't - // call the right virutal FreePlatformCaches. - FreePerDisplaySurfaces(); + // We have to call this because FreePlatformCached() in ~BrowserThemeProvider + // doesn't call the right virutal FreePlatformCaches. + FreePlatformCaches(); // Disconnect from the destroy signal of any redisual widgets in // |chrome_buttons_|. @@ -103,6 +125,39 @@ 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), @@ -345,40 +400,10 @@ 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 @@ -475,34 +500,35 @@ void GtkThemeProvider::LoadGtkValues() { } } - SetThemeColorFromGtk(kColorFrame, &frame_color); + 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); + // Skip COLOR_FRAME_INACTIVE and the incognito colors, as they will be - // autogenerated from tints. - SetThemeColorFromGtk(kColorToolbar, &toolbar_color); - SetThemeColorFromGtk(kColorTabText, &label_color); - SetThemeColorFromGtk(kColorBookmarkText, &label_color); - SetThemeColorFromGtk(kColorControlBackground, + // 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, &window_style->bg[GTK_STATE_NORMAL]); - SetThemeColorFromGtk(kColorButtonBackground, + SetThemeColorFromGtk(BrowserThemeProvider::COLOR_BUTTON_BACKGROUND, &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 = GetTint(TINT_BACKGROUND_TAB); + color_utils::HSL inactive_tab_text_hsl = tints_[TINT_BACKGROUND_TAB]; if (inactive_tab_text_hsl.l < 0.5) inactive_tab_text_hsl.l = kDarkInactiveLuminance; else @@ -513,32 +539,26 @@ void GtkThemeProvider::LoadGtkValues() { else inactive_tab_text_hsl.s = kLightInactiveSaturation; - SetColor(kColorBackgroundTabText, - color_utils::HSLToSkColor(inactive_tab_text_hsl, 255)); + colors_[BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT] = + 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(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(); + 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); } -void GtkThemeProvider::SetThemeColorFromGtk(const char* id, GdkColor* color) { - SetColor(id, GdkToSkColor(color)); +void GtkThemeProvider::SetThemeColorFromGtk(int id, GdkColor* color) { + colors_[id] = GdkToSkColor(color); } -void GtkThemeProvider::SetThemeTintFromGtk( - const char* id, - GdkColor* color, - const color_utils::HSL& default_tint) { +void GtkThemeProvider::SetThemeTintFromGtk(int id, GdkColor* color) { + color_utils::HSL default_tint = GetDefaultTint(id); color_utils::HSL hsl; color_utils::SkColorToHSL(GdkToSkColor(color), &hsl); @@ -547,7 +567,19 @@ void GtkThemeProvider::SetThemeTintFromGtk( if (default_tint.l != -1) hsl.l = default_tint.l; - SetTint(id, hsl); + + 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; } void GtkThemeProvider::FreePerDisplaySurfaces() { @@ -561,6 +593,55 @@ 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 60d72e7..44fed99 100644 --- a/chrome/browser/gtk/gtk_theme_provider.h +++ b/chrome/browser/gtk/gtk_theme_provider.h @@ -40,6 +40,9 @@ 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(); @@ -86,24 +89,17 @@ class GtkThemeProvider : public BrowserThemeProvider, static GdkPixbuf* GetFolderIcon(bool native); static GdkPixbuf* GetDefaultFavicon(bool native); - 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: + typedef std::map<int, SkColor> ColorMap; + typedef std::map<int, color_utils::HSL> TintMap; + typedef std::map<int, SkBitmap*> ImageCache; + // 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(); @@ -115,9 +111,10 @@ class GtkThemeProvider : public BrowserThemeProvider, void LoadGtkValues(); // Sets the underlying theme colors/tints from a GTK color. - void SetThemeColorFromGtk(const char* id, GdkColor* color); - void SetThemeTintFromGtk(const char* id, GdkColor* color, - const color_utils::HSL& default_tint); + 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); // Split out from FreePlatformCaches so it can be called in our destructor; // FreePlatformCaches() is called from the BrowserThemeProvider's destructor, @@ -125,6 +122,16 @@ 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, @@ -142,6 +149,15 @@ 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 c3b85cb..93012ba 100644 --- a/chrome/browser/gtk/gtk_theme_provider_unittest.cc +++ b/chrome/browser/gtk/gtk_theme_provider_unittest.cc @@ -35,11 +35,6 @@ class GtkThemeProviderTest : public testing::Test { provider_ = GtkThemeProvider::GetFrom(&profile_); } - void UseThemeProvider(GtkThemeProvider* provider) { - profile_.UseThemeProvider(provider); - provider_ = GtkThemeProvider::GetFrom(&profile_); - } - protected: TestingProfile profile_; @@ -51,42 +46,11 @@ TEST_F(GtkThemeProviderTest, DefaultValues) { BuildProvider(); // Test that we get the default theme colors back when in normal mode. - 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); + 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; + } } TEST_F(GtkThemeProviderTest, UsingGtkValues) { @@ -104,32 +68,3 @@ 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 22444f4..3f5deee 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -150,6 +150,7 @@ 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 c8adfe4..85729ef 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -188,6 +188,8 @@ '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 f90d6d0..69b2d74 100755 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -526,6 +526,7 @@ '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 1a936b5..1b85a19 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 kThemeImagesDirname[] = FPL("Cached Theme Images"); +const FilePath::CharType kThemePackFilename[] = FPL("Cached Theme.pak"); 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 4473f32..ea79fd8 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 kThemeImagesDirname[]; +extern const FilePath::CharType kThemePackFilename[]; 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 acd77db..6c1edf9 100644 --- a/chrome/common/pref_names.cc +++ b/chrome/common/pref_names.cc @@ -250,6 +250,7 @@ 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 d3095b1..8229a6f 100644 --- a/chrome/common/pref_names.h +++ b/chrome/common/pref_names.h @@ -101,6 +101,7 @@ 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 differdeleted file mode 100644 index c1099db..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactive +++ /dev/null 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 differdeleted file mode 100644 index 5d73cfd..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito +++ /dev/null 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 differdeleted file mode 100644 index 78b69a4..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactive +++ /dev/null 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 differdeleted file mode 100644 index fbd9a95..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_original +++ /dev/null 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 differdeleted file mode 100644 index be17ca5..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognito +++ /dev/null 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 differdeleted file mode 100644 index 90cc6b4..0000000 --- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_original +++ /dev/null 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 differnew file mode 100644 index 0000000..0a76cb9 --- /dev/null +++ b/chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak diff --git a/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate b/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate index f869914..7c3eb03 100644 --- a/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate +++ b/chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate @@ -50,39 +50,8 @@ } }, "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", - "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 ] - } + "pack": "$1/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak" } }, "profile": { |