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