1// Copyright (c) 2012 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 <limits> 8 9#include "base/memory/ref_counted_memory.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/stl_util.h" 12#include "base/strings/string_number_conversions.h" 13#include "base/strings/string_util.h" 14#include "base/strings/utf_string_conversions.h" 15#include "base/threading/sequenced_worker_pool.h" 16#include "base/threading/thread_restrictions.h" 17#include "base/values.h" 18#include "chrome/browser/themes/theme_properties.h" 19#include "chrome/common/extensions/manifest_handlers/theme_handler.h" 20#include "content/public/browser/browser_thread.h" 21#include "extensions/common/id_util.h" 22#include "grit/theme_resources.h" 23#include "grit/ui_resources.h" 24#include "net/base/file_stream.h" 25#include "net/base/net_errors.h" 26#include "third_party/skia/include/core/SkCanvas.h" 27#include "ui/base/resource/data_pack.h" 28#include "ui/base/resource/resource_bundle.h" 29#include "ui/gfx/canvas.h" 30#include "ui/gfx/codec/png_codec.h" 31#include "ui/gfx/image/canvas_image_source.h" 32#include "ui/gfx/image/image.h" 33#include "ui/gfx/image/image_skia.h" 34#include "ui/gfx/image/image_skia_operations.h" 35#include "ui/gfx/screen.h" 36#include "ui/gfx/size_conversions.h" 37#include "ui/gfx/skia_util.h" 38 39using content::BrowserThread; 40using extensions::Extension; 41 42namespace { 43 44// Version number of the current theme pack. We just throw out and rebuild 45// theme packs that aren't int-equal to this. Increment this number if you 46// change default theme assets. 47const int kThemePackVersion = 32; 48 49// IDs that are in the DataPack won't clash with the positive integer 50// uint16. kHeaderID should always have the maximum value because we want the 51// "header" to be written last. That way we can detect whether the pack was 52// successfully written and ignore and regenerate if it was only partially 53// written (i.e. chrome crashed on a different thread while writing the pack). 54const int kMaxID = 0x0000FFFF; // Max unsigned 16-bit int. 55const int kHeaderID = kMaxID - 1; 56const int kTintsID = kMaxID - 2; 57const int kColorsID = kMaxID - 3; 58const int kDisplayPropertiesID = kMaxID - 4; 59const int kSourceImagesID = kMaxID - 5; 60const int kScaleFactorsID = kMaxID - 6; 61 62// The sum of kFrameBorderThickness and kNonClientRestoredExtraThickness from 63// OpaqueBrowserFrameView. 64const int kRestoredTabVerticalOffset = 15; 65 66// Persistent constants for the main images that we need. These have the same 67// names as their IDR_* counterparts but these values will always stay the 68// same. 69const int PRS_THEME_FRAME = 1; 70const int PRS_THEME_FRAME_INACTIVE = 2; 71const int PRS_THEME_FRAME_INCOGNITO = 3; 72const int PRS_THEME_FRAME_INCOGNITO_INACTIVE = 4; 73const int PRS_THEME_TOOLBAR = 5; 74const int PRS_THEME_TAB_BACKGROUND = 6; 75const int PRS_THEME_TAB_BACKGROUND_INCOGNITO = 7; 76const int PRS_THEME_TAB_BACKGROUND_V = 8; 77const int PRS_THEME_NTP_BACKGROUND = 9; 78const int PRS_THEME_FRAME_OVERLAY = 10; 79const int PRS_THEME_FRAME_OVERLAY_INACTIVE = 11; 80const int PRS_THEME_BUTTON_BACKGROUND = 12; 81const int PRS_THEME_NTP_ATTRIBUTION = 13; 82const int PRS_THEME_WINDOW_CONTROL_BACKGROUND = 14; 83 84struct PersistingImagesTable { 85 // A non-changing integer ID meant to be saved in theme packs. This ID must 86 // not change between versions of chrome. 87 int persistent_id; 88 89 // The IDR that depends on the whims of GRIT and therefore changes whenever 90 // someone adds a new resource. 91 int idr_id; 92 93 // String to check for when parsing theme manifests or NULL if this isn't 94 // supposed to be changeable by the user. 95 const char* key; 96}; 97 98// IDR_* resource names change whenever new resources are added; use persistent 99// IDs when storing to a cached pack. 100PersistingImagesTable kPersistingImages[] = { 101 { PRS_THEME_FRAME, IDR_THEME_FRAME, 102 "theme_frame" }, 103 { PRS_THEME_FRAME_INACTIVE, IDR_THEME_FRAME_INACTIVE, 104 "theme_frame_inactive" }, 105 { PRS_THEME_FRAME_INCOGNITO, IDR_THEME_FRAME_INCOGNITO, 106 "theme_frame_incognito" }, 107 { PRS_THEME_FRAME_INCOGNITO_INACTIVE, IDR_THEME_FRAME_INCOGNITO_INACTIVE, 108 "theme_frame_incognito_inactive" }, 109 { PRS_THEME_TOOLBAR, IDR_THEME_TOOLBAR, 110 "theme_toolbar" }, 111 { PRS_THEME_TAB_BACKGROUND, IDR_THEME_TAB_BACKGROUND, 112 "theme_tab_background" }, 113 { PRS_THEME_TAB_BACKGROUND_INCOGNITO, IDR_THEME_TAB_BACKGROUND_INCOGNITO, 114 "theme_tab_background_incognito" }, 115 { PRS_THEME_TAB_BACKGROUND_V, IDR_THEME_TAB_BACKGROUND_V, 116 "theme_tab_background_v"}, 117 { PRS_THEME_NTP_BACKGROUND, IDR_THEME_NTP_BACKGROUND, 118 "theme_ntp_background" }, 119 { PRS_THEME_FRAME_OVERLAY, IDR_THEME_FRAME_OVERLAY, 120 "theme_frame_overlay" }, 121 { PRS_THEME_FRAME_OVERLAY_INACTIVE, IDR_THEME_FRAME_OVERLAY_INACTIVE, 122 "theme_frame_overlay_inactive" }, 123 { PRS_THEME_BUTTON_BACKGROUND, IDR_THEME_BUTTON_BACKGROUND, 124 "theme_button_background" }, 125 { PRS_THEME_NTP_ATTRIBUTION, IDR_THEME_NTP_ATTRIBUTION, 126 "theme_ntp_attribution" }, 127 { PRS_THEME_WINDOW_CONTROL_BACKGROUND, IDR_THEME_WINDOW_CONTROL_BACKGROUND, 128 "theme_window_control_background"}, 129 130 // The rest of these entries have no key because they can't be overridden 131 // from the json manifest. 132 { 15, IDR_BACK, NULL }, 133 { 16, IDR_BACK_D, NULL }, 134 { 17, IDR_BACK_H, NULL }, 135 { 18, IDR_BACK_P, NULL }, 136 { 19, IDR_FORWARD, NULL }, 137 { 20, IDR_FORWARD_D, NULL }, 138 { 21, IDR_FORWARD_H, NULL }, 139 { 22, IDR_FORWARD_P, NULL }, 140 { 23, IDR_HOME, NULL }, 141 { 24, IDR_HOME_H, NULL }, 142 { 25, IDR_HOME_P, NULL }, 143 { 26, IDR_RELOAD, NULL }, 144 { 27, IDR_RELOAD_H, NULL }, 145 { 28, IDR_RELOAD_P, NULL }, 146 { 29, IDR_STOP, NULL }, 147 { 30, IDR_STOP_D, NULL }, 148 { 31, IDR_STOP_H, NULL }, 149 { 32, IDR_STOP_P, NULL }, 150 { 33, IDR_BROWSER_ACTIONS_OVERFLOW, NULL }, 151 { 34, IDR_BROWSER_ACTIONS_OVERFLOW_H, NULL }, 152 { 35, IDR_BROWSER_ACTIONS_OVERFLOW_P, NULL }, 153 { 36, IDR_TOOLS, NULL }, 154 { 37, IDR_TOOLS_H, NULL }, 155 { 38, IDR_TOOLS_P, NULL }, 156 { 39, IDR_MENU_DROPARROW, NULL }, 157 { 40, IDR_THROBBER, NULL }, 158 { 41, IDR_THROBBER_WAITING, NULL }, 159 { 42, IDR_THROBBER_LIGHT, NULL }, 160 { 43, IDR_TOOLBAR_BEZEL_HOVER, NULL }, 161 { 44, IDR_TOOLBAR_BEZEL_PRESSED, NULL }, 162 { 45, IDR_TOOLS_BAR, NULL }, 163}; 164const size_t kPersistingImagesLength = arraysize(kPersistingImages); 165 166#if defined(OS_WIN) && defined(USE_AURA) 167// Persistent theme ids for Windows AURA. 168const int PRS_THEME_FRAME_WIN = 100; 169const int PRS_THEME_FRAME_INACTIVE_WIN = 101; 170const int PRS_THEME_FRAME_INCOGNITO_WIN = 102; 171const int PRS_THEME_FRAME_INCOGNITO_INACTIVE_WIN = 103; 172const int PRS_THEME_TOOLBAR_WIN = 104; 173const int PRS_THEME_TAB_BACKGROUND_WIN = 105; 174const int PRS_THEME_TAB_BACKGROUND_INCOGNITO_WIN = 106; 175 176// Persistent theme to resource id mapping for Windows AURA. 177PersistingImagesTable kPersistingImagesWinDesktopAura[] = { 178 { PRS_THEME_FRAME_WIN, IDR_THEME_FRAME_WIN, 179 "theme_frame" }, 180 { PRS_THEME_FRAME_INACTIVE_WIN, IDR_THEME_FRAME_INACTIVE_WIN, 181 "theme_frame_inactive" }, 182 { PRS_THEME_FRAME_INCOGNITO_WIN, IDR_THEME_FRAME_INCOGNITO_WIN, 183 "theme_frame_incognito" }, 184 { PRS_THEME_FRAME_INCOGNITO_INACTIVE_WIN, 185 IDR_THEME_FRAME_INCOGNITO_INACTIVE_WIN, 186 "theme_frame_incognito_inactive" }, 187 { PRS_THEME_TOOLBAR_WIN, IDR_THEME_TOOLBAR_WIN, 188 "theme_toolbar" }, 189 { PRS_THEME_TAB_BACKGROUND_WIN, IDR_THEME_TAB_BACKGROUND_WIN, 190 "theme_tab_background" }, 191 { PRS_THEME_TAB_BACKGROUND_INCOGNITO_WIN, 192 IDR_THEME_TAB_BACKGROUND_INCOGNITO_WIN, 193 "theme_tab_background_incognito" }, 194}; 195const size_t kPersistingImagesWinDesktopAuraLength = 196 arraysize(kPersistingImagesWinDesktopAura); 197#endif 198 199int GetPersistentIDByNameHelper(const std::string& key, 200 const PersistingImagesTable* image_table, 201 size_t image_table_size) { 202 for (size_t i = 0; i < image_table_size; ++i) { 203 if (image_table[i].key != NULL && 204 base::strcasecmp(key.c_str(), image_table[i].key) == 0) { 205 return image_table[i].persistent_id; 206 } 207 } 208 return -1; 209} 210 211int GetPersistentIDByName(const std::string& key) { 212 return GetPersistentIDByNameHelper(key, 213 kPersistingImages, 214 kPersistingImagesLength); 215} 216 217int GetPersistentIDByIDR(int idr) { 218 static std::map<int,int>* lookup_table = new std::map<int,int>(); 219 if (lookup_table->empty()) { 220 for (size_t i = 0; i < kPersistingImagesLength; ++i) { 221 int idr = kPersistingImages[i].idr_id; 222 int prs_id = kPersistingImages[i].persistent_id; 223 (*lookup_table)[idr] = prs_id; 224 } 225#if defined(OS_WIN) && defined(USE_AURA) 226 for (size_t i = 0; i < kPersistingImagesWinDesktopAuraLength; ++i) { 227 int idr = kPersistingImagesWinDesktopAura[i].idr_id; 228 int prs_id = kPersistingImagesWinDesktopAura[i].persistent_id; 229 (*lookup_table)[idr] = prs_id; 230 } 231#endif 232 } 233 std::map<int,int>::iterator it = lookup_table->find(idr); 234 return (it == lookup_table->end()) ? -1 : it->second; 235} 236 237// Returns true if the scales in |input| match those in |expected|. 238// The order must match as the index is used in determining the raw id. 239bool InputScalesValid(const base::StringPiece& input, 240 const std::vector<ui::ScaleFactor>& expected) { 241 size_t scales_size = static_cast<size_t>(input.size() / sizeof(float)); 242 if (scales_size != expected.size()) 243 return false; 244 scoped_ptr<float[]> scales(new float[scales_size]); 245 // Do a memcpy to avoid misaligned memory access. 246 memcpy(scales.get(), input.data(), input.size()); 247 for (size_t index = 0; index < scales_size; ++index) { 248 if (scales[index] != ui::GetImageScale(expected[index])) 249 return false; 250 } 251 return true; 252} 253 254// Returns |scale_factors| as a string to be written to disk. 255std::string GetScaleFactorsAsString( 256 const std::vector<ui::ScaleFactor>& scale_factors) { 257 scoped_ptr<float[]> scales(new float[scale_factors.size()]); 258 for (size_t i = 0; i < scale_factors.size(); ++i) 259 scales[i] = ui::GetImageScale(scale_factors[i]); 260 std::string out_string = std::string( 261 reinterpret_cast<const char*>(scales.get()), 262 scale_factors.size() * sizeof(float)); 263 return out_string; 264} 265 266struct StringToIntTable { 267 const char* key; 268 ThemeProperties::OverwritableByUserThemeProperty id; 269}; 270 271// Strings used by themes to identify tints in the JSON. 272StringToIntTable kTintTable[] = { 273 { "buttons", ThemeProperties::TINT_BUTTONS }, 274 { "frame", ThemeProperties::TINT_FRAME }, 275 { "frame_inactive", ThemeProperties::TINT_FRAME_INACTIVE }, 276 { "frame_incognito", ThemeProperties::TINT_FRAME_INCOGNITO }, 277 { "frame_incognito_inactive", 278 ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE }, 279 { "background_tab", ThemeProperties::TINT_BACKGROUND_TAB }, 280}; 281const size_t kTintTableLength = arraysize(kTintTable); 282 283// Strings used by themes to identify colors in the JSON. 284StringToIntTable kColorTable[] = { 285 { "frame", ThemeProperties::COLOR_FRAME }, 286 { "frame_inactive", ThemeProperties::COLOR_FRAME_INACTIVE }, 287 { "frame_incognito", ThemeProperties::COLOR_FRAME_INCOGNITO }, 288 { "frame_incognito_inactive", 289 ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE }, 290 { "toolbar", ThemeProperties::COLOR_TOOLBAR }, 291 { "tab_text", ThemeProperties::COLOR_TAB_TEXT }, 292 { "tab_background_text", ThemeProperties::COLOR_BACKGROUND_TAB_TEXT }, 293 { "bookmark_text", ThemeProperties::COLOR_BOOKMARK_TEXT }, 294 { "ntp_background", ThemeProperties::COLOR_NTP_BACKGROUND }, 295 { "ntp_text", ThemeProperties::COLOR_NTP_TEXT }, 296 { "ntp_link", ThemeProperties::COLOR_NTP_LINK }, 297 { "ntp_link_underline", ThemeProperties::COLOR_NTP_LINK_UNDERLINE }, 298 { "ntp_header", ThemeProperties::COLOR_NTP_HEADER }, 299 { "ntp_section", ThemeProperties::COLOR_NTP_SECTION }, 300 { "ntp_section_text", ThemeProperties::COLOR_NTP_SECTION_TEXT }, 301 { "ntp_section_link", ThemeProperties::COLOR_NTP_SECTION_LINK }, 302 { "ntp_section_link_underline", 303 ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE }, 304 { "button_background", ThemeProperties::COLOR_BUTTON_BACKGROUND }, 305}; 306const size_t kColorTableLength = arraysize(kColorTable); 307 308// Strings used by themes to identify display properties keys in JSON. 309StringToIntTable kDisplayProperties[] = { 310 { "ntp_background_alignment", 311 ThemeProperties::NTP_BACKGROUND_ALIGNMENT }, 312 { "ntp_background_repeat", ThemeProperties::NTP_BACKGROUND_TILING }, 313 { "ntp_logo_alternate", ThemeProperties::NTP_LOGO_ALTERNATE }, 314}; 315const size_t kDisplayPropertiesSize = arraysize(kDisplayProperties); 316 317int GetIntForString(const std::string& key, 318 StringToIntTable* table, 319 size_t table_length) { 320 for (size_t i = 0; i < table_length; ++i) { 321 if (base::strcasecmp(key.c_str(), table[i].key) == 0) { 322 return table[i].id; 323 } 324 } 325 326 return -1; 327} 328 329struct IntToIntTable { 330 int key; 331 int value; 332}; 333 334// Mapping used in CreateFrameImages() to associate frame images with the 335// tint ID that should maybe be applied to it. 336IntToIntTable kFrameTintMap[] = { 337 { PRS_THEME_FRAME, ThemeProperties::TINT_FRAME }, 338 { PRS_THEME_FRAME_INACTIVE, ThemeProperties::TINT_FRAME_INACTIVE }, 339 { PRS_THEME_FRAME_OVERLAY, ThemeProperties::TINT_FRAME }, 340 { PRS_THEME_FRAME_OVERLAY_INACTIVE, 341 ThemeProperties::TINT_FRAME_INACTIVE }, 342 { PRS_THEME_FRAME_INCOGNITO, ThemeProperties::TINT_FRAME_INCOGNITO }, 343 { PRS_THEME_FRAME_INCOGNITO_INACTIVE, 344 ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE }, 345#if defined(OS_WIN) && defined(USE_AURA) 346 { PRS_THEME_FRAME_WIN, ThemeProperties::TINT_FRAME }, 347 { PRS_THEME_FRAME_INACTIVE_WIN, ThemeProperties::TINT_FRAME_INACTIVE }, 348 { PRS_THEME_FRAME_INCOGNITO_WIN, ThemeProperties::TINT_FRAME_INCOGNITO }, 349 { PRS_THEME_FRAME_INCOGNITO_INACTIVE_WIN, 350 ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE }, 351#endif 352}; 353 354// Mapping used in GenerateTabBackgroundImages() to associate what frame image 355// goes with which tab background. 356IntToIntTable kTabBackgroundMap[] = { 357 { PRS_THEME_TAB_BACKGROUND, PRS_THEME_FRAME }, 358 { PRS_THEME_TAB_BACKGROUND_INCOGNITO, PRS_THEME_FRAME_INCOGNITO }, 359#if defined(OS_WIN) && defined(USE_AURA) 360 { PRS_THEME_TAB_BACKGROUND_WIN, PRS_THEME_FRAME_WIN }, 361 { PRS_THEME_TAB_BACKGROUND_INCOGNITO_WIN, PRS_THEME_FRAME_INCOGNITO_WIN }, 362#endif 363}; 364 365struct CropEntry { 366 int prs_id; 367 368 // The maximum useful height of the image at |prs_id|. 369 int max_height; 370 371 // Whether cropping the image at |prs_id| should be skipped on OSes which 372 // have a frame border to the left and right of the web contents. 373 // This should be true for images which can be used to decorate the border to 374 // the left and the right of the web contents. 375 bool skip_if_frame_border; 376}; 377 378// The images which should be cropped before being saved to the data pack. The 379// maximum heights are meant to be conservative as to give room for the UI to 380// change without the maximum heights having to be modified. 381// |kThemePackVersion| must be incremented if any of the maximum heights below 382// are modified. 383struct CropEntry kImagesToCrop[] = { 384 { PRS_THEME_FRAME, 120, true }, 385 { PRS_THEME_FRAME_INACTIVE, 120, true }, 386 { PRS_THEME_FRAME_INCOGNITO, 120, true }, 387 { PRS_THEME_FRAME_INCOGNITO_INACTIVE, 120, true }, 388 { PRS_THEME_FRAME_OVERLAY, 120, true }, 389 { PRS_THEME_FRAME_OVERLAY_INACTIVE, 120, true }, 390 { PRS_THEME_TOOLBAR, 200, false }, 391 { PRS_THEME_BUTTON_BACKGROUND, 60, false }, 392 { PRS_THEME_WINDOW_CONTROL_BACKGROUND, 50, false }, 393#if defined(OS_WIN) && defined(USE_AURA) 394 { PRS_THEME_TOOLBAR_WIN, 200, false } 395#endif 396}; 397 398 399// A list of images that don't need tinting or any other modification and can 400// be byte-copied directly into the finished DataPack. This should contain the 401// persistent IDs for all themeable image IDs that aren't in kFrameTintMap, 402// kTabBackgroundMap or kImagesToCrop. 403const int kPreloadIDs[] = { 404 PRS_THEME_NTP_BACKGROUND, 405 PRS_THEME_NTP_ATTRIBUTION, 406}; 407 408// Returns true if this OS uses a browser frame which has a non zero width to 409// the left and the right of the web contents. 410bool HasFrameBorder() { 411#if defined(OS_CHROMEOS) || defined(OS_MACOSX) 412 return false; 413#else 414 return true; 415#endif 416} 417 418// Returns a piece of memory with the contents of the file |path|. 419base::RefCountedMemory* ReadFileData(const base::FilePath& path) { 420 if (!path.empty()) { 421 net::FileStream file(NULL); 422 int flags = base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ; 423 if (file.OpenSync(path, flags) == net::OK) { 424 int64 avail = file.Available(); 425 if (avail > 0 && avail < INT_MAX) { 426 size_t size = static_cast<size_t>(avail); 427 std::vector<unsigned char> raw_data; 428 raw_data.resize(size); 429 char* data = reinterpret_cast<char*>(&(raw_data.front())); 430 if (file.ReadUntilComplete(data, size) == avail) 431 return base::RefCountedBytes::TakeVector(&raw_data); 432 } 433 } 434 } 435 436 return NULL; 437} 438 439// Shifts an image's HSL values. The caller is responsible for deleting 440// the returned image. 441gfx::Image CreateHSLShiftedImage(const gfx::Image& image, 442 const color_utils::HSL& hsl_shift) { 443 const gfx::ImageSkia* src_image = image.ToImageSkia(); 444 return gfx::Image(gfx::ImageSkiaOperations::CreateHSLShiftedImage( 445 *src_image, hsl_shift)); 446} 447 448// Computes a bitmap at one scale from a bitmap at a different scale. 449SkBitmap CreateLowQualityResizedBitmap(const SkBitmap& source_bitmap, 450 ui::ScaleFactor source_scale_factor, 451 ui::ScaleFactor desired_scale_factor) { 452 gfx::Size scaled_size = gfx::ToCeiledSize( 453 gfx::ScaleSize(gfx::Size(source_bitmap.width(), 454 source_bitmap.height()), 455 ui::GetImageScale(desired_scale_factor) / 456 ui::GetImageScale(source_scale_factor))); 457 SkBitmap scaled_bitmap; 458 scaled_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 459 scaled_size.width(), 460 scaled_size.height()); 461 if (!scaled_bitmap.allocPixels()) 462 SK_CRASH(); 463 scaled_bitmap.eraseARGB(0, 0, 0, 0); 464 SkCanvas canvas(scaled_bitmap); 465 SkRect scaled_bounds = RectToSkRect(gfx::Rect(scaled_size)); 466 // Note(oshima): The following scaling code doesn't work with 467 // a mask image. 468 canvas.drawBitmapRect(source_bitmap, NULL, scaled_bounds); 469 return scaled_bitmap; 470} 471 472// A ImageSkiaSource that scales 100P image to the target scale factor 473// if the ImageSkiaRep for the target scale factor isn't available. 474class ThemeImageSource: public gfx::ImageSkiaSource { 475 public: 476 explicit ThemeImageSource(const gfx::ImageSkia& source) : source_(source) { 477 } 478 virtual ~ThemeImageSource() {} 479 480 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 481 if (source_.HasRepresentation(scale)) 482 return source_.GetRepresentation(scale); 483 const gfx::ImageSkiaRep& rep_100p = source_.GetRepresentation(1.0f); 484 SkBitmap scaled_bitmap = CreateLowQualityResizedBitmap( 485 rep_100p.sk_bitmap(), 486 ui::SCALE_FACTOR_100P, 487 ui::GetSupportedScaleFactor(scale)); 488 return gfx::ImageSkiaRep(scaled_bitmap, scale); 489 } 490 491 private: 492 const gfx::ImageSkia source_; 493 494 DISALLOW_COPY_AND_ASSIGN(ThemeImageSource); 495}; 496 497// An ImageSkiaSource that delays decoding PNG data into bitmaps until 498// needed. Missing data for a scale factor is computed by scaling data for an 499// available scale factor. Computed bitmaps are stored for future look up. 500class ThemeImagePngSource : public gfx::ImageSkiaSource { 501 public: 502 typedef std::map<ui::ScaleFactor, 503 scoped_refptr<base::RefCountedMemory> > PngMap; 504 505 explicit ThemeImagePngSource(const PngMap& png_map) : png_map_(png_map) {} 506 507 virtual ~ThemeImagePngSource() {} 508 509 private: 510 virtual gfx::ImageSkiaRep GetImageForScale(float scale) OVERRIDE { 511 ui::ScaleFactor scale_factor = ui::GetSupportedScaleFactor(scale); 512 // Look up the bitmap for |scale factor| in the bitmap map. If found 513 // return it. 514 BitmapMap::const_iterator exact_bitmap_it = bitmap_map_.find(scale_factor); 515 if (exact_bitmap_it != bitmap_map_.end()) 516 return gfx::ImageSkiaRep(exact_bitmap_it->second, scale); 517 518 // Look up the raw PNG data for |scale_factor| in the png map. If found, 519 // decode it, store the result in the bitmap map and return it. 520 PngMap::const_iterator exact_png_it = png_map_.find(scale_factor); 521 if (exact_png_it != png_map_.end()) { 522 SkBitmap bitmap; 523 if (!gfx::PNGCodec::Decode(exact_png_it->second->front(), 524 exact_png_it->second->size(), 525 &bitmap)) { 526 NOTREACHED(); 527 return gfx::ImageSkiaRep(); 528 } 529 bitmap_map_[scale_factor] = bitmap; 530 return gfx::ImageSkiaRep(bitmap, scale); 531 } 532 533 // Find an available PNG for another scale factor. We want to use the 534 // highest available scale factor. 535 PngMap::const_iterator available_png_it = png_map_.end(); 536 for (PngMap::const_iterator png_it = png_map_.begin(); 537 png_it != png_map_.end(); ++png_it) { 538 if (available_png_it == png_map_.end() || 539 ui::GetImageScale(png_it->first) > 540 ui::GetImageScale(available_png_it->first)) { 541 available_png_it = png_it; 542 } 543 } 544 if (available_png_it == png_map_.end()) 545 return gfx::ImageSkiaRep(); 546 ui::ScaleFactor available_scale_factor = available_png_it->first; 547 548 // Look up the bitmap for |available_scale_factor| in the bitmap map. 549 // If not found, decode the corresponging png data, store the result 550 // in the bitmap map. 551 BitmapMap::const_iterator available_bitmap_it = 552 bitmap_map_.find(available_scale_factor); 553 if (available_bitmap_it == bitmap_map_.end()) { 554 SkBitmap available_bitmap; 555 if (!gfx::PNGCodec::Decode(available_png_it->second->front(), 556 available_png_it->second->size(), 557 &available_bitmap)) { 558 NOTREACHED(); 559 return gfx::ImageSkiaRep(); 560 } 561 bitmap_map_[available_scale_factor] = available_bitmap; 562 available_bitmap_it = bitmap_map_.find(available_scale_factor); 563 } 564 565 // Scale the available bitmap to the desired scale factor, store the result 566 // in the bitmap map and return it. 567 SkBitmap scaled_bitmap = CreateLowQualityResizedBitmap( 568 available_bitmap_it->second, 569 available_scale_factor, 570 scale_factor); 571 bitmap_map_[scale_factor] = scaled_bitmap; 572 return gfx::ImageSkiaRep(scaled_bitmap, scale); 573 } 574 575 PngMap png_map_; 576 577 typedef std::map<ui::ScaleFactor, SkBitmap> BitmapMap; 578 BitmapMap bitmap_map_; 579 580 DISALLOW_COPY_AND_ASSIGN(ThemeImagePngSource); 581}; 582 583class TabBackgroundImageSource: public gfx::CanvasImageSource { 584 public: 585 TabBackgroundImageSource(const gfx::ImageSkia& image_to_tint, 586 const gfx::ImageSkia& overlay, 587 const color_utils::HSL& hsl_shift, 588 int vertical_offset) 589 : gfx::CanvasImageSource(image_to_tint.size(), false), 590 image_to_tint_(image_to_tint), 591 overlay_(overlay), 592 hsl_shift_(hsl_shift), 593 vertical_offset_(vertical_offset) { 594 } 595 596 virtual ~TabBackgroundImageSource() { 597 } 598 599 // Overridden from CanvasImageSource: 600 virtual void Draw(gfx::Canvas* canvas) OVERRIDE { 601 gfx::ImageSkia bg_tint = 602 gfx::ImageSkiaOperations::CreateHSLShiftedImage(image_to_tint_, 603 hsl_shift_); 604 canvas->TileImageInt(bg_tint, 0, vertical_offset_, 0, 0, 605 size().width(), size().height()); 606 607 // If they've provided a custom image, overlay it. 608 if (!overlay_.isNull()) { 609 canvas->TileImageInt(overlay_, 0, 0, size().width(), 610 overlay_.height()); 611 } 612 } 613 614 private: 615 const gfx::ImageSkia image_to_tint_; 616 const gfx::ImageSkia overlay_; 617 const color_utils::HSL hsl_shift_; 618 const int vertical_offset_; 619 620 DISALLOW_COPY_AND_ASSIGN(TabBackgroundImageSource); 621}; 622 623} // namespace 624 625BrowserThemePack::~BrowserThemePack() { 626 if (!data_pack_.get()) { 627 delete header_; 628 delete [] tints_; 629 delete [] colors_; 630 delete [] display_properties_; 631 delete [] source_images_; 632 } 633} 634 635// static 636scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromExtension( 637 const Extension* extension) { 638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 639 DCHECK(extension); 640 DCHECK(extension->is_theme()); 641 642 scoped_refptr<BrowserThemePack> pack(new BrowserThemePack); 643 pack->BuildHeader(extension); 644 pack->BuildTintsFromJSON(extensions::ThemeInfo::GetTints(extension)); 645 pack->BuildColorsFromJSON(extensions::ThemeInfo::GetColors(extension)); 646 pack->BuildDisplayPropertiesFromJSON( 647 extensions::ThemeInfo::GetDisplayProperties(extension)); 648 649 // Builds the images. (Image building is dependent on tints). 650 FilePathMap file_paths; 651 pack->ParseImageNamesFromJSON( 652 extensions::ThemeInfo::GetImages(extension), 653 extension->path(), 654 &file_paths); 655 pack->BuildSourceImagesArray(file_paths); 656 657 if (!pack->LoadRawBitmapsTo(file_paths, &pack->images_on_ui_thread_)) 658 return NULL; 659 660 pack->CreateImages(&pack->images_on_ui_thread_); 661 662 // Make sure the |images_on_file_thread_| has bitmaps for supported 663 // scale factors before passing to FILE thread. 664 pack->images_on_file_thread_ = pack->images_on_ui_thread_; 665 for (ImageCache::iterator it = pack->images_on_file_thread_.begin(); 666 it != pack->images_on_file_thread_.end(); ++it) { 667 gfx::ImageSkia* image_skia = 668 const_cast<gfx::ImageSkia*>(it->second.ToImageSkia()); 669 image_skia->MakeThreadSafe(); 670 } 671 672 // Set ThemeImageSource on |images_on_ui_thread_| to resample the source 673 // image if a caller of BrowserThemePack::GetImageNamed() requests an 674 // ImageSkiaRep for a scale factor not specified by the theme author. 675 // Callers of BrowserThemePack::GetImageNamed() to be able to retrieve 676 // ImageSkiaReps for all supported scale factors. 677 for (ImageCache::iterator it = pack->images_on_ui_thread_.begin(); 678 it != pack->images_on_ui_thread_.end(); ++it) { 679 const gfx::ImageSkia source_image_skia = it->second.AsImageSkia(); 680 ThemeImageSource* source = new ThemeImageSource(source_image_skia); 681 // image_skia takes ownership of source. 682 gfx::ImageSkia image_skia(source, source_image_skia.size()); 683 it->second = gfx::Image(image_skia); 684 } 685 686 // Generate raw images (for new-tab-page attribution and background) for 687 // any missing scale from an available scale image. 688 for (size_t i = 0; i < arraysize(kPreloadIDs); ++i) { 689 pack->GenerateRawImageForAllSupportedScales(kPreloadIDs[i]); 690 } 691 692 // The BrowserThemePack is now in a consistent state. 693 return pack; 694} 695 696// static 697scoped_refptr<BrowserThemePack> BrowserThemePack::BuildFromDataPack( 698 const base::FilePath& path, const std::string& expected_id) { 699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 700 // Allow IO on UI thread due to deep-seated theme design issues. 701 // (see http://crbug.com/80206) 702 base::ThreadRestrictions::ScopedAllowIO allow_io; 703 scoped_refptr<BrowserThemePack> pack(new BrowserThemePack); 704 // Scale factor parameter is moot as data pack has image resources for all 705 // supported scale factors. 706 pack->data_pack_.reset( 707 new ui::DataPack(ui::SCALE_FACTOR_NONE)); 708 709 if (!pack->data_pack_->LoadFromPath(path)) { 710 LOG(ERROR) << "Failed to load theme data pack."; 711 return NULL; 712 } 713 714 base::StringPiece pointer; 715 if (!pack->data_pack_->GetStringPiece(kHeaderID, &pointer)) 716 return NULL; 717 pack->header_ = reinterpret_cast<BrowserThemePackHeader*>(const_cast<char*>( 718 pointer.data())); 719 720 if (pack->header_->version != kThemePackVersion) { 721 DLOG(ERROR) << "BuildFromDataPack failure! Version mismatch!"; 722 return NULL; 723 } 724 // TODO(erg): Check endianess once DataPack works on the other endian. 725 std::string theme_id(reinterpret_cast<char*>(pack->header_->theme_id), 726 extensions::id_util::kIdSize); 727 std::string truncated_id = 728 expected_id.substr(0, extensions::id_util::kIdSize); 729 if (theme_id != truncated_id) { 730 DLOG(ERROR) << "Wrong id: " << theme_id << " vs " << expected_id; 731 return NULL; 732 } 733 734 if (!pack->data_pack_->GetStringPiece(kTintsID, &pointer)) 735 return NULL; 736 pack->tints_ = reinterpret_cast<TintEntry*>(const_cast<char*>( 737 pointer.data())); 738 739 if (!pack->data_pack_->GetStringPiece(kColorsID, &pointer)) 740 return NULL; 741 pack->colors_ = 742 reinterpret_cast<ColorPair*>(const_cast<char*>(pointer.data())); 743 744 if (!pack->data_pack_->GetStringPiece(kDisplayPropertiesID, &pointer)) 745 return NULL; 746 pack->display_properties_ = reinterpret_cast<DisplayPropertyPair*>( 747 const_cast<char*>(pointer.data())); 748 749 if (!pack->data_pack_->GetStringPiece(kSourceImagesID, &pointer)) 750 return NULL; 751 pack->source_images_ = reinterpret_cast<int*>( 752 const_cast<char*>(pointer.data())); 753 754 if (!pack->data_pack_->GetStringPiece(kScaleFactorsID, &pointer)) 755 return NULL; 756 757 if (!InputScalesValid(pointer, pack->scale_factors_)) { 758 DLOG(ERROR) << "BuildFromDataPack failure! The pack scale factors differ " 759 << "from those supported by platform."; 760 } 761 return pack; 762} 763 764// static 765void BrowserThemePack::GetThemeableImageIDRs(std::set<int>* result) { 766 if (!result) 767 return; 768 769 result->clear(); 770 for (size_t i = 0; i < kPersistingImagesLength; ++i) 771 result->insert(kPersistingImages[i].idr_id); 772 773#if defined(OS_WIN) && defined(USE_AURA) 774 for (size_t i = 0; i < kPersistingImagesWinDesktopAuraLength; ++i) 775 result->insert(kPersistingImagesWinDesktopAura[i].idr_id); 776#endif 777} 778 779bool BrowserThemePack::WriteToDisk(const base::FilePath& path) const { 780 // Add resources for each of the property arrays. 781 RawDataForWriting resources; 782 resources[kHeaderID] = base::StringPiece( 783 reinterpret_cast<const char*>(header_), sizeof(BrowserThemePackHeader)); 784 resources[kTintsID] = base::StringPiece( 785 reinterpret_cast<const char*>(tints_), 786 sizeof(TintEntry[kTintTableLength])); 787 resources[kColorsID] = base::StringPiece( 788 reinterpret_cast<const char*>(colors_), 789 sizeof(ColorPair[kColorTableLength])); 790 resources[kDisplayPropertiesID] = base::StringPiece( 791 reinterpret_cast<const char*>(display_properties_), 792 sizeof(DisplayPropertyPair[kDisplayPropertiesSize])); 793 794 int source_count = 1; 795 int* end = source_images_; 796 for (; *end != -1 ; end++) 797 source_count++; 798 resources[kSourceImagesID] = base::StringPiece( 799 reinterpret_cast<const char*>(source_images_), 800 source_count * sizeof(*source_images_)); 801 802 // Store results of GetScaleFactorsAsString() in std::string as 803 // base::StringPiece does not copy data in constructor. 804 std::string scale_factors_string = GetScaleFactorsAsString(scale_factors_); 805 resources[kScaleFactorsID] = scale_factors_string; 806 807 AddRawImagesTo(image_memory_, &resources); 808 809 RawImages reencoded_images; 810 RepackImages(images_on_file_thread_, &reencoded_images); 811 AddRawImagesTo(reencoded_images, &resources); 812 813 return ui::DataPack::WritePack(path, resources, ui::DataPack::BINARY); 814} 815 816bool BrowserThemePack::GetTint(int id, color_utils::HSL* hsl) const { 817 if (tints_) { 818 for (size_t i = 0; i < kTintTableLength; ++i) { 819 if (tints_[i].id == id) { 820 hsl->h = tints_[i].h; 821 hsl->s = tints_[i].s; 822 hsl->l = tints_[i].l; 823 return true; 824 } 825 } 826 } 827 828 return false; 829} 830 831bool BrowserThemePack::GetColor(int id, SkColor* color) const { 832 if (colors_) { 833 for (size_t i = 0; i < kColorTableLength; ++i) { 834 if (colors_[i].id == id) { 835 *color = colors_[i].color; 836 return true; 837 } 838 } 839 } 840 841 return false; 842} 843 844bool BrowserThemePack::GetDisplayProperty(int id, int* result) const { 845 if (display_properties_) { 846 for (size_t i = 0; i < kDisplayPropertiesSize; ++i) { 847 if (display_properties_[i].id == id) { 848 *result = display_properties_[i].property; 849 return true; 850 } 851 } 852 } 853 854 return false; 855} 856 857gfx::Image BrowserThemePack::GetImageNamed(int idr_id) { 858 int prs_id = GetPersistentIDByIDR(idr_id); 859 if (prs_id == -1) 860 return gfx::Image(); 861 862 // Check if the image is cached. 863 ImageCache::const_iterator image_iter = images_on_ui_thread_.find(prs_id); 864 if (image_iter != images_on_ui_thread_.end()) 865 return image_iter->second; 866 867 ThemeImagePngSource::PngMap png_map; 868 for (size_t i = 0; i < scale_factors_.size(); ++i) { 869 scoped_refptr<base::RefCountedMemory> memory = 870 GetRawData(idr_id, scale_factors_[i]); 871 if (memory.get()) 872 png_map[scale_factors_[i]] = memory; 873 } 874 if (!png_map.empty()) { 875 gfx::ImageSkia image_skia(new ThemeImagePngSource(png_map), 876 ui::SCALE_FACTOR_100P); 877 // |image_skia| takes ownership of ThemeImagePngSource. 878 gfx::Image ret = gfx::Image(image_skia); 879 images_on_ui_thread_[prs_id] = ret; 880 return ret; 881 } 882 883 return gfx::Image(); 884} 885 886base::RefCountedMemory* BrowserThemePack::GetRawData( 887 int idr_id, 888 ui::ScaleFactor scale_factor) const { 889 base::RefCountedMemory* memory = NULL; 890 int prs_id = GetPersistentIDByIDR(idr_id); 891 int raw_id = GetRawIDByPersistentID(prs_id, scale_factor); 892 893 if (raw_id != -1) { 894 if (data_pack_.get()) { 895 memory = data_pack_->GetStaticMemory(raw_id); 896 } else { 897 RawImages::const_iterator it = image_memory_.find(raw_id); 898 if (it != image_memory_.end()) { 899 memory = it->second.get(); 900 } 901 } 902 } 903 904 return memory; 905} 906 907bool BrowserThemePack::HasCustomImage(int idr_id) const { 908 int prs_id = GetPersistentIDByIDR(idr_id); 909 if (prs_id == -1) 910 return false; 911 912 int* img = source_images_; 913 for (; *img != -1; ++img) { 914 if (*img == prs_id) 915 return true; 916 } 917 918 return false; 919} 920 921// private: 922 923BrowserThemePack::BrowserThemePack() 924 : CustomThemeSupplier(EXTENSION), 925 header_(NULL), 926 tints_(NULL), 927 colors_(NULL), 928 display_properties_(NULL), 929 source_images_(NULL) { 930 scale_factors_ = ui::GetSupportedScaleFactors(); 931} 932 933void BrowserThemePack::BuildHeader(const Extension* extension) { 934 header_ = new BrowserThemePackHeader; 935 header_->version = kThemePackVersion; 936 937 // TODO(erg): Need to make this endian safe on other computers. Prerequisite 938 // is that ui::DataPack removes this same check. 939#if defined(__BYTE_ORDER) 940 // Linux check 941 COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, 942 datapack_assumes_little_endian); 943#elif defined(__BIG_ENDIAN__) 944 // Mac check 945 #error DataPack assumes little endian 946#endif 947 header_->little_endian = 1; 948 949 const std::string& id = extension->id(); 950 memcpy(header_->theme_id, id.c_str(), extensions::id_util::kIdSize); 951} 952 953void BrowserThemePack::BuildTintsFromJSON( 954 const base::DictionaryValue* tints_value) { 955 tints_ = new TintEntry[kTintTableLength]; 956 for (size_t i = 0; i < kTintTableLength; ++i) { 957 tints_[i].id = -1; 958 tints_[i].h = -1; 959 tints_[i].s = -1; 960 tints_[i].l = -1; 961 } 962 963 if (!tints_value) 964 return; 965 966 // Parse the incoming data from |tints_value| into an intermediary structure. 967 std::map<int, color_utils::HSL> temp_tints; 968 for (DictionaryValue::Iterator iter(*tints_value); !iter.IsAtEnd(); 969 iter.Advance()) { 970 const ListValue* tint_list; 971 if (iter.value().GetAsList(&tint_list) && 972 (tint_list->GetSize() == 3)) { 973 color_utils::HSL hsl = { -1, -1, -1 }; 974 975 if (tint_list->GetDouble(0, &hsl.h) && 976 tint_list->GetDouble(1, &hsl.s) && 977 tint_list->GetDouble(2, &hsl.l)) { 978 int id = GetIntForString(iter.key(), kTintTable, kTintTableLength); 979 if (id != -1) { 980 temp_tints[id] = hsl; 981 } 982 } 983 } 984 } 985 986 // Copy data from the intermediary data structure to the array. 987 size_t count = 0; 988 for (std::map<int, color_utils::HSL>::const_iterator it = 989 temp_tints.begin(); 990 it != temp_tints.end() && count < kTintTableLength; 991 ++it, ++count) { 992 tints_[count].id = it->first; 993 tints_[count].h = it->second.h; 994 tints_[count].s = it->second.s; 995 tints_[count].l = it->second.l; 996 } 997} 998 999void BrowserThemePack::BuildColorsFromJSON( 1000 const base::DictionaryValue* colors_value) { 1001 colors_ = new ColorPair[kColorTableLength]; 1002 for (size_t i = 0; i < kColorTableLength; ++i) { 1003 colors_[i].id = -1; 1004 colors_[i].color = SkColorSetRGB(0, 0, 0); 1005 } 1006 1007 std::map<int, SkColor> temp_colors; 1008 if (colors_value) 1009 ReadColorsFromJSON(colors_value, &temp_colors); 1010 GenerateMissingColors(&temp_colors); 1011 1012 // Copy data from the intermediary data structure to the array. 1013 size_t count = 0; 1014 for (std::map<int, SkColor>::const_iterator it = temp_colors.begin(); 1015 it != temp_colors.end() && count < kColorTableLength; ++it, ++count) { 1016 colors_[count].id = it->first; 1017 colors_[count].color = it->second; 1018 } 1019} 1020 1021void BrowserThemePack::ReadColorsFromJSON( 1022 const base::DictionaryValue* colors_value, 1023 std::map<int, SkColor>* temp_colors) { 1024 // Parse the incoming data from |colors_value| into an intermediary structure. 1025 for (DictionaryValue::Iterator iter(*colors_value); !iter.IsAtEnd(); 1026 iter.Advance()) { 1027 const ListValue* color_list; 1028 if (iter.value().GetAsList(&color_list) && 1029 ((color_list->GetSize() == 3) || (color_list->GetSize() == 4))) { 1030 SkColor color = SK_ColorWHITE; 1031 int r, g, b; 1032 if (color_list->GetInteger(0, &r) && 1033 color_list->GetInteger(1, &g) && 1034 color_list->GetInteger(2, &b)) { 1035 if (color_list->GetSize() == 4) { 1036 double alpha; 1037 int alpha_int; 1038 if (color_list->GetDouble(3, &alpha)) { 1039 color = SkColorSetARGB(static_cast<int>(alpha * 255), r, g, b); 1040 } else if (color_list->GetInteger(3, &alpha_int) && 1041 (alpha_int == 0 || alpha_int == 1)) { 1042 color = SkColorSetARGB(alpha_int ? 255 : 0, r, g, b); 1043 } else { 1044 // Invalid entry for part 4. 1045 continue; 1046 } 1047 } else { 1048 color = SkColorSetRGB(r, g, b); 1049 } 1050 1051 int id = GetIntForString(iter.key(), kColorTable, kColorTableLength); 1052 if (id != -1) { 1053 (*temp_colors)[id] = color; 1054 } 1055 } 1056 } 1057 } 1058} 1059 1060void BrowserThemePack::GenerateMissingColors( 1061 std::map<int, SkColor>* colors) { 1062 // Generate link colors, if missing. (See GetColor()). 1063 if (!colors->count(ThemeProperties::COLOR_NTP_HEADER) && 1064 colors->count(ThemeProperties::COLOR_NTP_SECTION)) { 1065 (*colors)[ThemeProperties::COLOR_NTP_HEADER] = 1066 (*colors)[ThemeProperties::COLOR_NTP_SECTION]; 1067 } 1068 1069 if (!colors->count(ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE) && 1070 colors->count(ThemeProperties::COLOR_NTP_SECTION_LINK)) { 1071 SkColor color_section_link = 1072 (*colors)[ThemeProperties::COLOR_NTP_SECTION_LINK]; 1073 (*colors)[ThemeProperties::COLOR_NTP_SECTION_LINK_UNDERLINE] = 1074 SkColorSetA(color_section_link, SkColorGetA(color_section_link) / 3); 1075 } 1076 1077 if (!colors->count(ThemeProperties::COLOR_NTP_LINK_UNDERLINE) && 1078 colors->count(ThemeProperties::COLOR_NTP_LINK)) { 1079 SkColor color_link = (*colors)[ThemeProperties::COLOR_NTP_LINK]; 1080 (*colors)[ThemeProperties::COLOR_NTP_LINK_UNDERLINE] = 1081 SkColorSetA(color_link, SkColorGetA(color_link) / 3); 1082 } 1083 1084 // Generate frame colors, if missing. (See GenerateFrameColors()). 1085 SkColor frame; 1086 std::map<int, SkColor>::const_iterator it = 1087 colors->find(ThemeProperties::COLOR_FRAME); 1088 if (it != colors->end()) { 1089 frame = it->second; 1090 } else { 1091 frame = ThemeProperties::GetDefaultColor( 1092 ThemeProperties::COLOR_FRAME); 1093 } 1094 1095 if (!colors->count(ThemeProperties::COLOR_FRAME)) { 1096 (*colors)[ThemeProperties::COLOR_FRAME] = 1097 HSLShift(frame, GetTintInternal(ThemeProperties::TINT_FRAME)); 1098 } 1099 if (!colors->count(ThemeProperties::COLOR_FRAME_INACTIVE)) { 1100 (*colors)[ThemeProperties::COLOR_FRAME_INACTIVE] = 1101 HSLShift(frame, GetTintInternal( 1102 ThemeProperties::TINT_FRAME_INACTIVE)); 1103 } 1104 if (!colors->count(ThemeProperties::COLOR_FRAME_INCOGNITO)) { 1105 (*colors)[ThemeProperties::COLOR_FRAME_INCOGNITO] = 1106 HSLShift(frame, GetTintInternal( 1107 ThemeProperties::TINT_FRAME_INCOGNITO)); 1108 } 1109 if (!colors->count(ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE)) { 1110 (*colors)[ThemeProperties::COLOR_FRAME_INCOGNITO_INACTIVE] = 1111 HSLShift(frame, GetTintInternal( 1112 ThemeProperties::TINT_FRAME_INCOGNITO_INACTIVE)); 1113 } 1114} 1115 1116void BrowserThemePack::BuildDisplayPropertiesFromJSON( 1117 const base::DictionaryValue* display_properties_value) { 1118 display_properties_ = new DisplayPropertyPair[kDisplayPropertiesSize]; 1119 for (size_t i = 0; i < kDisplayPropertiesSize; ++i) { 1120 display_properties_[i].id = -1; 1121 display_properties_[i].property = 0; 1122 } 1123 1124 if (!display_properties_value) 1125 return; 1126 1127 std::map<int, int> temp_properties; 1128 for (DictionaryValue::Iterator iter(*display_properties_value); 1129 !iter.IsAtEnd(); iter.Advance()) { 1130 int property_id = GetIntForString(iter.key(), kDisplayProperties, 1131 kDisplayPropertiesSize); 1132 switch (property_id) { 1133 case ThemeProperties::NTP_BACKGROUND_ALIGNMENT: { 1134 std::string val; 1135 if (iter.value().GetAsString(&val)) { 1136 temp_properties[ThemeProperties::NTP_BACKGROUND_ALIGNMENT] = 1137 ThemeProperties::StringToAlignment(val); 1138 } 1139 break; 1140 } 1141 case ThemeProperties::NTP_BACKGROUND_TILING: { 1142 std::string val; 1143 if (iter.value().GetAsString(&val)) { 1144 temp_properties[ThemeProperties::NTP_BACKGROUND_TILING] = 1145 ThemeProperties::StringToTiling(val); 1146 } 1147 break; 1148 } 1149 case ThemeProperties::NTP_LOGO_ALTERNATE: { 1150 int val = 0; 1151 if (iter.value().GetAsInteger(&val)) 1152 temp_properties[ThemeProperties::NTP_LOGO_ALTERNATE] = val; 1153 break; 1154 } 1155 } 1156 } 1157 1158 // Copy data from the intermediary data structure to the array. 1159 size_t count = 0; 1160 for (std::map<int, int>::const_iterator it = temp_properties.begin(); 1161 it != temp_properties.end() && count < kDisplayPropertiesSize; 1162 ++it, ++count) { 1163 display_properties_[count].id = it->first; 1164 display_properties_[count].property = it->second; 1165 } 1166} 1167 1168void BrowserThemePack::ParseImageNamesFromJSON( 1169 const base::DictionaryValue* images_value, 1170 const base::FilePath& images_path, 1171 FilePathMap* file_paths) const { 1172 if (!images_value) 1173 return; 1174 1175 for (DictionaryValue::Iterator iter(*images_value); !iter.IsAtEnd(); 1176 iter.Advance()) { 1177 if (iter.value().IsType(Value::TYPE_DICTIONARY)) { 1178 const DictionaryValue* inner_value = NULL; 1179 if (iter.value().GetAsDictionary(&inner_value)) { 1180 for (DictionaryValue::Iterator inner_iter(*inner_value); 1181 !inner_iter.IsAtEnd(); 1182 inner_iter.Advance()) { 1183 std::string name; 1184 ui::ScaleFactor scale_factor = ui::SCALE_FACTOR_NONE; 1185 if (GetScaleFactorFromManifestKey(inner_iter.key(), &scale_factor) && 1186 inner_iter.value().IsType(Value::TYPE_STRING) && 1187 inner_iter.value().GetAsString(&name)) { 1188 AddFileAtScaleToMap(iter.key(), 1189 scale_factor, 1190 images_path.AppendASCII(name), 1191 file_paths); 1192 } 1193 } 1194 } 1195 } else if (iter.value().IsType(Value::TYPE_STRING)) { 1196 std::string name; 1197 if (iter.value().GetAsString(&name)) { 1198 AddFileAtScaleToMap(iter.key(), 1199 ui::SCALE_FACTOR_100P, 1200 images_path.AppendASCII(name), 1201 file_paths); 1202 } 1203 } 1204 } 1205} 1206 1207void BrowserThemePack::AddFileAtScaleToMap(const std::string& image_name, 1208 ui::ScaleFactor scale_factor, 1209 const base::FilePath& image_path, 1210 FilePathMap* file_paths) const { 1211 int id = GetPersistentIDByName(image_name); 1212 if (id != -1) 1213 (*file_paths)[id][scale_factor] = image_path; 1214#if defined(OS_WIN) && defined(USE_AURA) 1215 id = GetPersistentIDByNameHelper(image_name, 1216 kPersistingImagesWinDesktopAura, 1217 kPersistingImagesWinDesktopAuraLength); 1218 if (id != -1) 1219 (*file_paths)[id][scale_factor] = image_path; 1220#endif 1221} 1222 1223void BrowserThemePack::BuildSourceImagesArray(const FilePathMap& file_paths) { 1224 std::vector<int> ids; 1225 for (FilePathMap::const_iterator it = file_paths.begin(); 1226 it != file_paths.end(); ++it) { 1227 ids.push_back(it->first); 1228 } 1229 1230 source_images_ = new int[ids.size() + 1]; 1231 std::copy(ids.begin(), ids.end(), source_images_); 1232 source_images_[ids.size()] = -1; 1233} 1234 1235bool BrowserThemePack::LoadRawBitmapsTo( 1236 const FilePathMap& file_paths, 1237 ImageCache* image_cache) { 1238 // Themes should be loaded on the file thread, not the UI thread. 1239 // http://crbug.com/61838 1240 base::ThreadRestrictions::ScopedAllowIO allow_io; 1241 1242 for (FilePathMap::const_iterator it = file_paths.begin(); 1243 it != file_paths.end(); ++it) { 1244 int prs_id = it->first; 1245 // Some images need to go directly into |image_memory_|. No modification is 1246 // necessary or desirable. 1247 bool is_copyable = false; 1248 for (size_t i = 0; i < arraysize(kPreloadIDs); ++i) { 1249 if (kPreloadIDs[i] == prs_id) { 1250 is_copyable = true; 1251 break; 1252 } 1253 } 1254 gfx::ImageSkia image_skia; 1255 for (int pass = 0; pass < 2; ++pass) { 1256 // Two passes: In the first pass, we process only scale factor 1257 // 100% and in the second pass all other scale factors. We 1258 // process scale factor 100% first because the first image added 1259 // in image_skia.AddRepresentation() determines the DIP size for 1260 // all representations. 1261 for (ScaleFactorToFileMap::const_iterator s2f = it->second.begin(); 1262 s2f != it->second.end(); ++s2f) { 1263 ui::ScaleFactor scale_factor = s2f->first; 1264 if ((pass == 0 && scale_factor != ui::SCALE_FACTOR_100P) || 1265 (pass == 1 && scale_factor == ui::SCALE_FACTOR_100P)) { 1266 continue; 1267 } 1268 scoped_refptr<base::RefCountedMemory> raw_data( 1269 ReadFileData(s2f->second)); 1270 if (!raw_data.get() || !raw_data->size()) { 1271 LOG(ERROR) << "Could not load theme image" 1272 << " prs_id=" << prs_id 1273 << " scale_factor_enum=" << scale_factor 1274 << " file=" << s2f->second.value() 1275 << (raw_data.get() ? " (zero size)" : " (read error)"); 1276 return false; 1277 } 1278 if (is_copyable) { 1279 int raw_id = GetRawIDByPersistentID(prs_id, scale_factor); 1280 image_memory_[raw_id] = raw_data; 1281 } else { 1282 SkBitmap bitmap; 1283 if (gfx::PNGCodec::Decode(raw_data->front(), raw_data->size(), 1284 &bitmap)) { 1285 image_skia.AddRepresentation( 1286 gfx::ImageSkiaRep(bitmap, 1287 ui::GetImageScale(scale_factor))); 1288 } else { 1289 NOTREACHED() << "Unable to decode theme image resource " 1290 << it->first; 1291 } 1292 } 1293 } 1294 } 1295 if (!is_copyable && !image_skia.isNull()) 1296 (*image_cache)[prs_id] = gfx::Image(image_skia); 1297 } 1298 1299 return true; 1300} 1301 1302void BrowserThemePack::CreateImages(ImageCache* images) const { 1303 CropImages(images); 1304 CreateFrameImages(images); 1305 CreateTintedButtons(GetTintInternal(ThemeProperties::TINT_BUTTONS), images); 1306 CreateTabBackgroundImages(images); 1307} 1308 1309void BrowserThemePack::CropImages(ImageCache* images) const { 1310 bool has_frame_border = HasFrameBorder(); 1311 for (size_t i = 0; i < arraysize(kImagesToCrop); ++i) { 1312 if (has_frame_border && kImagesToCrop[i].skip_if_frame_border) 1313 continue; 1314 1315 int prs_id = kImagesToCrop[i].prs_id; 1316 ImageCache::iterator it = images->find(prs_id); 1317 if (it == images->end()) 1318 continue; 1319 1320 int crop_height = kImagesToCrop[i].max_height; 1321 gfx::ImageSkia image_skia = it->second.AsImageSkia(); 1322 (*images)[prs_id] = gfx::Image(gfx::ImageSkiaOperations::ExtractSubset( 1323 image_skia, gfx::Rect(0, 0, image_skia.width(), crop_height))); 1324 } 1325} 1326 1327void BrowserThemePack::CreateFrameImages(ImageCache* images) const { 1328 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 1329 1330 // Create all the output images in a separate cache and move them back into 1331 // the input images because there can be name collisions. 1332 ImageCache temp_output; 1333 1334 for (size_t i = 0; i < arraysize(kFrameTintMap); ++i) { 1335 int prs_id = kFrameTintMap[i].key; 1336 gfx::Image frame; 1337 // If there's no frame image provided for the specified id, then load 1338 // the default provided frame. If that's not provided, skip this whole 1339 // thing and just use the default images. 1340 int prs_base_id = 0; 1341 1342#if defined(OS_WIN) && defined(USE_AURA) 1343 if (prs_id == PRS_THEME_FRAME_INCOGNITO_INACTIVE_WIN) { 1344 prs_base_id = images->count(PRS_THEME_FRAME_INCOGNITO_WIN) ? 1345 PRS_THEME_FRAME_INCOGNITO_WIN : PRS_THEME_FRAME_WIN; 1346 } else if (prs_id == PRS_THEME_FRAME_INACTIVE_WIN) { 1347 prs_base_id = PRS_THEME_FRAME_WIN; 1348 } else if (prs_id == PRS_THEME_FRAME_INCOGNITO_WIN && 1349 !images->count(PRS_THEME_FRAME_INCOGNITO_WIN)) { 1350 prs_base_id = PRS_THEME_FRAME_WIN; 1351 } 1352#endif 1353 if (!prs_base_id) { 1354 if (prs_id == PRS_THEME_FRAME_INCOGNITO_INACTIVE) { 1355 prs_base_id = images->count(PRS_THEME_FRAME_INCOGNITO) ? 1356 PRS_THEME_FRAME_INCOGNITO : PRS_THEME_FRAME; 1357 } else if (prs_id == PRS_THEME_FRAME_OVERLAY_INACTIVE) { 1358 prs_base_id = PRS_THEME_FRAME_OVERLAY; 1359 } else if (prs_id == PRS_THEME_FRAME_INACTIVE) { 1360 prs_base_id = PRS_THEME_FRAME; 1361 } else if (prs_id == PRS_THEME_FRAME_INCOGNITO && 1362 !images->count(PRS_THEME_FRAME_INCOGNITO)) { 1363 prs_base_id = PRS_THEME_FRAME; 1364 } else { 1365 prs_base_id = prs_id; 1366 } 1367 } 1368 if (images->count(prs_id)) { 1369 frame = (*images)[prs_id]; 1370 } else if (prs_base_id != prs_id && images->count(prs_base_id)) { 1371 frame = (*images)[prs_base_id]; 1372 } else if (prs_base_id == PRS_THEME_FRAME_OVERLAY) { 1373#if defined(OS_WIN) && defined(USE_AURA) 1374 if (images->count(PRS_THEME_FRAME_WIN)) { 1375#else 1376 if (images->count(PRS_THEME_FRAME)) { 1377#endif 1378 // If there is no theme overlay, don't tint the default frame, 1379 // because it will overwrite the custom frame image when we cache and 1380 // reload from disk. 1381 frame = gfx::Image(); 1382 } 1383 } else { 1384 // If the theme doesn't specify an image, then apply the tint to 1385 // the default frame. 1386 frame = rb.GetImageNamed(IDR_THEME_FRAME); 1387#if defined(OS_WIN) && defined(USE_AURA) 1388 if (prs_id >= PRS_THEME_FRAME_WIN && 1389 prs_id <= PRS_THEME_FRAME_INCOGNITO_INACTIVE_WIN) { 1390 frame = rb.GetImageNamed(IDR_THEME_FRAME_WIN); 1391 } 1392#endif 1393 } 1394 if (!frame.IsEmpty()) { 1395 temp_output[prs_id] = CreateHSLShiftedImage( 1396 frame, GetTintInternal(kFrameTintMap[i].value)); 1397 } 1398 } 1399 MergeImageCaches(temp_output, images); 1400} 1401 1402void BrowserThemePack::CreateTintedButtons( 1403 const color_utils::HSL& button_tint, 1404 ImageCache* processed_images) const { 1405 if (button_tint.h != -1 || button_tint.s != -1 || button_tint.l != -1) { 1406 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 1407 const std::set<int>& idr_ids = 1408 ThemeProperties::GetTintableToolbarButtons(); 1409 for (std::set<int>::const_iterator it = idr_ids.begin(); 1410 it != idr_ids.end(); ++it) { 1411 int prs_id = GetPersistentIDByIDR(*it); 1412 DCHECK(prs_id > 0); 1413 1414 // Fetch the image by IDR... 1415 gfx::Image& button = rb.GetImageNamed(*it); 1416 1417 // but save a version with the persistent ID. 1418 (*processed_images)[prs_id] = 1419 CreateHSLShiftedImage(button, button_tint); 1420 } 1421 } 1422} 1423 1424void BrowserThemePack::CreateTabBackgroundImages(ImageCache* images) const { 1425 ImageCache temp_output; 1426 for (size_t i = 0; i < arraysize(kTabBackgroundMap); ++i) { 1427 int prs_id = kTabBackgroundMap[i].key; 1428 int prs_base_id = kTabBackgroundMap[i].value; 1429 1430 // We only need to generate the background tab images if we were provided 1431 // with a PRS_THEME_FRAME. 1432 ImageCache::const_iterator it = images->find(prs_base_id); 1433 if (it != images->end()) { 1434 gfx::ImageSkia image_to_tint = (it->second).AsImageSkia(); 1435 color_utils::HSL hsl_shift = GetTintInternal( 1436 ThemeProperties::TINT_BACKGROUND_TAB); 1437 int vertical_offset = images->count(prs_id) 1438 ? kRestoredTabVerticalOffset : 0; 1439 1440 gfx::ImageSkia overlay; 1441 ImageCache::const_iterator overlay_it = images->find(prs_id); 1442 if (overlay_it != images->end()) 1443 overlay = overlay_it->second.AsImageSkia(); 1444 1445 gfx::ImageSkiaSource* source = new TabBackgroundImageSource( 1446 image_to_tint, overlay, hsl_shift, vertical_offset); 1447 // ImageSkia takes ownership of |source|. 1448 temp_output[prs_id] = gfx::Image(gfx::ImageSkia(source, 1449 image_to_tint.size())); 1450 } 1451 } 1452 MergeImageCaches(temp_output, images); 1453} 1454 1455void BrowserThemePack::RepackImages(const ImageCache& images, 1456 RawImages* reencoded_images) const { 1457 typedef std::vector<ui::ScaleFactor> ScaleFactors; 1458 for (ImageCache::const_iterator it = images.begin(); 1459 it != images.end(); ++it) { 1460 gfx::ImageSkia image_skia = *it->second.ToImageSkia(); 1461 1462 typedef std::vector<gfx::ImageSkiaRep> ImageSkiaReps; 1463 ImageSkiaReps image_reps = image_skia.image_reps(); 1464 if (image_reps.empty()) { 1465 NOTREACHED() << "No image reps for resource " << it->first << "."; 1466 } 1467 for (ImageSkiaReps::iterator rep_it = image_reps.begin(); 1468 rep_it != image_reps.end(); ++rep_it) { 1469 std::vector<unsigned char> bitmap_data; 1470 if (!gfx::PNGCodec::EncodeBGRASkBitmap(rep_it->sk_bitmap(), false, 1471 &bitmap_data)) { 1472 NOTREACHED() << "Image file for resource " << it->first 1473 << " could not be encoded."; 1474 } 1475 int raw_id = GetRawIDByPersistentID( 1476 it->first, 1477 ui::GetSupportedScaleFactor(rep_it->scale())); 1478 (*reencoded_images)[raw_id] = 1479 base::RefCountedBytes::TakeVector(&bitmap_data); 1480 } 1481 } 1482} 1483 1484void BrowserThemePack::MergeImageCaches( 1485 const ImageCache& source, ImageCache* destination) const { 1486 for (ImageCache::const_iterator it = source.begin(); it != source.end(); 1487 ++it) { 1488 (*destination)[it->first] = it->second; 1489 } 1490} 1491 1492void BrowserThemePack::AddRawImagesTo(const RawImages& images, 1493 RawDataForWriting* out) const { 1494 for (RawImages::const_iterator it = images.begin(); it != images.end(); 1495 ++it) { 1496 (*out)[it->first] = base::StringPiece( 1497 reinterpret_cast<const char*>(it->second->front()), it->second->size()); 1498 } 1499} 1500 1501color_utils::HSL BrowserThemePack::GetTintInternal(int id) const { 1502 if (tints_) { 1503 for (size_t i = 0; i < kTintTableLength; ++i) { 1504 if (tints_[i].id == id) { 1505 color_utils::HSL hsl; 1506 hsl.h = tints_[i].h; 1507 hsl.s = tints_[i].s; 1508 hsl.l = tints_[i].l; 1509 return hsl; 1510 } 1511 } 1512 } 1513 1514 return ThemeProperties::GetDefaultTint(id); 1515} 1516 1517int BrowserThemePack::GetRawIDByPersistentID( 1518 int prs_id, 1519 ui::ScaleFactor scale_factor) const { 1520 if (prs_id < 0) 1521 return -1; 1522 1523 for (size_t i = 0; i < scale_factors_.size(); ++i) { 1524 if (scale_factors_[i] == scale_factor) 1525 return static_cast<int>(kPersistingImagesLength * i) + prs_id; 1526 } 1527 return -1; 1528} 1529 1530bool BrowserThemePack::GetScaleFactorFromManifestKey( 1531 const std::string& key, 1532 ui::ScaleFactor* scale_factor) const { 1533 int percent = 0; 1534 if (base::StringToInt(key, &percent)) { 1535 float scale = static_cast<float>(percent) / 100.0f; 1536 for (size_t i = 0; i < scale_factors_.size(); ++i) { 1537 if (fabs(ui::GetImageScale(scale_factors_[i]) - scale) < 0.001) { 1538 *scale_factor = scale_factors_[i]; 1539 return true; 1540 } 1541 } 1542 } 1543 return false; 1544} 1545 1546void BrowserThemePack::GenerateRawImageForAllSupportedScales(int prs_id) { 1547 // Compute (by scaling) bitmaps for |prs_id| for any scale factors 1548 // for which the theme author did not provide a bitmap. We compute 1549 // the bitmaps using the highest scale factor that theme author 1550 // provided. 1551 // Note: We use only supported scale factors. For example, if scale 1552 // factor 2x is supported by the current system, but 1.8x is not and 1553 // if the theme author did not provide an image for 2x but one for 1554 // 1.8x, we will not use the 1.8x image here. Here we will only use 1555 // images provided for scale factors supported by the current system. 1556 1557 // See if any image is missing. If not, we're done. 1558 bool image_missing = false; 1559 for (size_t i = 0; i < scale_factors_.size(); ++i) { 1560 int raw_id = GetRawIDByPersistentID(prs_id, scale_factors_[i]); 1561 if (image_memory_.find(raw_id) == image_memory_.end()) { 1562 image_missing = true; 1563 break; 1564 } 1565 } 1566 if (!image_missing) 1567 return; 1568 1569 // Find available scale factor with highest scale. 1570 ui::ScaleFactor available_scale_factor = ui::SCALE_FACTOR_NONE; 1571 for (size_t i = 0; i < scale_factors_.size(); ++i) { 1572 int raw_id = GetRawIDByPersistentID(prs_id, scale_factors_[i]); 1573 if ((available_scale_factor == ui::SCALE_FACTOR_NONE || 1574 (ui::GetImageScale(scale_factors_[i]) > 1575 ui::GetImageScale(available_scale_factor))) && 1576 image_memory_.find(raw_id) != image_memory_.end()) { 1577 available_scale_factor = scale_factors_[i]; 1578 } 1579 } 1580 // If no scale factor is available, we're done. 1581 if (available_scale_factor == ui::SCALE_FACTOR_NONE) 1582 return; 1583 1584 // Get bitmap for the available scale factor. 1585 int available_raw_id = GetRawIDByPersistentID(prs_id, available_scale_factor); 1586 RawImages::const_iterator it = image_memory_.find(available_raw_id); 1587 SkBitmap available_bitmap; 1588 if (!gfx::PNGCodec::Decode(it->second->front(), 1589 it->second->size(), 1590 &available_bitmap)) { 1591 NOTREACHED() << "Unable to decode theme image for prs_id=" 1592 << prs_id << " for scale_factor=" << available_scale_factor; 1593 return; 1594 } 1595 1596 // Fill in all missing scale factors by scaling the available bitmap. 1597 for (size_t i = 0; i < scale_factors_.size(); ++i) { 1598 int scaled_raw_id = GetRawIDByPersistentID(prs_id, scale_factors_[i]); 1599 if (image_memory_.find(scaled_raw_id) != image_memory_.end()) 1600 continue; 1601 SkBitmap scaled_bitmap = 1602 CreateLowQualityResizedBitmap(available_bitmap, 1603 available_scale_factor, 1604 scale_factors_[i]); 1605 std::vector<unsigned char> bitmap_data; 1606 if (!gfx::PNGCodec::EncodeBGRASkBitmap(scaled_bitmap, 1607 false, 1608 &bitmap_data)) { 1609 NOTREACHED() << "Unable to encode theme image for prs_id=" 1610 << prs_id << " for scale_factor=" << scale_factors_[i]; 1611 break; 1612 } 1613 image_memory_[scaled_raw_id] = 1614 base::RefCountedBytes::TakeVector(&bitmap_data); 1615 } 1616} 1617