Home | History | Annotate | Download | only in resource
      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 "ui/base/resource/resource_bundle.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/command_line.h"
     10 #include "base/file_util.h"
     11 #include "base/logging.h"
     12 #include "base/memory/ref_counted_memory.h"
     13 #include "base/metrics/histogram.h"
     14 #include "base/path_service.h"
     15 #include "base/stl_util.h"
     16 #include "base/strings/string_piece.h"
     17 #include "base/strings/utf_string_conversions.h"
     18 #include "base/synchronization/lock.h"
     19 #include "build/build_config.h"
     20 #include "grit/app_locale_settings.h"
     21 #include "net/base/big_endian.h"
     22 #include "skia/ext/image_operations.h"
     23 #include "third_party/skia/include/core/SkBitmap.h"
     24 #include "ui/base/l10n/l10n_util.h"
     25 #include "ui/base/layout.h"
     26 #include "ui/base/resource/data_pack.h"
     27 #include "ui/base/ui_base_paths.h"
     28 #include "ui/base/ui_base_switches.h"
     29 #include "ui/gfx/codec/jpeg_codec.h"
     30 #include "ui/gfx/codec/png_codec.h"
     31 #include "ui/gfx/image/image_skia.h"
     32 #include "ui/gfx/image/image_skia_source.h"
     33 #include "ui/gfx/safe_integer_conversions.h"
     34 #include "ui/gfx/screen.h"
     35 #include "ui/gfx/size_conversions.h"
     36 #include "ui/gfx/skbitmap_operations.h"
     37 
     38 namespace ui {
     39 
     40 namespace {
     41 
     42 // Font sizes relative to base font.
     43 const int kSmallFontSizeDelta = -1;
     44 const int kMediumFontSizeDelta = 3;
     45 const int kLargeFontSizeDelta = 8;
     46 
     47 // PNG-related constants.
     48 const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
     49 const size_t kPngChunkMetadataSize = 12;  // length, type, crc32
     50 const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' };
     51 const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' };
     52 
     53 ResourceBundle* g_shared_instance_ = NULL;
     54 
     55 }  // namespace
     56 
     57 // An ImageSkiaSource that loads bitmaps for the requested scale factor from
     58 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the
     59 // requested scale factor does not exist, it will return the 1x bitmap scaled
     60 // by the scale factor. This may lead to broken UI if the correct size of the
     61 // scaled image is not exactly |scale_factor| * the size of the 1x resource.
     62 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images
     63 // are higlighted by blending them with red.
     64 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource {
     65  public:
     66   ResourceBundleImageSource(ResourceBundle* rb, int resource_id)
     67       : rb_(rb), resource_id_(resource_id) {}
     68   virtual ~ResourceBundleImageSource() {}
     69 
     70   // gfx::ImageSkiaSource overrides:
     71   virtual gfx::ImageSkiaRep GetImageForScale(
     72       ui::ScaleFactor scale_factor) OVERRIDE {
     73     SkBitmap image;
     74     bool fell_back_to_1x = false;
     75     bool found = rb_->LoadBitmap(resource_id_, &scale_factor,
     76                                  &image, &fell_back_to_1x);
     77     if (!found)
     78       return gfx::ImageSkiaRep();
     79 
     80     if (fell_back_to_1x) {
     81       // GRIT fell back to the 100% image, so rescale it to the correct size.
     82       float scale = GetScaleFactorScale(scale_factor);
     83       image = skia::ImageOperations::Resize(
     84           image,
     85           skia::ImageOperations::RESIZE_LANCZOS3,
     86           gfx::ToCeiledInt(image.width() * scale),
     87           gfx::ToCeiledInt(image.height() * scale));
     88       // If --highlight-missing-scaled-resources is specified, log the resource
     89       // id and blend the created resource with red.
     90       if (ShouldHighlightMissingScaledResources()) {
     91         LOG(ERROR) << "Missing " << scale << "x scaled resource. id="
     92                    << resource_id_;
     93 
     94         SkBitmap mask;
     95         mask.setConfig(SkBitmap::kARGB_8888_Config,
     96                        image.width(), image.height());
     97         mask.allocPixels();
     98         mask.eraseColor(SK_ColorRED);
     99         image = SkBitmapOperations::CreateBlendedBitmap(image, mask, 0.2);
    100       }
    101     }
    102 
    103     return gfx::ImageSkiaRep(image, scale_factor);
    104   }
    105 
    106  private:
    107   ResourceBundle* rb_;
    108   const int resource_id_;
    109 
    110   DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource);
    111 };
    112 
    113 // static
    114 std::string ResourceBundle::InitSharedInstanceWithLocale(
    115     const std::string& pref_locale, Delegate* delegate) {
    116   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
    117   g_shared_instance_ = new ResourceBundle(delegate);
    118 
    119   g_shared_instance_->LoadCommonResources();
    120   std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
    121   return result;
    122 }
    123 
    124 // static
    125 std::string ResourceBundle::InitSharedInstanceLocaleOnly(
    126     const std::string& pref_locale, Delegate* delegate) {
    127   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
    128   g_shared_instance_ = new ResourceBundle(delegate);
    129 
    130   std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
    131   return result;
    132 }
    133 
    134 // static
    135 void ResourceBundle::InitSharedInstanceWithPakFile(
    136     base::PlatformFile pak_file, bool should_load_common_resources) {
    137   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
    138   g_shared_instance_ = new ResourceBundle(NULL);
    139 
    140   if (should_load_common_resources)
    141     g_shared_instance_->LoadCommonResources();
    142 
    143   scoped_ptr<DataPack> data_pack(
    144       new DataPack(SCALE_FACTOR_100P));
    145   if (!data_pack->LoadFromFile(pak_file)) {
    146     NOTREACHED() << "failed to load pak file";
    147     return;
    148   }
    149   g_shared_instance_->locale_resources_data_.reset(data_pack.release());
    150 }
    151 
    152 // static
    153 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) {
    154   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
    155   g_shared_instance_ = new ResourceBundle(NULL);
    156 
    157   g_shared_instance_->LoadTestResources(path, path);
    158 }
    159 
    160 // static
    161 void ResourceBundle::CleanupSharedInstance() {
    162   if (g_shared_instance_) {
    163     delete g_shared_instance_;
    164     g_shared_instance_ = NULL;
    165   }
    166 }
    167 
    168 // static
    169 bool ResourceBundle::HasSharedInstance() {
    170   return g_shared_instance_ != NULL;
    171 }
    172 
    173 // static
    174 ResourceBundle& ResourceBundle::GetSharedInstance() {
    175   // Must call InitSharedInstance before this function.
    176   CHECK(g_shared_instance_ != NULL);
    177   return *g_shared_instance_;
    178 }
    179 
    180 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
    181   return !GetLocaleFilePath(locale, true).empty();
    182 }
    183 
    184 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
    185                                          ScaleFactor scale_factor) {
    186   AddDataPackFromPathInternal(path, scale_factor, false);
    187 }
    188 
    189 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path,
    190                                          ScaleFactor scale_factor) {
    191   AddDataPackFromPathInternal(path, scale_factor, true);
    192 }
    193 
    194 void ResourceBundle::AddDataPackFromFile(base::PlatformFile file,
    195                                          ScaleFactor scale_factor) {
    196   scoped_ptr<DataPack> data_pack(
    197       new DataPack(scale_factor));
    198   if (data_pack->LoadFromFile(file)) {
    199     AddDataPack(data_pack.release());
    200   } else {
    201     LOG(ERROR) << "Failed to load data pack from file."
    202                << "\nSome features may not be available.";
    203   }
    204 }
    205 
    206 #if !defined(OS_MACOSX)
    207 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale,
    208                                                  bool test_file_exists) {
    209   if (app_locale.empty())
    210     return base::FilePath();
    211 
    212   base::FilePath locale_file_path;
    213 
    214   PathService::Get(ui::DIR_LOCALES, &locale_file_path);
    215 
    216   if (!locale_file_path.empty())
    217     locale_file_path = locale_file_path.AppendASCII(app_locale + ".pak");
    218 
    219   if (delegate_) {
    220     locale_file_path =
    221         delegate_->GetPathForLocalePack(locale_file_path, app_locale);
    222   }
    223 
    224   // Don't try to load empty values or values that are not absolute paths.
    225   if (locale_file_path.empty() || !locale_file_path.IsAbsolute())
    226     return base::FilePath();
    227 
    228   if (test_file_exists && !base::PathExists(locale_file_path))
    229     return base::FilePath();
    230 
    231   return locale_file_path;
    232 }
    233 #endif
    234 
    235 std::string ResourceBundle::LoadLocaleResources(
    236     const std::string& pref_locale) {
    237   DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded";
    238   std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);
    239   base::FilePath locale_file_path = GetOverriddenPakPath();
    240   if (locale_file_path.empty()) {
    241     CommandLine* command_line = CommandLine::ForCurrentProcess();
    242     if (command_line->HasSwitch(switches::kLocalePak)) {
    243       locale_file_path =
    244           command_line->GetSwitchValuePath(switches::kLocalePak);
    245     } else {
    246       locale_file_path = GetLocaleFilePath(app_locale, true);
    247     }
    248   }
    249 
    250   if (locale_file_path.empty()) {
    251     // It's possible that there is no locale.pak.
    252     LOG(WARNING) << "locale_file_path.empty()";
    253     return std::string();
    254   }
    255 
    256   scoped_ptr<DataPack> data_pack(
    257       new DataPack(SCALE_FACTOR_100P));
    258   if (!data_pack->LoadFromPath(locale_file_path)) {
    259     UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError",
    260                               logging::GetLastSystemErrorCode(), 16000);
    261     LOG(ERROR) << "failed to load locale.pak";
    262     NOTREACHED();
    263     return std::string();
    264   }
    265 
    266   locale_resources_data_.reset(data_pack.release());
    267   return app_locale;
    268 }
    269 
    270 void ResourceBundle::LoadTestResources(const base::FilePath& path,
    271                                        const base::FilePath& locale_path) {
    272   // Use the given resource pak for both common and localized resources.
    273   scoped_ptr<DataPack> data_pack(
    274       new DataPack(SCALE_FACTOR_100P));
    275   if (!path.empty() && data_pack->LoadFromPath(path))
    276     AddDataPack(data_pack.release());
    277 
    278   data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE));
    279   if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
    280     locale_resources_data_.reset(data_pack.release());
    281   } else {
    282     locale_resources_data_.reset(
    283         new DataPack(ui::SCALE_FACTOR_NONE));
    284   }
    285 }
    286 
    287 void ResourceBundle::UnloadLocaleResources() {
    288   locale_resources_data_.reset();
    289 }
    290 
    291 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) {
    292   overridden_pak_path_ = pak_path;
    293 }
    294 
    295 const base::FilePath& ResourceBundle::GetOverriddenPakPath() {
    296   return overridden_pak_path_;
    297 }
    298 
    299 std::string ResourceBundle::ReloadLocaleResources(
    300     const std::string& pref_locale) {
    301   base::AutoLock lock_scope(*locale_resources_data_lock_);
    302   UnloadLocaleResources();
    303   return LoadLocaleResources(pref_locale);
    304 }
    305 
    306 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) {
    307   const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia();
    308   return const_cast<gfx::ImageSkia*>(image);
    309 }
    310 
    311 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) {
    312   // Check to see if the image is already in the cache.
    313   {
    314     base::AutoLock lock_scope(*images_and_fonts_lock_);
    315     if (images_.count(resource_id))
    316       return images_[resource_id];
    317   }
    318 
    319   gfx::Image image;
    320   if (delegate_)
    321     image = delegate_->GetImageNamed(resource_id);
    322 
    323   if (image.IsEmpty()) {
    324     DCHECK(!delegate_ && !data_packs_.empty()) <<
    325         "Missing call to SetResourcesDataDLL?";
    326 
    327     // TODO(oshima): Consider reading the image size from png IHDR chunk and
    328     // skip decoding here and remove #ifdef below.
    329     // ResourceBundle::GetSharedInstance() is destroyed after the
    330     // BrowserMainLoop has finished running. |image_skia| is guaranteed to be
    331     // destroyed before the resource bundle is destroyed.
    332 #if defined(OS_CHROMEOS)
    333     ui::ScaleFactor scale_factor_to_load = ui::GetMaxScaleFactor();
    334 #else
    335     ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
    336 #endif
    337     gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id),
    338                               scale_factor_to_load);
    339     if (image_skia.isNull()) {
    340       LOG(WARNING) << "Unable to load image with id " << resource_id;
    341       NOTREACHED();  // Want to assert in debug mode.
    342       // The load failed to retrieve the image; show a debugging red square.
    343       return GetEmptyImage();
    344     }
    345     image_skia.SetReadOnly();
    346     image = gfx::Image(image_skia);
    347   }
    348 
    349   // The load was successful, so cache the image.
    350   base::AutoLock lock_scope(*images_and_fonts_lock_);
    351 
    352   // Another thread raced the load and has already cached the image.
    353   if (images_.count(resource_id))
    354     return images_[resource_id];
    355 
    356   images_[resource_id] = image;
    357   return images_[resource_id];
    358 }
    359 
    360 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
    361   return GetNativeImageNamed(resource_id, RTL_DISABLED);
    362 }
    363 
    364 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes(
    365     int resource_id) const {
    366   return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE);
    367 }
    368 
    369 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale(
    370     int resource_id,
    371     ScaleFactor scale_factor) const {
    372   base::RefCountedStaticMemory* bytes = NULL;
    373   if (delegate_)
    374     bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor);
    375 
    376   if (!bytes) {
    377     base::StringPiece data =
    378         GetRawDataResourceForScale(resource_id, scale_factor);
    379     if (!data.empty()) {
    380       bytes = new base::RefCountedStaticMemory(
    381           reinterpret_cast<const unsigned char*>(data.data()), data.length());
    382     }
    383   }
    384 
    385   return bytes;
    386 }
    387 
    388 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const {
    389   return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE);
    390 }
    391 
    392 base::StringPiece ResourceBundle::GetRawDataResourceForScale(
    393     int resource_id,
    394     ScaleFactor scale_factor) const {
    395   base::StringPiece data;
    396   if (delegate_ &&
    397       delegate_->GetRawDataResource(resource_id, scale_factor, &data))
    398     return data;
    399 
    400   if (scale_factor != ui::SCALE_FACTOR_100P) {
    401     for (size_t i = 0; i < data_packs_.size(); i++) {
    402       if (data_packs_[i]->GetScaleFactor() == scale_factor &&
    403           data_packs_[i]->GetStringPiece(resource_id, &data))
    404         return data;
    405     }
    406   }
    407   for (size_t i = 0; i < data_packs_.size(); i++) {
    408     if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P ||
    409          data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) &&
    410         data_packs_[i]->GetStringPiece(resource_id, &data))
    411       return data;
    412   }
    413 
    414   return base::StringPiece();
    415 }
    416 
    417 string16 ResourceBundle::GetLocalizedString(int message_id) {
    418   string16 string;
    419   if (delegate_ && delegate_->GetLocalizedString(message_id, &string))
    420     return string;
    421 
    422   // Ensure that ReloadLocaleResources() doesn't drop the resources while
    423   // we're using them.
    424   base::AutoLock lock_scope(*locale_resources_data_lock_);
    425 
    426   // If for some reason we were unable to load the resources , return an empty
    427   // string (better than crashing).
    428   if (!locale_resources_data_.get()) {
    429     LOG(WARNING) << "locale resources are not loaded";
    430     return string16();
    431   }
    432 
    433   base::StringPiece data;
    434   if (!locale_resources_data_->GetStringPiece(message_id, &data)) {
    435     // Fall back on the main data pack (shouldn't be any strings here except in
    436     // unittests).
    437     data = GetRawDataResource(message_id);
    438     if (data.empty()) {
    439       NOTREACHED() << "unable to find resource: " << message_id;
    440       return string16();
    441     }
    442   }
    443 
    444   // Strings should not be loaded from a data pack that contains binary data.
    445   ResourceHandle::TextEncodingType encoding =
    446       locale_resources_data_->GetTextEncodingType();
    447   DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8)
    448       << "requested localized string from binary pack file";
    449 
    450   // Data pack encodes strings as either UTF8 or UTF16.
    451   string16 msg;
    452   if (encoding == ResourceHandle::UTF16) {
    453     msg = string16(reinterpret_cast<const char16*>(data.data()),
    454                    data.length() / 2);
    455   } else if (encoding == ResourceHandle::UTF8) {
    456     msg = UTF8ToUTF16(data);
    457   }
    458   return msg;
    459 }
    460 
    461 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) {
    462   {
    463     base::AutoLock lock_scope(*images_and_fonts_lock_);
    464     LoadFontsIfNecessary();
    465   }
    466   switch (style) {
    467     case BoldFont:
    468       return *bold_font_list_;
    469     case SmallFont:
    470       return *small_font_list_;
    471     case MediumFont:
    472       return *medium_font_list_;
    473     case SmallBoldFont:
    474       return *small_bold_font_list_;
    475     case MediumBoldFont:
    476       return *medium_bold_font_list_;
    477     case LargeFont:
    478       return *large_font_list_;
    479     case LargeBoldFont:
    480       return *large_bold_font_list_;
    481     default:
    482       return *base_font_list_;
    483   }
    484 }
    485 
    486 const gfx::Font& ResourceBundle::GetFont(FontStyle style) {
    487   return GetFontList(style).GetPrimaryFont();
    488 }
    489 
    490 void ResourceBundle::ReloadFonts() {
    491   base::AutoLock lock_scope(*images_and_fonts_lock_);
    492   base_font_list_.reset();
    493   LoadFontsIfNecessary();
    494 }
    495 
    496 ResourceBundle::ResourceBundle(Delegate* delegate)
    497     : delegate_(delegate),
    498       images_and_fonts_lock_(new base::Lock),
    499       locale_resources_data_lock_(new base::Lock),
    500       max_scale_factor_(SCALE_FACTOR_100P) {
    501 }
    502 
    503 ResourceBundle::~ResourceBundle() {
    504   FreeImages();
    505   UnloadLocaleResources();
    506 }
    507 
    508 void ResourceBundle::FreeImages() {
    509   images_.clear();
    510 }
    511 
    512 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path,
    513                                                  ScaleFactor scale_factor,
    514                                                  bool optional) {
    515   // Do not pass an empty |path| value to this method. If the absolute path is
    516   // unknown pass just the pack file name.
    517   DCHECK(!path.empty());
    518 
    519   base::FilePath pack_path = path;
    520   if (delegate_)
    521     pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor);
    522 
    523   // Don't try to load empty values or values that are not absolute paths.
    524   if (pack_path.empty() || !pack_path.IsAbsolute())
    525     return;
    526 
    527   scoped_ptr<DataPack> data_pack(
    528       new DataPack(scale_factor));
    529   if (data_pack->LoadFromPath(pack_path)) {
    530     AddDataPack(data_pack.release());
    531   } else if (!optional) {
    532     LOG(ERROR) << "Failed to load " << pack_path.value()
    533                << "\nSome features may not be available.";
    534   }
    535 }
    536 
    537 void ResourceBundle::AddDataPack(DataPack* data_pack) {
    538   data_packs_.push_back(data_pack);
    539 
    540   if (GetScaleFactorScale(data_pack->GetScaleFactor()) >
    541       GetScaleFactorScale(max_scale_factor_))
    542     max_scale_factor_ = data_pack->GetScaleFactor();
    543 }
    544 
    545 void ResourceBundle::LoadFontsIfNecessary() {
    546   images_and_fonts_lock_->AssertAcquired();
    547   if (!base_font_list_.get()) {
    548     if (delegate_) {
    549       base_font_list_ = GetFontListFromDelegate(BaseFont);
    550       bold_font_list_ = GetFontListFromDelegate(BoldFont);
    551       small_font_list_ = GetFontListFromDelegate(SmallFont);
    552       small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont);
    553       medium_font_list_ = GetFontListFromDelegate(MediumFont);
    554       medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont);
    555       large_font_list_ = GetFontListFromDelegate(LargeFont);
    556       large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
    557     }
    558 
    559     if (!base_font_list_.get()) {
    560 #if defined(OS_CHROMEOS)
    561       base_font_list_.reset(new gfx::FontList(
    562           l10n_util::GetStringUTF8(IDS_UI_FONT_FAMILY_CROS)));
    563 #else
    564       base_font_list_.reset(new gfx::FontList());
    565 #endif
    566     }
    567 
    568     if (!bold_font_list_.get()) {
    569       bold_font_list_.reset(new gfx::FontList());
    570       *bold_font_list_ = base_font_list_->DeriveFontList(
    571           base_font_list_->GetFontStyle() | gfx::Font::BOLD);
    572     }
    573 
    574     if (!small_font_list_.get()) {
    575       small_font_list_.reset(new gfx::FontList());
    576       *small_font_list_ = base_font_list_->DeriveFontListWithSize(
    577           base_font_list_->GetFontSize() + kSmallFontSizeDelta);
    578     }
    579 
    580     if (!small_bold_font_list_.get()) {
    581       small_bold_font_list_.reset(new gfx::FontList());
    582       *small_bold_font_list_ = small_font_list_->DeriveFontList(
    583           small_font_list_->GetFontStyle() | gfx::Font::BOLD);
    584     }
    585 
    586     if (!medium_font_list_.get()) {
    587       medium_font_list_.reset(new gfx::FontList());
    588       *medium_font_list_ = base_font_list_->DeriveFontListWithSize(
    589           base_font_list_->GetFontSize() + kMediumFontSizeDelta);
    590     }
    591 
    592     if (!medium_bold_font_list_.get()) {
    593       medium_bold_font_list_.reset(new gfx::FontList());
    594       *medium_bold_font_list_ = medium_font_list_->DeriveFontList(
    595           medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
    596     }
    597 
    598     if (!large_font_list_.get()) {
    599       large_font_list_.reset(new gfx::FontList());
    600       *large_font_list_ = base_font_list_->DeriveFontListWithSize(
    601           base_font_list_->GetFontSize() + kLargeFontSizeDelta);
    602     }
    603 
    604     if (!large_bold_font_list_.get()) {
    605       large_bold_font_list_.reset(new gfx::FontList());
    606       *large_bold_font_list_ = large_font_list_->DeriveFontList(
    607           large_font_list_->GetFontStyle() | gfx::Font::BOLD);
    608     }
    609   }
    610 }
    611 
    612 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate(
    613     FontStyle style) {
    614   DCHECK(delegate_);
    615   scoped_ptr<gfx::Font> font = delegate_->GetFont(style);
    616   if (font.get())
    617     return scoped_ptr<gfx::FontList>(new gfx::FontList(*font));
    618   return scoped_ptr<gfx::FontList>();
    619 }
    620 
    621 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle,
    622                                 int resource_id,
    623                                 SkBitmap* bitmap,
    624                                 bool* fell_back_to_1x) const {
    625   DCHECK(fell_back_to_1x);
    626   scoped_refptr<base::RefCountedMemory> memory(
    627       data_handle.GetStaticMemory(resource_id));
    628   if (!memory.get())
    629     return false;
    630 
    631   if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x))
    632     return true;
    633 
    634 #if !defined(OS_IOS)
    635   // iOS does not compile or use the JPEG codec.  On other platforms,
    636   // 99% of our assets are PNGs, however fallback to JPEG.
    637   scoped_ptr<SkBitmap> jpeg_bitmap(
    638       gfx::JPEGCodec::Decode(memory->front(), memory->size()));
    639   if (jpeg_bitmap.get()) {
    640     bitmap->swap(*jpeg_bitmap.get());
    641     *fell_back_to_1x = false;
    642     return true;
    643   }
    644 #endif
    645 
    646   NOTREACHED() << "Unable to decode theme image resource " << resource_id;
    647   return false;
    648 }
    649 
    650 bool ResourceBundle::LoadBitmap(int resource_id,
    651                                 ScaleFactor* scale_factor,
    652                                 SkBitmap* bitmap,
    653                                 bool* fell_back_to_1x) const {
    654   DCHECK(fell_back_to_1x);
    655   for (size_t i = 0; i < data_packs_.size(); ++i) {
    656     // If the resource is in the package with SCALE_FACTOR_NONE, it
    657     // can be used in any scale factor, but set 100P in ImageSkia so
    658     // that it will be scaled property.
    659     if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
    660         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
    661       *scale_factor = ui::SCALE_FACTOR_100P;
    662       DCHECK(!*fell_back_to_1x);
    663       return true;
    664     }
    665     if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
    666         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
    667       return true;
    668     }
    669   }
    670   return false;
    671 }
    672 
    673 gfx::Image& ResourceBundle::GetEmptyImage() {
    674   base::AutoLock lock(*images_and_fonts_lock_);
    675 
    676   if (empty_image_.IsEmpty()) {
    677     // The placeholder bitmap is bright red so people notice the problem.
    678     SkBitmap bitmap;
    679     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32);
    680     bitmap.allocPixels();
    681     bitmap.eraseARGB(255, 255, 0, 0);
    682     empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
    683   }
    684   return empty_image_;
    685 }
    686 
    687 // static
    688 bool ResourceBundle::ShouldHighlightMissingScaledResources() {
    689   return CommandLine::ForCurrentProcess()->HasSwitch(
    690       switches::kHighlightMissingScaledResources);
    691 }
    692 
    693 // static
    694 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
    695                                size_t size) {
    696   if (size < arraysize(kPngMagic) ||
    697       memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
    698     // Data invalid or a JPEG.
    699     return false;
    700   }
    701   size_t pos = arraysize(kPngMagic);
    702 
    703   // Scan for custom chunks until we find one, find the IDAT chunk, or run out
    704   // of chunks.
    705   for (;;) {
    706     if (size - pos < kPngChunkMetadataSize)
    707       break;
    708     uint32 length = 0;
    709     net::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
    710     if (size - pos - kPngChunkMetadataSize < length)
    711       break;
    712     if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,
    713                               arraysize(kPngScaleChunkType)) == 0) {
    714       return true;
    715     }
    716     if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType,
    717                arraysize(kPngDataChunkType)) == 0) {
    718       // Stop looking for custom chunks, any custom chunks should be before an
    719       // IDAT chunk.
    720       break;
    721     }
    722     pos += length + kPngChunkMetadataSize;
    723   }
    724   return false;
    725 }
    726 
    727 // static
    728 bool ResourceBundle::DecodePNG(const unsigned char* buf,
    729                                size_t size,
    730                                SkBitmap* bitmap,
    731                                bool* fell_back_to_1x) {
    732   *fell_back_to_1x = PNGContainsFallbackMarker(buf, size);
    733   return gfx::PNGCodec::Decode(buf, size, bitmap);
    734 }
    735 
    736 }  // namespace ui
    737