Home | History | Annotate | Download | only in themes
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/themes/browser_theme_pack.h"
      6 
      7 #include "base/stl_util-inl.h"
      8 #include "base/string_util.h"
      9 #include "base/threading/thread_restrictions.h"
     10 #include "base/utf_string_conversions.h"
     11 #include "base/values.h"
     12 #include "chrome/browser/themes/theme_service.h"
     13 #include "content/browser/browser_thread.h"
     14 #include "grit/app_resources.h"
     15 #include "grit/theme_resources.h"
     16 #include "net/base/file_stream.h"
     17 #include "net/base/net_errors.h"
     18 #include "third_party/skia/include/core/SkCanvas.h"
     19 #include "ui/base/resource/data_pack.h"
     20 #include "ui/base/resource/resource_bundle.h"
     21 #include "ui/gfx/codec/png_codec.h"
     22 #include "ui/gfx/skbitmap_operations.h"
     23 
     24 namespace {
     25 
     26 // Version number of the current theme pack. We just throw out and rebuild
     27 // theme packs that aren't int-equal to this.
     28 const int kThemePackVersion = 15;
     29 
     30 // IDs that are in the DataPack won't clash with the positive integer
     31 // int32_t. kHeaderID should always have the maximum value because we want the
     32 // "header" to be written last. That way we can detect whether the pack was
     33 // successfully written and ignore and regenerate if it was only partially
     34 // written (i.e. chrome crashed on a different thread while writing the pack).
     35 const int kHeaderID = UINT_MAX - 1;
     36 const int kTintsID = UINT_MAX - 2;
     37 const int kColorsID = UINT_MAX - 3;
     38 const int kDisplayPropertiesID = UINT_MAX - 4;
     39 const int kSourceImagesID = UINT_MAX - 5;
     40 
     41 // Static size of the tint/color/display property arrays that are mmapped.
     42 const int kTintArraySize = 6;
     43 const int kColorArraySize = 19;
     44 const int kDisplayPropertySize = 3;
     45 
     46 // The sum of kFrameBorderThickness and kNonClientRestoredExtraThickness from
     47 // OpaqueBrowserFrameView.
     48 const int kRestoredTabVerticalOffset = 15;
     49 
     50 // Persistent constants for the main images that we need. These have the same
     51 // names as their IDR_* counterparts but these values will always stay the
     52 // same.
     53 const int PRS_THEME_FRAME = 1;
     54 const int PRS_THEME_FRAME_INACTIVE = 2;
     55 const int PRS_THEME_FRAME_INCOGNITO = 3;
     56 const int PRS_THEME_FRAME_INCOGNITO_INACTIVE = 4;
     57 const int PRS_THEME_TOOLBAR = 5;
     58 const int PRS_THEME_TAB_BACKGROUND = 6;
     59 const int PRS_THEME_TAB_BACKGROUND_INCOGNITO = 7;
     60 const int PRS_THEME_TAB_BACKGROUND_V = 8;
     61 const int PRS_THEME_NTP_BACKGROUND = 9;
     62 const int PRS_THEME_FRAME_OVERLAY = 10;
     63 const int PRS_THEME_FRAME_OVERLAY_INACTIVE = 11;
     64 const int PRS_THEME_BUTTON_BACKGROUND = 12;
     65 const int PRS_THEME_NTP_ATTRIBUTION = 13;
     66 const int PRS_THEME_WINDOW_CONTROL_BACKGROUND = 14;
     67 
     68 struct PersistingImagesTable {
     69   // A non-changing integer ID meant to be saved in theme packs. This ID must
     70   // not change between versions of chrome.
     71   int persistent_id;
     72 
     73   // The IDR that depends on the whims of GRIT and therefore changes whenever
     74   // someone adds a new resource.
     75   int idr_id;
     76 
     77   // String to check for when parsing theme manifests or NULL if this isn't
     78   // supposed to be changeable by the user.
     79   const char* key;
     80 };
     81 
     82 // IDR_* resource names change whenever new resources are added; use persistent
     83 // IDs when storing to a cached pack.
     84 PersistingImagesTable kPersistingImages[] = {
     85   { PRS_THEME_FRAME, IDR_THEME_FRAME,
     86     "theme_frame" },
     87   { PRS_THEME_FRAME_INACTIVE, IDR_THEME_FRAME_INACTIVE,
     88     "theme_frame_inactive" },
     89   { PRS_THEME_FRAME_INCOGNITO, IDR_THEME_FRAME_INCOGNITO,
     90     "theme_frame_incognito" },
     91   { PRS_THEME_FRAME_INCOGNITO_INACTIVE, IDR_THEME_FRAME_INCOGNITO_INACTIVE,
     92     "theme_frame_incognito_inactive" },
     93   { PRS_THEME_TOOLBAR, IDR_THEME_TOOLBAR,
     94     "theme_toolbar" },
     95   { PRS_THEME_TAB_BACKGROUND, IDR_THEME_TAB_BACKGROUND,
     96     "theme_tab_background" },
     97   { PRS_THEME_TAB_BACKGROUND_INCOGNITO, IDR_THEME_TAB_BACKGROUND_INCOGNITO,
     98     "theme_tab_background_incognito" },
     99   { PRS_THEME_TAB_BACKGROUND_V, IDR_THEME_TAB_BACKGROUND_V,
    100     "theme_tab_background_v"},
    101   { PRS_THEME_NTP_BACKGROUND, IDR_THEME_NTP_BACKGROUND,
    102     "theme_ntp_background" },
    103   { PRS_THEME_FRAME_OVERLAY, IDR_THEME_FRAME_OVERLAY,
    104     "theme_frame_overlay" },
    105   { PRS_THEME_FRAME_OVERLAY_INACTIVE, IDR_THEME_FRAME_OVERLAY_INACTIVE,
    106     "theme_frame_overlay_inactive" },
    107   { PRS_THEME_BUTTON_BACKGROUND, IDR_THEME_BUTTON_BACKGROUND,
    108     "theme_button_background" },
    109   { PRS_THEME_NTP_ATTRIBUTION, IDR_THEME_NTP_ATTRIBUTION,
    110     "theme_ntp_attribution" },
    111   { PRS_THEME_WINDOW_CONTROL_BACKGROUND, IDR_THEME_WINDOW_CONTROL_BACKGROUND,
    112     "theme_window_control_background"},
    113 
    114   // The rest of these entries have no key because they can't be overridden
    115   // from the json manifest.
    116   { 15, IDR_BACK, NULL },
    117   { 16, IDR_BACK_D, NULL },
    118   { 17, IDR_BACK_H, NULL },
    119   { 18, IDR_BACK_P, NULL },
    120   { 19, IDR_FORWARD, NULL },
    121   { 20, IDR_FORWARD_D, NULL },
    122   { 21, IDR_FORWARD_H, NULL },
    123   { 22, IDR_FORWARD_P, NULL },
    124   { 23, IDR_HOME, NULL },
    125   { 24, IDR_HOME_H, NULL },
    126   { 25, IDR_HOME_P, NULL },
    127   { 26, IDR_RELOAD, NULL },
    128   { 27, IDR_RELOAD_H, NULL },
    129   { 28, IDR_RELOAD_P, NULL },
    130   { 29, IDR_STOP, NULL },
    131   { 30, IDR_STOP_D, NULL },
    132   { 31, IDR_STOP_H, NULL },
    133   { 32, IDR_STOP_P, NULL },
    134   { 33, IDR_LOCATIONBG_C, NULL },
    135   { 34, IDR_LOCATIONBG_L, NULL },
    136   { 35, IDR_LOCATIONBG_R, NULL },
    137   { 36, IDR_BROWSER_ACTIONS_OVERFLOW, NULL },
    138   { 37, IDR_BROWSER_ACTIONS_OVERFLOW_H, NULL },
    139   { 38, IDR_BROWSER_ACTIONS_OVERFLOW_P, NULL },
    140   { 39, IDR_TOOLS, NULL },
    141   { 40, IDR_TOOLS_H, NULL },
    142   { 41, IDR_TOOLS_P, NULL },
    143   { 42, IDR_MENU_DROPARROW, NULL },
    144   { 43, IDR_THROBBER, NULL },
    145   { 44, IDR_THROBBER_WAITING, NULL },
    146   { 45, IDR_THROBBER_LIGHT, NULL },
    147 };
    148 
    149 int GetPersistentIDByName(const std::string& key) {
    150   for (size_t i = 0; i < arraysize(kPersistingImages); ++i) {
    151     if (kPersistingImages[i].key != NULL &&
    152         base::strcasecmp(key.c_str(), kPersistingImages[i].key) == 0) {
    153       return kPersistingImages[i].persistent_id;
    154     }
    155   }
    156 
    157   return -1;
    158 }
    159 
    160 int GetPersistentIDByIDR(int idr) {
    161   for (size_t i = 0; i < arraysize(kPersistingImages); ++i) {
    162     if (kPersistingImages[i].idr_id == idr) {
    163       return kPersistingImages[i].persistent_id;
    164     }
    165   }
    166 
    167   return -1;
    168 }
    169 
    170 struct StringToIntTable {
    171   const char* key;
    172   int id;
    173 };
    174 
    175 // Strings used by themes to identify tints in the JSON.
    176 StringToIntTable kTintTable[] = {
    177   { "buttons", ThemeService::TINT_BUTTONS },
    178   { "frame", ThemeService::TINT_FRAME },
    179   { "frame_inactive", ThemeService::TINT_FRAME_INACTIVE },
    180   { "frame_incognito", ThemeService::TINT_FRAME_INCOGNITO },
    181   { "frame_incognito_inactive",
    182     ThemeService::TINT_FRAME_INCOGNITO_INACTIVE },
    183   { "background_tab", ThemeService::TINT_BACKGROUND_TAB },
    184   { NULL, 0 }
    185 };
    186 
    187 // Strings used by themes to identify colors in the JSON.
    188 StringToIntTable kColorTable[] = {
    189   { "frame", ThemeService::COLOR_FRAME },
    190   { "frame_inactive", ThemeService::COLOR_FRAME_INACTIVE },
    191   { "frame_incognito", ThemeService::COLOR_FRAME_INCOGNITO },
    192   { "frame_incognito_inactive",
    193     ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE },
    194   { "toolbar", ThemeService::COLOR_TOOLBAR },
    195   { "tab_text", ThemeService::COLOR_TAB_TEXT },
    196   { "tab_background_text", ThemeService::COLOR_BACKGROUND_TAB_TEXT },
    197   { "bookmark_text", ThemeService::COLOR_BOOKMARK_TEXT },
    198   { "ntp_background", ThemeService::COLOR_NTP_BACKGROUND },
    199   { "ntp_text", ThemeService::COLOR_NTP_TEXT },
    200   { "ntp_link", ThemeService::COLOR_NTP_LINK },
    201   { "ntp_link_underline", ThemeService::COLOR_NTP_LINK_UNDERLINE },
    202   { "ntp_header", ThemeService::COLOR_NTP_HEADER },
    203   { "ntp_section", ThemeService::COLOR_NTP_SECTION },
    204   { "ntp_section_text", ThemeService::COLOR_NTP_SECTION_TEXT },
    205   { "ntp_section_link", ThemeService::COLOR_NTP_SECTION_LINK },
    206   { "ntp_section_link_underline",
    207     ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE },
    208   { "control_background", ThemeService::COLOR_CONTROL_BACKGROUND },
    209   { "button_background", ThemeService::COLOR_BUTTON_BACKGROUND },
    210   { NULL, 0 }
    211 };
    212 
    213 // Strings used by themes to identify display properties keys in JSON.
    214 StringToIntTable kDisplayProperties[] = {
    215   { "ntp_background_alignment",
    216     ThemeService::NTP_BACKGROUND_ALIGNMENT },
    217   { "ntp_background_repeat", ThemeService::NTP_BACKGROUND_TILING },
    218   { "ntp_logo_alternate", ThemeService::NTP_LOGO_ALTERNATE },
    219   { NULL, 0 }
    220 };
    221 
    222 // Strings used by the tiling values in JSON.
    223 StringToIntTable kTilingStrings[] = {
    224   { "no-repeat", ThemeService::NO_REPEAT },
    225   { "repeat-x", ThemeService::REPEAT_X },
    226   { "repeat-y", ThemeService::REPEAT_Y },
    227   { "repeat", ThemeService::REPEAT },
    228   { NULL, 0 }
    229 };
    230 
    231 int GetIntForString(const std::string& key, StringToIntTable* table) {
    232   for (int i = 0; table[i].key != NULL; ++i) {
    233     if (base::strcasecmp(key.c_str(), table[i].key) == 0) {
    234       return table[i].id;
    235     }
    236   }
    237 
    238   return -1;
    239 }
    240 
    241 struct IntToIntTable {
    242   int key;
    243   int value;
    244 };
    245 
    246 // Mapping used in GenerateFrameImages() to associate frame images with the
    247 // tint ID that should maybe be applied to it.
    248 IntToIntTable kFrameTintMap[] = {
    249   { PRS_THEME_FRAME, ThemeService::TINT_FRAME },
    250   { PRS_THEME_FRAME_INACTIVE, ThemeService::TINT_FRAME_INACTIVE },
    251   { PRS_THEME_FRAME_OVERLAY, ThemeService::TINT_FRAME },
    252   { PRS_THEME_FRAME_OVERLAY_INACTIVE,
    253     ThemeService::TINT_FRAME_INACTIVE },
    254   { PRS_THEME_FRAME_INCOGNITO, ThemeService::TINT_FRAME_INCOGNITO },
    255   { PRS_THEME_FRAME_INCOGNITO_INACTIVE,
    256     ThemeService::TINT_FRAME_INCOGNITO_INACTIVE }
    257 };
    258 
    259 // Mapping used in GenerateTabBackgroundImages() to associate what frame image
    260 // goes with which tab background.
    261 IntToIntTable kTabBackgroundMap[] = {
    262   { PRS_THEME_TAB_BACKGROUND, PRS_THEME_FRAME },
    263   { PRS_THEME_TAB_BACKGROUND_INCOGNITO, PRS_THEME_FRAME_INCOGNITO }
    264 };
    265 
    266 // A list of images that don't need tinting or any other modification and can
    267 // be byte-copied directly into the finished DataPack. This should contain the
    268 // persistent IDs for all themeable image IDs that aren't in kFrameTintMap or
    269 // kTabBackgroundMap.
    270 const int kPreloadIDs[] = {
    271   PRS_THEME_TOOLBAR,
    272   PRS_THEME_NTP_BACKGROUND,
    273   PRS_THEME_BUTTON_BACKGROUND,
    274   PRS_THEME_NTP_ATTRIBUTION,
    275   PRS_THEME_WINDOW_CONTROL_BACKGROUND
    276 };
    277 
    278 // Returns a piece of memory with the contents of the file |path|.
    279 RefCountedMemory* ReadFileData(const FilePath& path) {
    280   if (!path.empty()) {
    281     net::FileStream file;
    282     int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ;
    283     if (file.Open(path, flags) == net::OK) {
    284       int64 avail = file.Available();
    285       if (avail > 0 && avail < INT_MAX) {
    286         size_t size = static_cast<size_t>(avail);
    287         std::vector<unsigned char> raw_data;
    288         raw_data.resize(size);
    289         char* data = reinterpret_cast<char*>(&(raw_data.front()));
    290         if (file.ReadUntilComplete(data, size) == avail)
    291           return RefCountedBytes::TakeVector(&raw_data);
    292       }
    293     }
    294   }
    295 
    296   return NULL;
    297 }
    298 
    299 // Does error checking for invalid incoming data while trying to read an
    300 // floating point value.
    301 bool ValidDoubleValue(ListValue* tint_list, int index, double* out) {
    302   if (tint_list->GetDouble(index, out))
    303     return true;
    304 
    305   int value = 0;
    306   if (tint_list->GetInteger(index, &value)) {
    307     *out = value;
    308     return true;
    309   }
    310 
    311   return false;
    312 }
    313 
    314 }  // namespace
    315 
    316 BrowserThemePack::~BrowserThemePack() {
    317   if (!data_pack_.get()) {
    318     delete header_;
    319     delete [] tints_;
    320     delete [] colors_;
    321     delete [] display_properties_;
    322     delete [] source_images_;
    323   }
    324 
    325   STLDeleteValues(&prepared_images_);
    326   STLDeleteValues(&loaded_images_);
    327 }
    328 
    329 // static
    330 BrowserThemePack* BrowserThemePack::BuildFromExtension(
    331     const Extension* extension) {
    332   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    333   DCHECK(extension);
    334   DCHECK(extension->is_theme());
    335 
    336   BrowserThemePack* pack = new BrowserThemePack;
    337   pack->BuildHeader(extension);
    338   pack->BuildTintsFromJSON(extension->GetThemeTints());
    339   pack->BuildColorsFromJSON(extension->GetThemeColors());
    340   pack->BuildDisplayPropertiesFromJSON(extension->GetThemeDisplayProperties());
    341 
    342   // Builds the images. (Image building is dependent on tints).
    343   FilePathMap file_paths;
    344   pack->ParseImageNamesFromJSON(extension->GetThemeImages(),
    345                                 extension->path(),
    346                                 &file_paths);
    347   pack->BuildSourceImagesArray(file_paths);
    348 
    349   if (!pack->LoadRawBitmapsTo(file_paths, &pack->prepared_images_))
    350     return NULL;
    351 
    352   pack->GenerateFrameImages(&pack->prepared_images_);
    353 
    354 #if !defined(OS_MACOSX)
    355   // OSX uses its own special buttons that are PDFs that do odd sorts of vector
    356   // graphics tricks. Other platforms use bitmaps and we must pre-tint them.
    357   pack->GenerateTintedButtons(
    358       pack->GetTintInternal(ThemeService::TINT_BUTTONS),
    359       &pack->prepared_images_);
    360 #endif
    361 
    362   pack->GenerateTabBackgroundImages(&pack->prepared_images_);
    363 
    364   // The BrowserThemePack is now in a consistent state.
    365   return pack;
    366 }
    367 
    368 // static
    369 scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromDataPack(
    370     FilePath path, const std::string& expected_id) {
    371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    372   scoped_refptr<BrowserThemePack> pack(new BrowserThemePack);
    373   pack->data_pack_.reset(new ui::DataPack);
    374 
    375   if (!pack->data_pack_->Load(path)) {
    376     LOG(ERROR) << "Failed to load theme data pack.";
    377     return NULL;
    378   }
    379 
    380   base::StringPiece pointer;
    381   if (!pack->data_pack_->GetStringPiece(kHeaderID, &pointer))
    382     return NULL;
    383   pack->header_ = reinterpret_cast<BrowserThemePackHeader*>(const_cast<char*>(
    384       pointer.data()));
    385 
    386   if (pack->header_->version != kThemePackVersion) {
    387     DLOG(ERROR) << "BuildFromDataPack failure! Version mismatch!";
    388     return NULL;
    389   }
    390   // TODO(erg): Check endianess once DataPack works on the other endian.
    391   std::string theme_id(reinterpret_cast<char*>(pack->header_->theme_id),
    392                        Extension::kIdSize);
    393   std::string truncated_id = expected_id.substr(0, Extension::kIdSize);
    394   if (theme_id != truncated_id) {
    395     DLOG(ERROR) << "Wrong id: " << theme_id << " vs " << expected_id;
    396     return NULL;
    397   }
    398 
    399   if (!pack->data_pack_->GetStringPiece(kTintsID, &pointer))
    400     return NULL;
    401   pack->tints_ = reinterpret_cast<TintEntry*>(const_cast<char*>(
    402       pointer.data()));
    403 
    404   if (!pack->data_pack_->GetStringPiece(kColorsID, &pointer))
    405     return NULL;
    406   pack->colors_ =
    407       reinterpret_cast<ColorPair*>(const_cast<char*>(pointer.data()));
    408 
    409   if (!pack->data_pack_->GetStringPiece(kDisplayPropertiesID, &pointer))
    410     return NULL;
    411   pack->display_properties_ = reinterpret_cast<DisplayPropertyPair*>(
    412       const_cast<char*>(pointer.data()));
    413 
    414   if (!pack->data_pack_->GetStringPiece(kSourceImagesID, &pointer))
    415     return NULL;
    416   pack->source_images_ = reinterpret_cast<int*>(
    417       const_cast<char*>(pointer.data()));
    418 
    419   return pack;
    420 }
    421 
    422 bool BrowserThemePack::WriteToDisk(FilePath path) const {
    423   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
    424   // Add resources for each of the property arrays.
    425   RawDataForWriting resources;
    426   resources[kHeaderID] = base::StringPiece(
    427       reinterpret_cast<const char*>(header_), sizeof(BrowserThemePackHeader));
    428   resources[kTintsID] = base::StringPiece(
    429       reinterpret_cast<const char*>(tints_), sizeof(TintEntry[kTintArraySize]));
    430   resources[kColorsID] = base::StringPiece(
    431       reinterpret_cast<const char*>(colors_),
    432       sizeof(ColorPair[kColorArraySize]));
    433   resources[kDisplayPropertiesID] = base::StringPiece(
    434       reinterpret_cast<const char*>(display_properties_),
    435       sizeof(DisplayPropertyPair[kDisplayPropertySize]));
    436 
    437   int source_count = 1;
    438   int* end = source_images_;
    439   for (; *end != -1 ; end++)
    440     source_count++;
    441   resources[kSourceImagesID] = base::StringPiece(
    442       reinterpret_cast<const char*>(source_images_),
    443       source_count * sizeof(*source_images_));
    444 
    445   AddRawImagesTo(image_memory_, &resources);
    446 
    447   RawImages reencoded_images;
    448   RepackImages(prepared_images_, &reencoded_images);
    449   AddRawImagesTo(reencoded_images, &resources);
    450 
    451   return ui::DataPack::WritePack(path, resources);
    452 }
    453 
    454 bool BrowserThemePack::GetTint(int id, color_utils::HSL* hsl) const {
    455   if (tints_) {
    456     for (int i = 0; i < kTintArraySize; ++i) {
    457       if (tints_[i].id == id) {
    458         hsl->h = tints_[i].h;
    459         hsl->s = tints_[i].s;
    460         hsl->l = tints_[i].l;
    461         return true;
    462       }
    463     }
    464   }
    465 
    466   return false;
    467 }
    468 
    469 bool BrowserThemePack::GetColor(int id, SkColor* color) const {
    470   if (colors_) {
    471     for (int i = 0; i < kColorArraySize; ++i) {
    472       if (colors_[i].id == id) {
    473         *color = colors_[i].color;
    474         return true;
    475       }
    476     }
    477   }
    478 
    479   return false;
    480 }
    481 
    482 bool BrowserThemePack::GetDisplayProperty(int id, int* result) const {
    483   if (display_properties_) {
    484     for (int i = 0; i < kDisplayPropertySize; ++i) {
    485       if (display_properties_[i].id == id) {
    486         *result = display_properties_[i].property;
    487         return true;
    488       }
    489     }
    490   }
    491 
    492   return false;
    493 }
    494 
    495 SkBitmap* BrowserThemePack::GetBitmapNamed(int idr_id) const {
    496   int prs_id = GetPersistentIDByIDR(idr_id);
    497   if (prs_id == -1)
    498     return NULL;
    499 
    500   // Check our cache of prepared images, first.
    501   ImageCache::const_iterator image_iter = prepared_images_.find(prs_id);
    502   if (image_iter != prepared_images_.end())
    503     return image_iter->second;
    504 
    505   // Check if we've already loaded this image.
    506   image_iter = loaded_images_.find(prs_id);
    507   if (image_iter != loaded_images_.end())
    508     return image_iter->second;
    509 
    510   scoped_refptr<RefCountedMemory> memory;
    511   if (data_pack_.get()) {
    512     memory = data_pack_->GetStaticMemory(prs_id);
    513   } else {
    514     RawImages::const_iterator it = image_memory_.find(prs_id);
    515     if (it != image_memory_.end()) {
    516       memory = it->second;
    517     }
    518   }
    519 
    520   if (memory.get()) {
    521     // Decode the PNG.
    522     SkBitmap bitmap;
    523     if (!gfx::PNGCodec::Decode(memory->front(), memory->size(),
    524                                &bitmap)) {
    525       NOTREACHED() << "Unable to decode theme image resource " << idr_id
    526                    << " from saved DataPack.";
    527       return NULL;
    528     }
    529 
    530     SkBitmap* ret = new SkBitmap(bitmap);
    531     loaded_images_[prs_id] = ret;
    532 
    533     return ret;
    534   }
    535 
    536   return NULL;
    537 }
    538 
    539 RefCountedMemory* BrowserThemePack::GetRawData(int idr_id) const {
    540   RefCountedMemory* memory = NULL;
    541   int prs_id = GetPersistentIDByIDR(idr_id);
    542 
    543   if (prs_id != -1) {
    544     if (data_pack_.get()) {
    545       memory = data_pack_->GetStaticMemory(prs_id);
    546     } else {
    547       RawImages::const_iterator it = image_memory_.find(prs_id);
    548       if (it != image_memory_.end()) {
    549         memory = it->second;
    550       }
    551     }
    552   }
    553 
    554   return memory;
    555 }
    556 
    557 bool BrowserThemePack::HasCustomImage(int idr_id) const {
    558   int prs_id = GetPersistentIDByIDR(idr_id);
    559   if (prs_id == -1)
    560     return false;
    561 
    562   int* img = source_images_;
    563   for (; *img != -1; ++img) {
    564     if (*img == prs_id)
    565       return true;
    566   }
    567 
    568   return false;
    569 }
    570 
    571 // private:
    572 
    573 BrowserThemePack::BrowserThemePack()
    574     : header_(NULL),
    575       tints_(NULL),
    576       colors_(NULL),
    577       display_properties_(NULL),
    578       source_images_(NULL) {
    579 }
    580 
    581 void BrowserThemePack::BuildHeader(const Extension* extension) {
    582   header_ = new BrowserThemePackHeader;
    583   header_->version = kThemePackVersion;
    584 
    585   // TODO(erg): Need to make this endian safe on other computers. Prerequisite
    586   // is that ui::DataPack removes this same check.
    587 #if defined(__BYTE_ORDER)
    588   // Linux check
    589   COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN,
    590                  datapack_assumes_little_endian);
    591 #elif defined(__BIG_ENDIAN__)
    592   // Mac check
    593   #error DataPack assumes little endian
    594 #endif
    595   header_->little_endian = 1;
    596 
    597   const std::string& id = extension->id();
    598   memcpy(header_->theme_id, id.c_str(), Extension::kIdSize);
    599 }
    600 
    601 void BrowserThemePack::BuildTintsFromJSON(DictionaryValue* tints_value) {
    602   tints_ = new TintEntry[kTintArraySize];
    603   for (int i = 0; i < kTintArraySize; ++i) {
    604     tints_[i].id = -1;
    605     tints_[i].h = -1;
    606     tints_[i].s = -1;
    607     tints_[i].l = -1;
    608   }
    609 
    610   if (!tints_value)
    611     return;
    612 
    613   // Parse the incoming data from |tints_value| into an intermediary structure.
    614   std::map<int, color_utils::HSL> temp_tints;
    615   for (DictionaryValue::key_iterator iter(tints_value->begin_keys());
    616        iter != tints_value->end_keys(); ++iter) {
    617     ListValue* tint_list;
    618     if (tints_value->GetList(*iter, &tint_list) &&
    619         (tint_list->GetSize() == 3)) {
    620       color_utils::HSL hsl = { -1, -1, -1 };
    621 
    622       if (ValidDoubleValue(tint_list, 0, &hsl.h) &&
    623           ValidDoubleValue(tint_list, 1, &hsl.s) &&
    624           ValidDoubleValue(tint_list, 2, &hsl.l)) {
    625         int id = GetIntForString(*iter, kTintTable);
    626         if (id != -1) {
    627           temp_tints[id] = hsl;
    628         }
    629       }
    630     }
    631   }
    632 
    633   // Copy data from the intermediary data structure to the array.
    634   int count = 0;
    635   for (std::map<int, color_utils::HSL>::const_iterator it =
    636            temp_tints.begin(); it != temp_tints.end() && count < kTintArraySize;
    637        ++it, ++count) {
    638     tints_[count].id = it->first;
    639     tints_[count].h = it->second.h;
    640     tints_[count].s = it->second.s;
    641     tints_[count].l = it->second.l;
    642   }
    643 }
    644 
    645 void BrowserThemePack::BuildColorsFromJSON(DictionaryValue* colors_value) {
    646   colors_ = new ColorPair[kColorArraySize];
    647   for (int i = 0; i < kColorArraySize; ++i) {
    648     colors_[i].id = -1;
    649     colors_[i].color = SkColorSetRGB(0, 0, 0);
    650   }
    651 
    652   std::map<int, SkColor> temp_colors;
    653   if (colors_value)
    654     ReadColorsFromJSON(colors_value, &temp_colors);
    655   GenerateMissingColors(&temp_colors);
    656 
    657   // Copy data from the intermediary data structure to the array.
    658   int count = 0;
    659   for (std::map<int, SkColor>::const_iterator it = temp_colors.begin();
    660        it != temp_colors.end() && count < kColorArraySize; ++it, ++count) {
    661     colors_[count].id = it->first;
    662     colors_[count].color = it->second;
    663   }
    664 }
    665 
    666 void BrowserThemePack::ReadColorsFromJSON(
    667     DictionaryValue* colors_value,
    668     std::map<int, SkColor>* temp_colors) {
    669   // Parse the incoming data from |colors_value| into an intermediary structure.
    670   for (DictionaryValue::key_iterator iter(colors_value->begin_keys());
    671        iter != colors_value->end_keys(); ++iter) {
    672     ListValue* color_list;
    673     if (colors_value->GetList(*iter, &color_list) &&
    674         ((color_list->GetSize() == 3) || (color_list->GetSize() == 4))) {
    675       SkColor color = SK_ColorWHITE;
    676       int r, g, b;
    677       if (color_list->GetInteger(0, &r) &&
    678           color_list->GetInteger(1, &g) &&
    679           color_list->GetInteger(2, &b)) {
    680         if (color_list->GetSize() == 4) {
    681           double alpha;
    682           int alpha_int;
    683           if (color_list->GetDouble(3, &alpha)) {
    684             color = SkColorSetARGB(static_cast<int>(alpha * 255), r, g, b);
    685           } else if (color_list->GetInteger(3, &alpha_int) &&
    686                      (alpha_int == 0 || alpha_int == 1)) {
    687             color = SkColorSetARGB(alpha_int ? 255 : 0, r, g, b);
    688           } else {
    689             // Invalid entry for part 4.
    690             continue;
    691           }
    692         } else {
    693           color = SkColorSetRGB(r, g, b);
    694         }
    695 
    696         int id = GetIntForString(*iter, kColorTable);
    697         if (id != -1) {
    698           (*temp_colors)[id] = color;
    699         }
    700       }
    701     }
    702   }
    703 }
    704 
    705 void BrowserThemePack::GenerateMissingColors(
    706     std::map<int, SkColor>* colors) {
    707   // Generate link colors, if missing. (See GetColor()).
    708   if (!colors->count(ThemeService::COLOR_NTP_HEADER) &&
    709       colors->count(ThemeService::COLOR_NTP_SECTION)) {
    710     (*colors)[ThemeService::COLOR_NTP_HEADER] =
    711         (*colors)[ThemeService::COLOR_NTP_SECTION];
    712   }
    713 
    714   if (!colors->count(ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE) &&
    715       colors->count(ThemeService::COLOR_NTP_SECTION_LINK)) {
    716     SkColor color_section_link =
    717         (*colors)[ThemeService::COLOR_NTP_SECTION_LINK];
    718     (*colors)[ThemeService::COLOR_NTP_SECTION_LINK_UNDERLINE] =
    719         SkColorSetA(color_section_link, SkColorGetA(color_section_link) / 3);
    720   }
    721 
    722   if (!colors->count(ThemeService::COLOR_NTP_LINK_UNDERLINE) &&
    723       colors->count(ThemeService::COLOR_NTP_LINK)) {
    724     SkColor color_link = (*colors)[ThemeService::COLOR_NTP_LINK];
    725     (*colors)[ThemeService::COLOR_NTP_LINK_UNDERLINE] =
    726         SkColorSetA(color_link, SkColorGetA(color_link) / 3);
    727   }
    728 
    729   // Generate frame colors, if missing. (See GenerateFrameColors()).
    730   SkColor frame;
    731   std::map<int, SkColor>::const_iterator it =
    732       colors->find(ThemeService::COLOR_FRAME);
    733   if (it != colors->end()) {
    734     frame = it->second;
    735   } else {
    736     frame = ThemeService::GetDefaultColor(
    737         ThemeService::COLOR_FRAME);
    738   }
    739 
    740   if (!colors->count(ThemeService::COLOR_FRAME)) {
    741     (*colors)[ThemeService::COLOR_FRAME] =
    742         HSLShift(frame, GetTintInternal(ThemeService::TINT_FRAME));
    743   }
    744   if (!colors->count(ThemeService::COLOR_FRAME_INACTIVE)) {
    745     (*colors)[ThemeService::COLOR_FRAME_INACTIVE] =
    746         HSLShift(frame, GetTintInternal(
    747             ThemeService::TINT_FRAME_INACTIVE));
    748   }
    749   if (!colors->count(ThemeService::COLOR_FRAME_INCOGNITO)) {
    750     (*colors)[ThemeService::COLOR_FRAME_INCOGNITO] =
    751         HSLShift(frame, GetTintInternal(
    752             ThemeService::TINT_FRAME_INCOGNITO));
    753   }
    754   if (!colors->count(ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE)) {
    755     (*colors)[ThemeService::COLOR_FRAME_INCOGNITO_INACTIVE] =
    756         HSLShift(frame, GetTintInternal(
    757             ThemeService::TINT_FRAME_INCOGNITO_INACTIVE));
    758   }
    759 }
    760 
    761 void BrowserThemePack::BuildDisplayPropertiesFromJSON(
    762     DictionaryValue* display_properties_value) {
    763   display_properties_ = new DisplayPropertyPair[kDisplayPropertySize];
    764   for (int i = 0; i < kDisplayPropertySize; ++i) {
    765     display_properties_[i].id = -1;
    766     display_properties_[i].property = 0;
    767   }
    768 
    769   if (!display_properties_value)
    770     return;
    771 
    772   std::map<int, int> temp_properties;
    773   for (DictionaryValue::key_iterator iter(
    774        display_properties_value->begin_keys());
    775        iter != display_properties_value->end_keys(); ++iter) {
    776     int property_id = GetIntForString(*iter, kDisplayProperties);
    777     switch (property_id) {
    778       case ThemeService::NTP_BACKGROUND_ALIGNMENT: {
    779         std::string val;
    780         if (display_properties_value->GetString(*iter, &val)) {
    781           temp_properties[ThemeService::NTP_BACKGROUND_ALIGNMENT] =
    782               ThemeService::StringToAlignment(val);
    783         }
    784         break;
    785       }
    786       case ThemeService::NTP_BACKGROUND_TILING: {
    787         std::string val;
    788         if (display_properties_value->GetString(*iter, &val)) {
    789           temp_properties[ThemeService::NTP_BACKGROUND_TILING] =
    790               GetIntForString(val, kTilingStrings);
    791         }
    792         break;
    793       }
    794       case ThemeService::NTP_LOGO_ALTERNATE: {
    795         int val = 0;
    796         if (display_properties_value->GetInteger(*iter, &val))
    797           temp_properties[ThemeService::NTP_LOGO_ALTERNATE] = val;
    798         break;
    799       }
    800     }
    801   }
    802 
    803   // Copy data from the intermediary data structure to the array.
    804   int count = 0;
    805   for (std::map<int, int>::const_iterator it = temp_properties.begin();
    806        it != temp_properties.end() && count < kDisplayPropertySize;
    807        ++it, ++count) {
    808     display_properties_[count].id = it->first;
    809     display_properties_[count].property = it->second;
    810   }
    811 }
    812 
    813 void BrowserThemePack::ParseImageNamesFromJSON(
    814     DictionaryValue* images_value,
    815     const FilePath& images_path,
    816     FilePathMap* file_paths) const {
    817   if (!images_value)
    818     return;
    819 
    820   for (DictionaryValue::key_iterator iter(images_value->begin_keys());
    821        iter != images_value->end_keys(); ++iter) {
    822     std::string val;
    823     if (images_value->GetString(*iter, &val)) {
    824       int id = GetPersistentIDByName(*iter);
    825       if (id != -1)
    826         (*file_paths)[id] = images_path.AppendASCII(val);
    827     }
    828   }
    829 }
    830 
    831 void BrowserThemePack::BuildSourceImagesArray(const FilePathMap& file_paths) {
    832   std::vector<int> ids;
    833   for (FilePathMap::const_iterator it = file_paths.begin();
    834        it != file_paths.end(); ++it) {
    835     ids.push_back(it->first);
    836   }
    837 
    838   source_images_ = new int[ids.size() + 1];
    839   std::copy(ids.begin(), ids.end(), source_images_);
    840   source_images_[ids.size()] = -1;
    841 }
    842 
    843 bool BrowserThemePack::LoadRawBitmapsTo(
    844     const FilePathMap& file_paths,
    845     ImageCache* raw_bitmaps) {
    846   // Themes should be loaded on the file thread, not the UI thread.
    847   // http://crbug.com/61838
    848   base::ThreadRestrictions::ScopedAllowIO allow_io;
    849 
    850   for (FilePathMap::const_iterator it = file_paths.begin();
    851        it != file_paths.end(); ++it) {
    852     scoped_refptr<RefCountedMemory> raw_data(ReadFileData(it->second));
    853     if (!raw_data.get()) {
    854       LOG(ERROR) << "Could not load theme image";
    855       return false;
    856     }
    857 
    858     int id = it->first;
    859 
    860     // Some images need to go directly into |image_memory_|. No modification is
    861     // necessary or desirable.
    862     bool is_copyable = false;
    863     for (size_t i = 0; i < arraysize(kPreloadIDs); ++i) {
    864       if (kPreloadIDs[i] == id) {
    865         is_copyable = true;
    866         break;
    867       }
    868     }
    869 
    870     if (is_copyable) {
    871       image_memory_[id] = raw_data;
    872     } else if (raw_data.get() && raw_data->size()) {
    873       // Decode the PNG.
    874       SkBitmap bitmap;
    875       if (gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(),
    876                                 &bitmap)) {
    877         (*raw_bitmaps)[it->first] = new SkBitmap(bitmap);
    878       } else {
    879         NOTREACHED() << "Unable to decode theme image resource " << it->first;
    880       }
    881     }
    882   }
    883 
    884   return true;
    885 }
    886 
    887 void BrowserThemePack::GenerateFrameImages(ImageCache* bitmaps) const {
    888   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    889 
    890   // Create all the output bitmaps in a separate cache and move them back into
    891   // the input bitmaps because there can be name collisions.
    892   ImageCache temp_output;
    893 
    894   for (size_t i = 0; i < arraysize(kFrameTintMap); ++i) {
    895     int prs_id = kFrameTintMap[i].key;
    896     scoped_ptr<SkBitmap> frame;
    897     // If there's no frame image provided for the specified id, then load
    898     // the default provided frame. If that's not provided, skip this whole
    899     // thing and just use the default images.
    900     int prs_base_id;
    901 
    902     if (prs_id == PRS_THEME_FRAME_INCOGNITO_INACTIVE) {
    903       prs_base_id = bitmaps->count(PRS_THEME_FRAME_INCOGNITO) ?
    904                     PRS_THEME_FRAME_INCOGNITO : PRS_THEME_FRAME;
    905     } else if (prs_id == PRS_THEME_FRAME_OVERLAY_INACTIVE) {
    906       prs_base_id = PRS_THEME_FRAME_OVERLAY;
    907     } else if (prs_id == PRS_THEME_FRAME_INACTIVE) {
    908       prs_base_id = PRS_THEME_FRAME;
    909     } else if (prs_id == PRS_THEME_FRAME_INCOGNITO &&
    910                !bitmaps->count(PRS_THEME_FRAME_INCOGNITO)) {
    911       prs_base_id = PRS_THEME_FRAME;
    912     } else {
    913       prs_base_id = prs_id;
    914     }
    915 
    916     if (bitmaps->count(prs_id)) {
    917       frame.reset(new SkBitmap(*(*bitmaps)[prs_id]));
    918     } else if (prs_base_id != prs_id && bitmaps->count(prs_base_id)) {
    919       frame.reset(new SkBitmap(*(*bitmaps)[prs_base_id]));
    920     } else if (prs_base_id == PRS_THEME_FRAME_OVERLAY &&
    921                bitmaps->count(PRS_THEME_FRAME)) {
    922       // If there is no theme overlay, don't tint the default frame,
    923       // because it will overwrite the custom frame image when we cache and
    924       // reload from disk.
    925       frame.reset(NULL);
    926     } else {
    927       // If the theme doesn't specify an image, then apply the tint to
    928       // the default frame.
    929       frame.reset(new SkBitmap(*rb.GetBitmapNamed(IDR_THEME_FRAME)));
    930     }
    931 
    932     if (frame.get()) {
    933       temp_output[prs_id] = new SkBitmap(
    934           SkBitmapOperations::CreateHSLShiftedBitmap(
    935               *frame, GetTintInternal(kFrameTintMap[i].value)));
    936     }
    937   }
    938 
    939   MergeImageCaches(temp_output, bitmaps);
    940 }
    941 
    942 void BrowserThemePack::GenerateTintedButtons(
    943     const color_utils::HSL& button_tint,
    944     ImageCache* processed_bitmaps) const {
    945   if (button_tint.h != -1 || button_tint.s != -1 || button_tint.l != -1) {
    946     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    947     const std::set<int>& idr_ids =
    948         ThemeService::GetTintableToolbarButtons();
    949     for (std::set<int>::const_iterator it = idr_ids.begin();
    950          it != idr_ids.end(); ++it) {
    951       int prs_id = GetPersistentIDByIDR(*it);
    952       DCHECK(prs_id > 0);
    953 
    954       // Fetch the image by IDR...
    955       scoped_ptr<SkBitmap> button(new SkBitmap(*rb.GetBitmapNamed(*it)));
    956 
    957       // but save a version with the persistent ID.
    958       (*processed_bitmaps)[prs_id] = new SkBitmap(
    959           SkBitmapOperations::CreateHSLShiftedBitmap(*button, button_tint));
    960     }
    961   }
    962 }
    963 
    964 void BrowserThemePack::GenerateTabBackgroundImages(ImageCache* bitmaps) const {
    965   ImageCache temp_output;
    966   for (size_t i = 0; i < arraysize(kTabBackgroundMap); ++i) {
    967     int prs_id = kTabBackgroundMap[i].key;
    968     int prs_base_id = kTabBackgroundMap[i].value;
    969 
    970     // We only need to generate the background tab images if we were provided
    971     // with a PRS_THEME_FRAME.
    972     ImageCache::const_iterator it = bitmaps->find(prs_base_id);
    973     if (it != bitmaps->end()) {
    974       SkBitmap bg_tint = SkBitmapOperations::CreateHSLShiftedBitmap(
    975           *(it->second), GetTintInternal(
    976               ThemeService::TINT_BACKGROUND_TAB));
    977       int vertical_offset = bitmaps->count(prs_id)
    978                             ? kRestoredTabVerticalOffset : 0;
    979       SkBitmap* bg_tab = new SkBitmap(SkBitmapOperations::CreateTiledBitmap(
    980           bg_tint, 0, vertical_offset, bg_tint.width(), bg_tint.height()));
    981 
    982       // If they've provided a custom image, overlay it.
    983       ImageCache::const_iterator overlay_it = bitmaps->find(prs_id);
    984       if (overlay_it != bitmaps->end()) {
    985         SkBitmap* overlay = overlay_it->second;
    986         SkCanvas canvas(*bg_tab);
    987         for (int x = 0; x < bg_tab->width(); x += overlay->width())
    988           canvas.drawBitmap(*overlay, static_cast<SkScalar>(x), 0, NULL);
    989       }
    990 
    991       temp_output[prs_id] = bg_tab;
    992     }
    993   }
    994 
    995   MergeImageCaches(temp_output, bitmaps);
    996 }
    997 
    998 void BrowserThemePack::RepackImages(const ImageCache& images,
    999                                     RawImages* reencoded_images) const {
   1000   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
   1001   for (ImageCache::const_iterator it = images.begin();
   1002        it != images.end(); ++it) {
   1003     std::vector<unsigned char> image_data;
   1004     if (!gfx::PNGCodec::EncodeBGRASkBitmap(*(it->second), false, &image_data)) {
   1005       NOTREACHED() << "Image file for resource " << it->first
   1006                    << " could not be encoded.";
   1007     } else {
   1008       (*reencoded_images)[it->first] = RefCountedBytes::TakeVector(&image_data);
   1009     }
   1010   }
   1011 }
   1012 
   1013 void BrowserThemePack::MergeImageCaches(
   1014     const ImageCache& source, ImageCache* destination) const {
   1015 
   1016   for (ImageCache::const_iterator it = source.begin(); it != source.end();
   1017        ++it) {
   1018     ImageCache::const_iterator bitmap_it = destination->find(it->first);
   1019     if (bitmap_it != destination->end())
   1020       delete bitmap_it->second;
   1021 
   1022     (*destination)[it->first] = it->second;
   1023   }
   1024 }
   1025 
   1026 void BrowserThemePack::AddRawImagesTo(const RawImages& images,
   1027                                       RawDataForWriting* out) const {
   1028   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
   1029   for (RawImages::const_iterator it = images.begin(); it != images.end();
   1030        ++it) {
   1031     (*out)[it->first] = base::StringPiece(
   1032         reinterpret_cast<const char*>(it->second->front()), it->second->size());
   1033   }
   1034 }
   1035 
   1036 color_utils::HSL BrowserThemePack::GetTintInternal(int id) const {
   1037   if (tints_) {
   1038     for (int i = 0; i < kTintArraySize; ++i) {
   1039       if (tints_[i].id == id) {
   1040         color_utils::HSL hsl;
   1041         hsl.h = tints_[i].h;
   1042         hsl.s = tints_[i].s;
   1043         hsl.l = tints_[i].l;
   1044         return hsl;
   1045       }
   1046     }
   1047   }
   1048 
   1049   return ThemeService::GetDefaultTint(id);
   1050 }
   1051