Upstream version 9.38.198.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 <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) && defined(USE_PANGO)
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,
168     Delegate* delegate,
169     LoadResources load_resources) {
170   InitSharedInstance(delegate);
171   if (load_resources == LOAD_COMMON_RESOURCES)
172     g_shared_instance_->LoadCommonResources();
173   std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
174   InitDefaultFontList();
175   return result;
176 }
177
178 // static
179 void ResourceBundle::InitSharedInstanceWithPakFileRegion(
180     base::File pak_file,
181     const base::MemoryMappedFile::Region& region) {
182   InitSharedInstance(NULL);
183   scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P));
184   if (!data_pack->LoadFromFileRegion(pak_file.Pass(), region)) {
185     NOTREACHED() << "failed to load pak file";
186     return;
187   }
188   g_shared_instance_->locale_resources_data_.reset(data_pack.release());
189   InitDefaultFontList();
190 }
191
192 // static
193 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) {
194   InitSharedInstance(NULL);
195   g_shared_instance_->LoadTestResources(path, path);
196
197   InitDefaultFontList();
198 }
199
200 // static
201 void ResourceBundle::CleanupSharedInstance() {
202   if (g_shared_instance_) {
203     delete g_shared_instance_;
204     g_shared_instance_ = NULL;
205   }
206 }
207
208 // static
209 bool ResourceBundle::HasSharedInstance() {
210   return g_shared_instance_ != NULL;
211 }
212
213 // static
214 ResourceBundle& ResourceBundle::GetSharedInstance() {
215   // Must call InitSharedInstance before this function.
216   CHECK(g_shared_instance_ != NULL);
217   return *g_shared_instance_;
218 }
219
220 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
221   bool locale_file_path_exists = !GetLocaleFilePath(locale, true).empty();
222 #if defined(OS_ANDROID)
223   // TODO(mkosiba,primiano): Chrome should mmap the .pak files too, in which
224   // case we'd not need to check if locale_file_path_exists here.
225   // http://crbug.com/394502.
226   return locale_file_path_exists ||
227       AssetContainedInApk(locale + kPakFileSuffix);
228 #else
229   return locale_file_path_exists;
230 #endif
231 }
232
233 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
234                                          ScaleFactor scale_factor) {
235   AddDataPackFromPathInternal(path, scale_factor, false);
236 }
237
238 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path,
239                                          ScaleFactor scale_factor) {
240   AddDataPackFromPathInternal(path, scale_factor, true);
241 }
242
243 void ResourceBundle::AddDataPackFromFile(base::File file,
244                                          ScaleFactor scale_factor) {
245   AddDataPackFromFileRegion(
246       file.Pass(), base::MemoryMappedFile::Region::kWholeFile, scale_factor);
247 }
248
249 void ResourceBundle::AddDataPackFromFileRegion(
250     base::File file,
251     const base::MemoryMappedFile::Region& region,
252     ScaleFactor scale_factor) {
253   scoped_ptr<DataPack> data_pack(
254       new DataPack(scale_factor));
255   if (data_pack->LoadFromFileRegion(file.Pass(), region)) {
256     AddDataPack(data_pack.release());
257   } else {
258     LOG(ERROR) << "Failed to load data pack from file."
259                << "\nSome features may not be available.";
260   }
261 }
262
263 #if !defined(OS_MACOSX)
264 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale,
265                                                  bool test_file_exists) {
266   if (app_locale.empty())
267     return base::FilePath();
268
269   base::FilePath locale_file_path;
270
271   PathService::Get(ui::DIR_LOCALES, &locale_file_path);
272
273   if (!locale_file_path.empty()) {
274     locale_file_path =
275         locale_file_path.AppendASCII(app_locale + kPakFileSuffix);
276   }
277
278   if (delegate_) {
279     locale_file_path =
280         delegate_->GetPathForLocalePack(locale_file_path, app_locale);
281   }
282
283   // Don't try to load empty values or values that are not absolute paths.
284   if (locale_file_path.empty() || !locale_file_path.IsAbsolute())
285     return base::FilePath();
286
287   if (test_file_exists && !base::PathExists(locale_file_path))
288     return base::FilePath();
289
290   return locale_file_path;
291 }
292 #endif
293
294 std::string ResourceBundle::LoadLocaleResources(
295     const std::string& pref_locale) {
296   DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded";
297   std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);
298   base::FilePath locale_file_path = GetOverriddenPakPath();
299   if (locale_file_path.empty())
300     locale_file_path = GetLocaleFilePath(app_locale, true);
301
302   if (locale_file_path.empty()) {
303     // It's possible that there is no locale.pak.
304     LOG(WARNING) << "locale_file_path.empty()";
305     return std::string();
306   }
307
308   scoped_ptr<DataPack> data_pack(
309       new DataPack(SCALE_FACTOR_100P));
310   if (!data_pack->LoadFromPath(locale_file_path)) {
311     UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError",
312                               logging::GetLastSystemErrorCode(), 16000);
313     LOG(ERROR) << "failed to load locale.pak";
314     NOTREACHED();
315     return std::string();
316   }
317
318   locale_resources_data_.reset(data_pack.release());
319   return app_locale;
320 }
321
322 void ResourceBundle::LoadTestResources(const base::FilePath& path,
323                                        const base::FilePath& locale_path) {
324   // Use the given resource pak for both common and localized resources.
325   scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P));
326   if (!path.empty() && data_pack->LoadFromPath(path))
327     AddDataPack(data_pack.release());
328
329   data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE));
330   if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
331     locale_resources_data_.reset(data_pack.release());
332   } else {
333     locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE));
334   }
335 }
336
337 void ResourceBundle::UnloadLocaleResources() {
338   locale_resources_data_.reset();
339 }
340
341 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) {
342   overridden_pak_path_ = pak_path;
343 }
344
345 void ResourceBundle::OverrideLocaleStringResource(
346     int message_id,
347     const base::string16& string) {
348   overridden_locale_strings_[message_id] = string;
349 }
350
351 const base::FilePath& ResourceBundle::GetOverriddenPakPath() {
352   return overridden_pak_path_;
353 }
354
355 std::string ResourceBundle::ReloadLocaleResources(
356     const std::string& pref_locale) {
357   base::AutoLock lock_scope(*locale_resources_data_lock_);
358
359   // Remove all overriden strings, as they will not be valid for the new locale.
360   overridden_locale_strings_.clear();
361
362   UnloadLocaleResources();
363   return LoadLocaleResources(pref_locale);
364 }
365
366 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) {
367   const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia();
368   return const_cast<gfx::ImageSkia*>(image);
369 }
370
371 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) {
372   // Check to see if the image is already in the cache.
373   {
374     base::AutoLock lock_scope(*images_and_fonts_lock_);
375     if (images_.count(resource_id))
376       return images_[resource_id];
377   }
378
379   gfx::Image image;
380   if (delegate_)
381     image = delegate_->GetImageNamed(resource_id);
382
383   if (image.IsEmpty()) {
384     DCHECK(!data_packs_.empty()) <<
385         "Missing call to SetResourcesDataDLL?";
386
387 #if defined(OS_CHROMEOS) || defined(OS_WIN)
388   ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor();
389 #else
390   ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
391 #endif
392
393     // TODO(oshima): Consider reading the image size from png IHDR chunk and
394     // skip decoding here and remove #ifdef below.
395     // ResourceBundle::GetSharedInstance() is destroyed after the
396     // BrowserMainLoop has finished running. |image_skia| is guaranteed to be
397     // destroyed before the resource bundle is destroyed.
398     gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id),
399                               GetScaleForScaleFactor(scale_factor_to_load));
400     if (image_skia.isNull()) {
401       LOG(WARNING) << "Unable to load image with id " << resource_id;
402       NOTREACHED();  // Want to assert in debug mode.
403       // The load failed to retrieve the image; show a debugging red square.
404       return GetEmptyImage();
405     }
406     image_skia.SetReadOnly();
407     image = gfx::Image(image_skia);
408   }
409
410   // The load was successful, so cache the image.
411   base::AutoLock lock_scope(*images_and_fonts_lock_);
412
413   // Another thread raced the load and has already cached the image.
414   if (images_.count(resource_id))
415     return images_[resource_id];
416
417   images_[resource_id] = image;
418   return images_[resource_id];
419 }
420
421 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
422   return GetNativeImageNamed(resource_id, RTL_DISABLED);
423 }
424
425 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes(
426     int resource_id) const {
427   return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE);
428 }
429
430 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale(
431     int resource_id,
432     ScaleFactor scale_factor) const {
433   base::RefCountedStaticMemory* bytes = NULL;
434   if (delegate_)
435     bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor);
436
437   if (!bytes) {
438     base::StringPiece data =
439         GetRawDataResourceForScale(resource_id, scale_factor);
440     if (!data.empty()) {
441       bytes = new base::RefCountedStaticMemory(data.data(), data.length());
442     }
443   }
444
445   return bytes;
446 }
447
448 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const {
449   return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE);
450 }
451
452 base::StringPiece ResourceBundle::GetRawDataResourceForScale(
453     int resource_id,
454     ScaleFactor scale_factor) const {
455   base::StringPiece data;
456   if (delegate_ &&
457       delegate_->GetRawDataResource(resource_id, scale_factor, &data))
458     return data;
459
460   if (scale_factor != ui::SCALE_FACTOR_100P) {
461     for (size_t i = 0; i < data_packs_.size(); i++) {
462       if (data_packs_[i]->GetScaleFactor() == scale_factor &&
463           data_packs_[i]->GetStringPiece(resource_id, &data))
464         return data;
465     }
466   }
467   for (size_t i = 0; i < data_packs_.size(); i++) {
468     if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P ||
469          data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P ||
470          data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) &&
471         data_packs_[i]->GetStringPiece(resource_id, &data))
472       return data;
473   }
474
475   return base::StringPiece();
476 }
477
478 base::string16 ResourceBundle::GetLocalizedString(int message_id) {
479   base::string16 string;
480   if (delegate_ && delegate_->GetLocalizedString(message_id, &string))
481     return string;
482
483   // Ensure that ReloadLocaleResources() doesn't drop the resources while
484   // we're using them.
485   base::AutoLock lock_scope(*locale_resources_data_lock_);
486
487   IdToStringMap::const_iterator it =
488       overridden_locale_strings_.find(message_id);
489   if (it != overridden_locale_strings_.end())
490     return it->second;
491
492   // If for some reason we were unable to load the resources , return an empty
493   // string (better than crashing).
494   if (!locale_resources_data_.get()) {
495     LOG(WARNING) << "locale resources are not loaded";
496     return base::string16();
497   }
498
499   base::StringPiece data;
500   if (!locale_resources_data_->GetStringPiece(message_id, &data)) {
501     // Fall back on the main data pack (shouldn't be any strings here except in
502     // unittests).
503     data = GetRawDataResource(message_id);
504     if (data.empty()) {
505       NOTREACHED() << "unable to find resource: " << message_id;
506       return base::string16();
507     }
508   }
509
510   // Strings should not be loaded from a data pack that contains binary data.
511   ResourceHandle::TextEncodingType encoding =
512       locale_resources_data_->GetTextEncodingType();
513   DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8)
514       << "requested localized string from binary pack file";
515
516   // Data pack encodes strings as either UTF8 or UTF16.
517   base::string16 msg;
518   if (encoding == ResourceHandle::UTF16) {
519     msg = base::string16(reinterpret_cast<const base::char16*>(data.data()),
520                          data.length() / 2);
521   } else if (encoding == ResourceHandle::UTF8) {
522     msg = base::UTF8ToUTF16(data);
523   }
524   return msg;
525 }
526
527 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) {
528   {
529     base::AutoLock lock_scope(*images_and_fonts_lock_);
530     LoadFontsIfNecessary();
531   }
532   switch (style) {
533     case BoldFont:
534       return *bold_font_list_;
535     case SmallFont:
536       return *small_font_list_;
537     case MediumFont:
538       return *medium_font_list_;
539     case SmallBoldFont:
540       return *small_bold_font_list_;
541     case MediumBoldFont:
542       return *medium_bold_font_list_;
543     case LargeFont:
544       return *large_font_list_;
545     case LargeBoldFont:
546       return *large_bold_font_list_;
547     default:
548       return *base_font_list_;
549   }
550 }
551
552 const gfx::Font& ResourceBundle::GetFont(FontStyle style) {
553   return GetFontList(style).GetPrimaryFont();
554 }
555
556 void ResourceBundle::ReloadFonts() {
557   base::AutoLock lock_scope(*images_and_fonts_lock_);
558   base_font_list_.reset();
559   LoadFontsIfNecessary();
560 }
561
562 ScaleFactor ResourceBundle::GetMaxScaleFactor() const {
563 #if defined(OS_CHROMEOS) || defined(OS_WIN)
564   return max_scale_factor_;
565 #else
566   return GetSupportedScaleFactors().back();
567 #endif
568 }
569
570 bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) {
571   const std::vector<ScaleFactor>& supported_scale_factors =
572       ui::GetSupportedScaleFactors();
573   return std::find(supported_scale_factors.begin(),
574                    supported_scale_factors.end(),
575                    scale_factor) != supported_scale_factors.end();
576 }
577
578 ResourceBundle::ResourceBundle(Delegate* delegate)
579     : delegate_(delegate),
580       images_and_fonts_lock_(new base::Lock),
581       locale_resources_data_lock_(new base::Lock),
582       max_scale_factor_(SCALE_FACTOR_100P) {
583 }
584
585 ResourceBundle::~ResourceBundle() {
586   FreeImages();
587   UnloadLocaleResources();
588 }
589
590 // static
591 void ResourceBundle::InitSharedInstance(Delegate* delegate) {
592   DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
593   g_shared_instance_ = new ResourceBundle(delegate);
594   static std::vector<ScaleFactor> supported_scale_factors;
595 #if !defined(OS_IOS) && !defined(OS_WIN)
596   // On platforms other than iOS, 100P is always a supported scale factor.
597   // For Windows we have a separate case in this function.
598   supported_scale_factors.push_back(SCALE_FACTOR_100P);
599 #endif
600 #if defined(OS_ANDROID)
601   const gfx::Display display =
602       gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
603   const float display_density = display.device_scale_factor();
604   const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density);
605   if (closest != SCALE_FACTOR_100P)
606     supported_scale_factors.push_back(closest);
607 #elif defined(OS_IOS)
608     gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
609   if (display.device_scale_factor() > 1.0) {
610     DCHECK_EQ(2.0, display.device_scale_factor());
611     supported_scale_factors.push_back(SCALE_FACTOR_200P);
612   } else {
613     supported_scale_factors.push_back(SCALE_FACTOR_100P);
614   }
615 #elif defined(OS_MACOSX)
616   if (base::mac::IsOSLionOrLater())
617     supported_scale_factors.push_back(SCALE_FACTOR_200P);
618 #elif defined(OS_CHROMEOS)
619   // TODO(oshima): Include 200P only if the device support 200P
620   supported_scale_factors.push_back(SCALE_FACTOR_200P);
621 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
622   supported_scale_factors.push_back(SCALE_FACTOR_200P);
623 #elif defined(OS_WIN)
624   bool default_to_100P = true;
625   if (gfx::IsHighDPIEnabled()) {
626     // On Windows if the dpi scale is greater than 1.25 on high dpi machines
627     // downscaling from 200 percent looks better than scaling up from 100
628     // percent.
629     if (gfx::GetDPIScale() > 1.25) {
630       supported_scale_factors.push_back(SCALE_FACTOR_200P);
631       default_to_100P = false;
632     }
633   }
634   if (default_to_100P)
635     supported_scale_factors.push_back(SCALE_FACTOR_100P);
636 #endif
637   ui::SetSupportedScaleFactors(supported_scale_factors);
638 #if defined(OS_WIN)
639   // Must be called _after_ supported scale factors are set since it
640   // uses them.
641   // Don't initialize the device scale factor if it has already been
642   // initialized.
643   if (!gfx::win::IsDeviceScaleFactorSet())
644     ui::win::InitDeviceScaleFactor();
645 #endif
646 }
647
648 void ResourceBundle::FreeImages() {
649   images_.clear();
650 }
651
652 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path,
653                                                  ScaleFactor scale_factor,
654                                                  bool optional) {
655   // Do not pass an empty |path| value to this method. If the absolute path is
656   // unknown pass just the pack file name.
657   DCHECK(!path.empty());
658
659   base::FilePath pack_path = path;
660   if (delegate_)
661     pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor);
662
663   // Don't try to load empty values or values that are not absolute paths.
664   if (pack_path.empty() || !pack_path.IsAbsolute())
665     return;
666
667   scoped_ptr<DataPack> data_pack(
668       new DataPack(scale_factor));
669   if (data_pack->LoadFromPath(pack_path)) {
670     AddDataPack(data_pack.release());
671   } else if (!optional) {
672     LOG(ERROR) << "Failed to load " << pack_path.value()
673                << "\nSome features may not be available.";
674   }
675 }
676
677 void ResourceBundle::AddDataPack(DataPack* data_pack) {
678   data_packs_.push_back(data_pack);
679
680   if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
681       GetScaleForScaleFactor(max_scale_factor_))
682     max_scale_factor_ = data_pack->GetScaleFactor();
683 }
684
685 void ResourceBundle::LoadFontsIfNecessary() {
686   images_and_fonts_lock_->AssertAcquired();
687   if (!base_font_list_.get()) {
688     if (delegate_) {
689       base_font_list_ = GetFontListFromDelegate(BaseFont);
690       bold_font_list_ = GetFontListFromDelegate(BoldFont);
691       small_font_list_ = GetFontListFromDelegate(SmallFont);
692       small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont);
693       medium_font_list_ = GetFontListFromDelegate(MediumFont);
694       medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont);
695       large_font_list_ = GetFontListFromDelegate(LargeFont);
696       large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
697     }
698
699     if (!base_font_list_.get())
700       base_font_list_.reset(new gfx::FontList());
701
702     if (!bold_font_list_.get()) {
703       bold_font_list_.reset(new gfx::FontList());
704       *bold_font_list_ = base_font_list_->DeriveWithStyle(
705           base_font_list_->GetFontStyle() | gfx::Font::BOLD);
706     }
707
708     if (!small_font_list_.get()) {
709       small_font_list_.reset(new gfx::FontList());
710       *small_font_list_ =
711           base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta);
712     }
713
714     if (!small_bold_font_list_.get()) {
715       small_bold_font_list_.reset(new gfx::FontList());
716       *small_bold_font_list_ = small_font_list_->DeriveWithStyle(
717           small_font_list_->GetFontStyle() | gfx::Font::BOLD);
718     }
719
720     if (!medium_font_list_.get()) {
721       medium_font_list_.reset(new gfx::FontList());
722       *medium_font_list_ =
723           base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta);
724     }
725
726     if (!medium_bold_font_list_.get()) {
727       medium_bold_font_list_.reset(new gfx::FontList());
728       *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle(
729           medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
730     }
731
732     if (!large_font_list_.get()) {
733       large_font_list_.reset(new gfx::FontList());
734       *large_font_list_ =
735           base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta);
736     }
737
738     if (!large_bold_font_list_.get()) {
739       large_bold_font_list_.reset(new gfx::FontList());
740       *large_bold_font_list_ = large_font_list_->DeriveWithStyle(
741           large_font_list_->GetFontStyle() | gfx::Font::BOLD);
742     }
743   }
744 }
745
746 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate(
747     FontStyle style) {
748   DCHECK(delegate_);
749   scoped_ptr<gfx::Font> font = delegate_->GetFont(style);
750   if (font.get())
751     return scoped_ptr<gfx::FontList>(new gfx::FontList(*font));
752   return scoped_ptr<gfx::FontList>();
753 }
754
755 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle,
756                                 int resource_id,
757                                 SkBitmap* bitmap,
758                                 bool* fell_back_to_1x) const {
759   DCHECK(fell_back_to_1x);
760   scoped_refptr<base::RefCountedMemory> memory(
761       data_handle.GetStaticMemory(resource_id));
762   if (!memory.get())
763     return false;
764
765   if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x))
766     return true;
767
768 #if !defined(OS_IOS)
769   // iOS does not compile or use the JPEG codec.  On other platforms,
770   // 99% of our assets are PNGs, however fallback to JPEG.
771   scoped_ptr<SkBitmap> jpeg_bitmap(
772       gfx::JPEGCodec::Decode(memory->front(), memory->size()));
773   if (jpeg_bitmap.get()) {
774     bitmap->swap(*jpeg_bitmap.get());
775     *fell_back_to_1x = false;
776     return true;
777   }
778 #endif
779
780   NOTREACHED() << "Unable to decode theme image resource " << resource_id;
781   return false;
782 }
783
784 bool ResourceBundle::LoadBitmap(int resource_id,
785                                 ScaleFactor* scale_factor,
786                                 SkBitmap* bitmap,
787                                 bool* fell_back_to_1x) const {
788   DCHECK(fell_back_to_1x);
789   for (size_t i = 0; i < data_packs_.size(); ++i) {
790     if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
791         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
792       DCHECK(!*fell_back_to_1x);
793       *scale_factor = ui::SCALE_FACTOR_NONE;
794       return true;
795     }
796     if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
797         LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
798       return true;
799     }
800   }
801   return false;
802 }
803
804 gfx::Image& ResourceBundle::GetEmptyImage() {
805   base::AutoLock lock(*images_and_fonts_lock_);
806
807   if (empty_image_.IsEmpty()) {
808     // The placeholder bitmap is bright red so people notice the problem.
809     SkBitmap bitmap;
810     bitmap.allocN32Pixels(32, 32);
811     bitmap.eraseARGB(255, 255, 0, 0);
812     empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
813   }
814   return empty_image_;
815 }
816
817 // static
818 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
819                                                size_t size) {
820   if (size < arraysize(kPngMagic) ||
821       memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
822     // Data invalid or a JPEG.
823     return false;
824   }
825   size_t pos = arraysize(kPngMagic);
826
827   // Scan for custom chunks until we find one, find the IDAT chunk, or run out
828   // of chunks.
829   for (;;) {
830     if (size - pos < kPngChunkMetadataSize)
831       break;
832     uint32 length = 0;
833     base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
834     if (size - pos - kPngChunkMetadataSize < length)
835       break;
836     if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,
837                               arraysize(kPngScaleChunkType)) == 0) {
838       return true;
839     }
840     if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType,
841                arraysize(kPngDataChunkType)) == 0) {
842       // Stop looking for custom chunks, any custom chunks should be before an
843       // IDAT chunk.
844       break;
845     }
846     pos += length + kPngChunkMetadataSize;
847   }
848   return false;
849 }
850
851 // static
852 bool ResourceBundle::DecodePNG(const unsigned char* buf,
853                                size_t size,
854                                SkBitmap* bitmap,
855                                bool* fell_back_to_1x) {
856   *fell_back_to_1x = PNGContainsFallbackMarker(buf, size);
857   return gfx::PNGCodec::Decode(buf, size, bitmap);
858 }
859
860 }  // namespace ui