summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp1
-rw-r--r--base/base.gypi1
-rw-r--r--base/data_pack.cc97
-rw-r--r--base/data_pack.h10
-rw-r--r--base/data_pack_unittest.cc54
-rw-r--r--chrome/browser/browser_theme_pack.cc864
-rw-r--r--chrome/browser/browser_theme_pack.h191
-rw-r--r--chrome/browser/browser_theme_pack_unittest.cc335
-rw-r--r--chrome/browser/browser_theme_provider.cc1118
-rw-r--r--chrome/browser/browser_theme_provider.h217
-rw-r--r--chrome/browser/browser_theme_provider_mac.mm11
-rw-r--r--chrome/browser/browser_theme_provider_unittest.cc179
-rw-r--r--chrome/browser/cocoa/download_shelf_controller.mm6
-rw-r--r--chrome/browser/extensions/theme_installed_infobar_delegate.cc4
-rw-r--r--chrome/browser/gtk/download_shelf_gtk.cc3
-rw-r--r--chrome/browser/gtk/gtk_theme_provider.cc225
-rw-r--r--chrome/browser/gtk/gtk_theme_provider.h44
-rw-r--r--chrome/browser/gtk/gtk_theme_provider_unittest.cc75
-rw-r--r--chrome/browser/profile.cc1
-rwxr-xr-xchrome/chrome_browser.gypi2
-rwxr-xr-xchrome/chrome_tests.gypi1
-rw-r--r--chrome/common/chrome_constants.cc2
-rw-r--r--chrome/common/chrome_constants.h2
-rw-r--r--chrome/common/pref_names.cc1
-rw-r--r--chrome/common/pref_names.h1
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactivebin48806 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognitobin50613 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactivebin57089 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_originalbin68244 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognitobin30590 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_originalbin28215 -> 0 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pakbin0 -> 1233037 bytes
-rw-r--r--chrome/test/data/profiles/complex_theme/Default/PreferencesTemplate33
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(&gtk_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
deleted file mode 100644
index c1099db..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_inactive
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 5d73cfd..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 78b69a4..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_incognito_inactive
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index fbd9a95..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_frame_original
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index be17ca5..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_incognito
+++ /dev/null
Binary files differ
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
deleted file mode 100644
index 90cc6b4..0000000
--- a/chrome/test/data/profiles/complex_theme/Default/Cached Theme Images/theme_tab_background_original
+++ /dev/null
Binary files differ
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
new file mode 100644
index 0000000..0a76cb9
--- /dev/null
+++ b/chrome/test/data/profiles/complex_theme/Default/Extensions/mblmlcbknbnfebdfjnolmcapmdofhmme/1.1/Cached Theme.pak
Binary files differ
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": {