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