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