Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / ui / base / resource / resource_bundle.cc
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::File 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.Pass())) {
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::File file,
223                                          ScaleFactor scale_factor) {
224   scoped_ptr<DataPack> data_pack(
225       new DataPack(scale_factor));
226   if (data_pack->LoadFromFile(file.Pass())) {
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 = base::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 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
575   supported_scale_factors.push_back(SCALE_FACTOR_200P);
576 #endif
577   ui::SetSupportedScaleFactors(supported_scale_factors);
578 #if defined(OS_WIN)
579   // Must be called _after_ supported scale factors are set since it
580   // uses them.
581   ui::win::InitDeviceScaleFactor();
582 #endif
583 }
584
585 void ResourceBundle::FreeImages() {
586   images_.clear();
587 }
588
589 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path,
590                                                  ScaleFactor scale_factor,
591                                                  bool optional) {
592   // Do not pass an empty |path| value to this method. If the absolute path is
593   // unknown pass just the pack file name.
594   DCHECK(!path.empty());
595
596   base::FilePath pack_path = path;
597   if (delegate_)
598     pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor);
599
600   // Don't try to load empty values or values that are not absolute paths.
601   if (pack_path.empty() || !pack_path.IsAbsolute())
602     return;
603
604   scoped_ptr<DataPack> data_pack(
605       new DataPack(scale_factor));
606   if (data_pack->LoadFromPath(pack_path)) {
607     AddDataPack(data_pack.release());
608   } else if (!optional) {
609     LOG(ERROR) << "Failed to load " << pack_path.value()
610                << "\nSome features may not be available.";
611   }
612 }
613
614 void ResourceBundle::AddDataPack(DataPack* data_pack) {
615   data_packs_.push_back(data_pack);
616
617   if (GetImageScale(data_pack->GetScaleFactor()) >
618       GetImageScale(max_scale_factor_))
619     max_scale_factor_ = data_pack->GetScaleFactor();
620 }
621
622 void ResourceBundle::LoadFontsIfNecessary() {
623   images_and_fonts_lock_->AssertAcquired();
624   if (!base_font_list_.get()) {
625     if (delegate_) {
626       base_font_list_ = GetFontListFromDelegate(BaseFont);
627       bold_font_list_ = GetFontListFromDelegate(BoldFont);
628       small_font_list_ = GetFontListFromDelegate(SmallFont);
629       small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont);
630       medium_font_list_ = GetFontListFromDelegate(MediumFont);
631       medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont);
632       large_font_list_ = GetFontListFromDelegate(LargeFont);
633       large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
634     }
635
636     if (!base_font_list_.get())
637       base_font_list_.reset(new gfx::FontList());
638
639     if (!bold_font_list_.get()) {
640       bold_font_list_.reset(new gfx::FontList());
641       *bold_font_list_ = base_font_list_->DeriveFontList(
642           base_font_list_->GetFontStyle() | gfx::Font::BOLD);
643     }
644
645     if (!small_font_list_.get()) {
646       small_font_list_.reset(new gfx::FontList());
647       *small_font_list_ = base_font_list_->DeriveFontListWithSize(
648           base_font_list_->GetFontSize() + kSmallFontSizeDelta);
649     }
650
651     if (!small_bold_font_list_.get()) {
652       small_bold_font_list_.reset(new gfx::FontList());
653       *small_bold_font_list_ = small_font_list_->DeriveFontList(
654           small_font_list_->GetFontStyle() | gfx::Font::BOLD);
655     }
656
657     if (!medium_font_list_.get()) {
658       medium_font_list_.reset(new gfx::FontList());
659       *medium_font_list_ = base_font_list_->DeriveFontListWithSize(
660           base_font_list_->GetFontSize() + kMediumFontSizeDelta);
661     }
662
663     if (!medium_bold_font_list_.get()) {
664       medium_bold_font_list_.reset(new gfx::FontList());
665       *medium_bold_font_list_ = medium_font_list_->DeriveFontList(
666           medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
667     }
668
669     if (!large_font_list_.get()) {
670       large_font_list_.reset(new gfx::FontList());
671       *large_font_list_ = base_font_list_->DeriveFontListWithSize(
672           base_font_list_->GetFontSize() + kLargeFontSizeDelta);
673     }
674
675     if (!large_bold_font_list_.get()) {
676       large_bold_font_list_.reset(new gfx::FontList());
677       *large_bold_font_list_ = large_font_list_->DeriveFontList(
678           large_font_list_->GetFontStyle() | gfx::Font::BOLD);
679     }
680   }
681 }
682
683 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate(
684     FontStyle style) {
685   DCHECK(delegate_);
686   scoped_ptr<gfx::Font> font = delegate_->GetFont(style);
687   if (font.get())
688     return scoped_ptr<gfx::FontList>(new gfx::FontList(*font));
689   return scoped_ptr<gfx::FontList>();
690 }
691
692 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle,
693                                 int resource_id,
694                                 SkBitmap* bitmap,
695                                 bool* fell_back_to_1x) const {
696   DCHECK(fell_back_to_1x);
697   scoped_refptr<base::RefCountedMemory> memory(
698       data_handle.GetStaticMemory(resource_id));
699   if (!memory.get())
700     return false;
701
702   if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x))
703     return true;
704
705 #if !defined(OS_IOS)
706   // iOS does not compile or use the JPEG codec.  On other platforms,
707   // 99% of our assets are PNGs, however fallback to JPEG.
708   scoped_ptr<SkBitmap> jpeg_bitmap(
709       gfx::JPEGCodec::Decode(memory->front(), memory->size()));
710   if (jpeg_bitmap.get()) {
711     bitmap->swap(*jpeg_bitmap.get());
712     *fell_back_to_1x = false;
713     return true;
714   }
715 #endif
716
717   NOTREACHED() << "Unable to decode theme image resource " << resource_id;
718   return false;
719 }
720
721 bool ResourceBundle::LoadBitmap(int resource_id,
722                                 ScaleFactor* scale_factor,
723                                 SkBitmap* bitmap,
724                                 bool* fell_back_to_1x) const {
725   DCHECK(fell_back_to_1x);
726   for (size_t i = 0; i < data_packs_.size(); ++i) {
727     // If the resource is in the package with SCALE_FACTOR_NONE, it
728     // can be used in any scale factor, but set 100P in ImageSkia so
729     // that it will be scaled property.
730     if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
731         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
732       *scale_factor = ui::SCALE_FACTOR_100P;
733       DCHECK(!*fell_back_to_1x);
734       return true;
735     }
736     if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
737         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
738       return true;
739     }
740   }
741   return false;
742 }
743
744 gfx::Image& ResourceBundle::GetEmptyImage() {
745   base::AutoLock lock(*images_and_fonts_lock_);
746
747   if (empty_image_.IsEmpty()) {
748     // The placeholder bitmap is bright red so people notice the problem.
749     SkBitmap bitmap;
750     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32);
751     bitmap.allocPixels();
752     bitmap.eraseARGB(255, 255, 0, 0);
753     empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
754   }
755   return empty_image_;
756 }
757
758 // static
759 bool ResourceBundle::ShouldHighlightMissingScaledResources() {
760   return CommandLine::ForCurrentProcess()->HasSwitch(
761       switches::kHighlightMissingScaledResources);
762 }
763
764 // static
765 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
766                                size_t size) {
767   if (size < arraysize(kPngMagic) ||
768       memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
769     // Data invalid or a JPEG.
770     return false;
771   }
772   size_t pos = arraysize(kPngMagic);
773
774   // Scan for custom chunks until we find one, find the IDAT chunk, or run out
775   // of chunks.
776   for (;;) {
777     if (size - pos < kPngChunkMetadataSize)
778       break;
779     uint32 length = 0;
780     net::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
781     if (size - pos - kPngChunkMetadataSize < length)
782       break;
783     if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,
784                               arraysize(kPngScaleChunkType)) == 0) {
785       return true;
786     }
787     if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType,
788                arraysize(kPngDataChunkType)) == 0) {
789       // Stop looking for custom chunks, any custom chunks should be before an
790       // IDAT chunk.
791       break;
792     }
793     pos += length + kPngChunkMetadataSize;
794   }
795   return false;
796 }
797
798 // static
799 bool ResourceBundle::DecodePNG(const unsigned char* buf,
800                                size_t size,
801                                SkBitmap* bitmap,
802                                bool* fell_back_to_1x) {
803   *fell_back_to_1x = PNGContainsFallbackMarker(buf, size);
804   return gfx::PNGCodec::Decode(buf, size, bitmap);
805 }
806
807 }  // namespace ui