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