Durable Storage: Refactor browser test and test the basic "deny" flow.
[chromium-blink-merge.git] / ui / base / resource / resource_bundle.cc
blobe8aff7b7141642c765a54ed295c29e8e0b5acf6a
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/gfx/platform_font_linux.h"
45 #endif
47 #if defined(OS_WIN)
48 #include "ui/gfx/win/dpi.h"
49 #endif
51 #if defined(OS_MACOSX) && !defined(OS_IOS)
52 #include "base/mac/mac_util.h"
53 #endif
55 namespace ui {
57 namespace {
59 // Font sizes relative to base font.
60 const int kSmallFontSizeDelta = -1;
61 const int kMediumFontSizeDelta = 3;
62 const int kLargeFontSizeDelta = 8;
64 // PNG-related constants.
65 const unsigned char kPngMagic[8] = { 0x89, 'P', 'N', 'G', 13, 10, 26, 10 };
66 const size_t kPngChunkMetadataSize = 12; // length, type, crc32
67 const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' };
68 const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' };
70 #if !defined(OS_MACOSX)
71 const char kPakFileSuffix[] = ".pak";
72 #endif
74 ResourceBundle* g_shared_instance_ = NULL;
76 #if defined(OS_ANDROID)
77 // Returns the scale factor closest to |scale| from the full list of factors.
78 // Note that it does NOT rely on the list of supported scale factors.
79 // Finding the closest match is inefficient and shouldn't be done frequently.
80 ScaleFactor FindClosestScaleFactorUnsafe(float scale) {
81 float smallest_diff = std::numeric_limits<float>::max();
82 ScaleFactor closest_match = SCALE_FACTOR_100P;
83 for (int i = SCALE_FACTOR_100P; i < NUM_SCALE_FACTORS; ++i) {
84 const ScaleFactor scale_factor = static_cast<ScaleFactor>(i);
85 float diff = std::abs(GetScaleForScaleFactor(scale_factor) - scale);
86 if (diff < smallest_diff) {
87 closest_match = scale_factor;
88 smallest_diff = diff;
91 return closest_match;
93 #endif // OS_ANDROID
95 } // namespace
97 // An ImageSkiaSource that loads bitmaps for the requested scale factor from
98 // ResourceBundle on demand for a given |resource_id|. If the bitmap for the
99 // requested scale factor does not exist, it will return the 1x bitmap scaled
100 // by the scale factor. This may lead to broken UI if the correct size of the
101 // scaled image is not exactly |scale_factor| * the size of the 1x resource.
102 // When --highlight-missing-scaled-resources flag is specified, scaled 1x images
103 // are higlighted by blending them with red.
104 class ResourceBundle::ResourceBundleImageSource : public gfx::ImageSkiaSource {
105 public:
106 ResourceBundleImageSource(ResourceBundle* rb, int resource_id)
107 : rb_(rb), resource_id_(resource_id) {}
108 ~ResourceBundleImageSource() override {}
110 // gfx::ImageSkiaSource overrides:
111 gfx::ImageSkiaRep GetImageForScale(float scale) override {
112 SkBitmap image;
113 bool fell_back_to_1x = false;
114 ScaleFactor scale_factor = GetSupportedScaleFactor(scale);
115 bool found = rb_->LoadBitmap(resource_id_, &scale_factor,
116 &image, &fell_back_to_1x);
117 if (!found)
118 return gfx::ImageSkiaRep();
120 // If the resource is in the package with SCALE_FACTOR_NONE, it
121 // can be used in any scale factor. The image is maked as "unscaled"
122 // so that the ImageSkia do not automatically scale.
123 if (scale_factor == ui::SCALE_FACTOR_NONE)
124 return gfx::ImageSkiaRep(image, 0.0f);
126 if (fell_back_to_1x) {
127 // GRIT fell back to the 100% image, so rescale it to the correct size.
128 image = skia::ImageOperations::Resize(
129 image,
130 skia::ImageOperations::RESIZE_LANCZOS3,
131 gfx::ToCeiledInt(image.width() * scale),
132 gfx::ToCeiledInt(image.height() * scale));
133 } else {
134 scale = GetScaleForScaleFactor(scale_factor);
136 return gfx::ImageSkiaRep(image, scale);
139 private:
140 ResourceBundle* rb_;
141 const int resource_id_;
143 DISALLOW_COPY_AND_ASSIGN(ResourceBundleImageSource);
146 // static
147 std::string ResourceBundle::InitSharedInstanceWithLocale(
148 const std::string& pref_locale,
149 Delegate* delegate,
150 LoadResources load_resources) {
151 InitSharedInstance(delegate);
152 if (load_resources == LOAD_COMMON_RESOURCES)
153 g_shared_instance_->LoadCommonResources();
154 std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
155 g_shared_instance_->InitDefaultFontList();
156 return result;
159 // static
160 void ResourceBundle::InitSharedInstanceWithPakFileRegion(
161 base::File pak_file,
162 const base::MemoryMappedFile::Region& region) {
163 InitSharedInstance(NULL);
164 scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P));
165 if (!data_pack->LoadFromFileRegion(pak_file.Pass(), region)) {
166 NOTREACHED() << "failed to load pak file";
167 return;
169 g_shared_instance_->locale_resources_data_.reset(data_pack.release());
170 g_shared_instance_->InitDefaultFontList();
173 // static
174 void ResourceBundle::InitSharedInstanceWithPakPath(const base::FilePath& path) {
175 InitSharedInstance(NULL);
176 g_shared_instance_->LoadTestResources(path, path);
178 g_shared_instance_->InitDefaultFontList();
181 // static
182 void ResourceBundle::CleanupSharedInstance() {
183 if (g_shared_instance_) {
184 delete g_shared_instance_;
185 g_shared_instance_ = NULL;
189 // static
190 bool ResourceBundle::HasSharedInstance() {
191 return g_shared_instance_ != NULL;
194 // static
195 ResourceBundle& ResourceBundle::GetSharedInstance() {
196 // Must call InitSharedInstance before this function.
197 CHECK(g_shared_instance_ != NULL);
198 return *g_shared_instance_;
201 #if !defined(OS_ANDROID)
202 bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
203 return !GetLocaleFilePath(locale, true).empty();
205 #endif // !defined(OS_ANDROID)
207 void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
208 ScaleFactor scale_factor) {
209 AddDataPackFromPathInternal(path, scale_factor, false, false);
212 void ResourceBundle::AddOptionalDataPackFromPath(const base::FilePath& path,
213 ScaleFactor scale_factor) {
214 AddDataPackFromPathInternal(path, scale_factor, true, false);
217 void ResourceBundle::AddMaterialDesignDataPackFromPath(
218 const base::FilePath& path,
219 ScaleFactor scale_factor) {
220 AddDataPackFromPathInternal(path, scale_factor, false, true);
223 void ResourceBundle::AddOptionalMaterialDesignDataPackFromPath(
224 const base::FilePath& path,
225 ScaleFactor scale_factor) {
226 AddDataPackFromPathInternal(path, scale_factor, true, true);
229 void ResourceBundle::AddDataPackFromFile(base::File file,
230 ScaleFactor scale_factor) {
231 AddDataPackFromFileRegion(
232 file.Pass(), base::MemoryMappedFile::Region::kWholeFile, scale_factor);
235 void ResourceBundle::AddDataPackFromFileRegion(
236 base::File file,
237 const base::MemoryMappedFile::Region& region,
238 ScaleFactor scale_factor) {
239 scoped_ptr<DataPack> data_pack(
240 new DataPack(scale_factor));
241 if (data_pack->LoadFromFileRegion(file.Pass(), region)) {
242 AddDataPack(data_pack.release());
243 } else {
244 LOG(ERROR) << "Failed to load data pack from file."
245 << "\nSome features may not be available.";
249 #if !defined(OS_MACOSX)
250 base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale,
251 bool test_file_exists) {
252 if (app_locale.empty())
253 return base::FilePath();
255 base::FilePath locale_file_path;
257 PathService::Get(ui::DIR_LOCALES, &locale_file_path);
259 if (!locale_file_path.empty()) {
260 locale_file_path =
261 locale_file_path.AppendASCII(app_locale + kPakFileSuffix);
264 if (delegate_) {
265 locale_file_path =
266 delegate_->GetPathForLocalePack(locale_file_path, app_locale);
269 // Don't try to load empty values or values that are not absolute paths.
270 if (locale_file_path.empty() || !locale_file_path.IsAbsolute())
271 return base::FilePath();
273 if (test_file_exists && !base::PathExists(locale_file_path))
274 return base::FilePath();
276 return locale_file_path;
278 #endif
280 #if !defined(OS_ANDROID)
281 std::string ResourceBundle::LoadLocaleResources(
282 const std::string& pref_locale) {
283 DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded";
284 std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);
285 base::FilePath locale_file_path = GetOverriddenPakPath();
286 if (locale_file_path.empty())
287 locale_file_path = GetLocaleFilePath(app_locale, true);
289 if (locale_file_path.empty()) {
290 // It's possible that there is no locale.pak.
291 LOG(WARNING) << "locale_file_path.empty() for locale " << app_locale;
292 return std::string();
295 scoped_ptr<DataPack> data_pack(
296 new DataPack(SCALE_FACTOR_100P));
297 if (!data_pack->LoadFromPath(locale_file_path)) {
298 UMA_HISTOGRAM_ENUMERATION("ResourceBundle.LoadLocaleResourcesError",
299 logging::GetLastSystemErrorCode(), 16000);
300 LOG(ERROR) << "failed to load locale.pak";
301 NOTREACHED();
302 return std::string();
305 locale_resources_data_.reset(data_pack.release());
306 return app_locale;
308 #endif // defined(OS_ANDROID)
310 void ResourceBundle::LoadTestResources(const base::FilePath& path,
311 const base::FilePath& locale_path) {
312 DCHECK(!ui::GetSupportedScaleFactors().empty());
313 const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]);
314 // Use the given resource pak for both common and localized resources.
315 scoped_ptr<DataPack> data_pack(new DataPack(scale_factor));
316 if (!path.empty() && data_pack->LoadFromPath(path))
317 AddDataPack(data_pack.release());
319 data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE));
320 if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
321 locale_resources_data_.reset(data_pack.release());
322 } else {
323 locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE));
327 void ResourceBundle::UnloadLocaleResources() {
328 locale_resources_data_.reset();
331 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) {
332 overridden_pak_path_ = pak_path;
335 void ResourceBundle::OverrideLocaleStringResource(
336 int message_id,
337 const base::string16& string) {
338 overridden_locale_strings_[message_id] = string;
341 const base::FilePath& ResourceBundle::GetOverriddenPakPath() {
342 return overridden_pak_path_;
345 std::string ResourceBundle::ReloadLocaleResources(
346 const std::string& pref_locale) {
347 base::AutoLock lock_scope(*locale_resources_data_lock_);
349 // Remove all overriden strings, as they will not be valid for the new locale.
350 overridden_locale_strings_.clear();
352 UnloadLocaleResources();
353 return LoadLocaleResources(pref_locale);
356 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) {
357 const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia();
358 return const_cast<gfx::ImageSkia*>(image);
361 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) {
362 // Check to see if the image is already in the cache.
364 base::AutoLock lock_scope(*images_and_fonts_lock_);
365 if (images_.count(resource_id))
366 return images_[resource_id];
369 gfx::Image image;
370 if (delegate_)
371 image = delegate_->GetImageNamed(resource_id);
373 if (image.IsEmpty()) {
374 DCHECK(!data_packs_.empty()) <<
375 "Missing call to SetResourcesDataDLL?";
377 #if defined(OS_CHROMEOS) || defined(OS_WIN)
378 ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor();
379 #else
380 ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
381 #endif
383 // TODO(oshima): Consider reading the image size from png IHDR chunk and
384 // skip decoding here and remove #ifdef below.
385 // ResourceBundle::GetSharedInstance() is destroyed after the
386 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be
387 // destroyed before the resource bundle is destroyed.
388 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id),
389 GetScaleForScaleFactor(scale_factor_to_load));
390 if (image_skia.isNull()) {
391 LOG(WARNING) << "Unable to load image with id " << resource_id;
392 NOTREACHED(); // Want to assert in debug mode.
393 // The load failed to retrieve the image; show a debugging red square.
394 return GetEmptyImage();
396 image_skia.SetReadOnly();
397 image = gfx::Image(image_skia);
400 // The load was successful, so cache the image.
401 base::AutoLock lock_scope(*images_and_fonts_lock_);
403 // Another thread raced the load and has already cached the image.
404 if (images_.count(resource_id))
405 return images_[resource_id];
407 images_[resource_id] = image;
408 return images_[resource_id];
411 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) {
412 return GetNativeImageNamed(resource_id, RTL_DISABLED);
415 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes(
416 int resource_id) const {
417 return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE);
420 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale(
421 int resource_id,
422 ScaleFactor scale_factor) const {
423 base::RefCountedStaticMemory* bytes = NULL;
424 if (delegate_)
425 bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor);
427 if (!bytes) {
428 base::StringPiece data =
429 GetRawDataResourceForScale(resource_id, scale_factor);
430 if (!data.empty()) {
431 bytes = new base::RefCountedStaticMemory(data.data(), data.length());
435 return bytes;
438 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const {
439 return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE);
442 base::StringPiece ResourceBundle::GetRawDataResourceForScale(
443 int resource_id,
444 ScaleFactor scale_factor) const {
445 base::StringPiece data;
446 if (delegate_ &&
447 delegate_->GetRawDataResource(resource_id, scale_factor, &data))
448 return data;
450 if (scale_factor != ui::SCALE_FACTOR_100P) {
451 for (size_t i = 0; i < data_packs_.size(); i++) {
452 if (data_packs_[i]->GetScaleFactor() == scale_factor &&
453 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id),
454 &data))
455 return data;
459 for (size_t i = 0; i < data_packs_.size(); i++) {
460 if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P ||
461 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P ||
462 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_300P ||
463 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) &&
464 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id),
465 &data))
466 return data;
469 return base::StringPiece();
472 base::string16 ResourceBundle::GetLocalizedString(int message_id) {
473 base::string16 string;
474 if (delegate_ && delegate_->GetLocalizedString(message_id, &string))
475 return string;
477 // Ensure that ReloadLocaleResources() doesn't drop the resources while
478 // we're using them.
479 base::AutoLock lock_scope(*locale_resources_data_lock_);
481 IdToStringMap::const_iterator it =
482 overridden_locale_strings_.find(message_id);
483 if (it != overridden_locale_strings_.end())
484 return it->second;
486 // If for some reason we were unable to load the resources , return an empty
487 // string (better than crashing).
488 if (!locale_resources_data_.get()) {
489 LOG(WARNING) << "locale resources are not loaded";
490 return base::string16();
493 base::StringPiece data;
494 if (!locale_resources_data_->GetStringPiece(static_cast<uint16>(message_id),
495 &data)) {
496 // Fall back on the main data pack (shouldn't be any strings here except in
497 // unittests).
498 data = GetRawDataResource(message_id);
499 if (data.empty()) {
500 NOTREACHED() << "unable to find resource: " << message_id;
501 return base::string16();
505 // Strings should not be loaded from a data pack that contains binary data.
506 ResourceHandle::TextEncodingType encoding =
507 locale_resources_data_->GetTextEncodingType();
508 DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8)
509 << "requested localized string from binary pack file";
511 // Data pack encodes strings as either UTF8 or UTF16.
512 base::string16 msg;
513 if (encoding == ResourceHandle::UTF16) {
514 msg = base::string16(reinterpret_cast<const base::char16*>(data.data()),
515 data.length() / 2);
516 } else if (encoding == ResourceHandle::UTF8) {
517 msg = base::UTF8ToUTF16(data);
519 return msg;
522 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) {
524 base::AutoLock lock_scope(*images_and_fonts_lock_);
525 LoadFontsIfNecessary();
527 switch (style) {
528 case BoldFont:
529 return *bold_font_list_;
530 case SmallFont:
531 return *small_font_list_;
532 case MediumFont:
533 return *medium_font_list_;
534 case SmallBoldFont:
535 return *small_bold_font_list_;
536 case MediumBoldFont:
537 return *medium_bold_font_list_;
538 case LargeFont:
539 return *large_font_list_;
540 case LargeBoldFont:
541 return *large_bold_font_list_;
542 default:
543 return *base_font_list_;
547 const gfx::Font& ResourceBundle::GetFont(FontStyle style) {
548 return GetFontList(style).GetPrimaryFont();
551 void ResourceBundle::ReloadFonts() {
552 base::AutoLock lock_scope(*images_and_fonts_lock_);
553 InitDefaultFontList();
554 base_font_list_.reset();
555 LoadFontsIfNecessary();
558 ScaleFactor ResourceBundle::GetMaxScaleFactor() const {
559 #if defined(OS_CHROMEOS) || defined(OS_WIN)
560 return max_scale_factor_;
561 #else
562 return GetSupportedScaleFactors().back();
563 #endif
566 bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) {
567 const std::vector<ScaleFactor>& supported_scale_factors =
568 ui::GetSupportedScaleFactors();
569 return std::find(supported_scale_factors.begin(),
570 supported_scale_factors.end(),
571 scale_factor) != supported_scale_factors.end();
574 ResourceBundle::ResourceBundle(Delegate* delegate)
575 : delegate_(delegate),
576 images_and_fonts_lock_(new base::Lock),
577 locale_resources_data_lock_(new base::Lock),
578 max_scale_factor_(SCALE_FACTOR_100P) {
581 ResourceBundle::~ResourceBundle() {
582 FreeImages();
583 UnloadLocaleResources();
586 // static
587 void ResourceBundle::InitSharedInstance(Delegate* delegate) {
588 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
589 g_shared_instance_ = new ResourceBundle(delegate);
590 static std::vector<ScaleFactor> supported_scale_factors;
591 #if !defined(OS_IOS) && !defined(OS_WIN)
592 // On platforms other than iOS, 100P is always a supported scale factor.
593 // For Windows we have a separate case in this function.
594 supported_scale_factors.push_back(SCALE_FACTOR_100P);
595 #endif
596 #if defined(OS_ANDROID)
597 const gfx::Display display =
598 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
599 const float display_density = display.device_scale_factor();
600 const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density);
601 if (closest != SCALE_FACTOR_100P)
602 supported_scale_factors.push_back(closest);
603 #elif defined(OS_IOS)
604 gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
605 if (display.device_scale_factor() > 2.0) {
606 DCHECK_EQ(3.0, display.device_scale_factor());
607 supported_scale_factors.push_back(SCALE_FACTOR_300P);
608 } else if (display.device_scale_factor() > 1.0) {
609 DCHECK_EQ(2.0, display.device_scale_factor());
610 supported_scale_factors.push_back(SCALE_FACTOR_200P);
611 } else {
612 supported_scale_factors.push_back(SCALE_FACTOR_100P);
614 #elif defined(OS_MACOSX)
615 if (base::mac::IsOSLionOrLater())
616 supported_scale_factors.push_back(SCALE_FACTOR_200P);
617 #elif defined(OS_CHROMEOS)
618 // TODO(oshima): Include 200P only if the device support 200P
619 supported_scale_factors.push_back(SCALE_FACTOR_200P);
620 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
621 supported_scale_factors.push_back(SCALE_FACTOR_200P);
622 #elif defined(OS_WIN)
623 bool default_to_100P = true;
624 // On Windows if the dpi scale is greater than 1.25 on high dpi machines
625 // downscaling from 200 percent looks better than scaling up from 100
626 // percent.
627 if (gfx::GetDPIScale() > 1.25) {
628 supported_scale_factors.push_back(SCALE_FACTOR_200P);
629 default_to_100P = false;
631 if (default_to_100P)
632 supported_scale_factors.push_back(SCALE_FACTOR_100P);
633 #endif
634 ui::SetSupportedScaleFactors(supported_scale_factors);
635 #if defined(OS_WIN)
636 // Must be called _after_ supported scale factors are set since it
637 // uses them.
638 gfx::InitDeviceScaleFactor(gfx::GetDPIScale());
639 #endif
642 void ResourceBundle::FreeImages() {
643 images_.clear();
646 void ResourceBundle::AddDataPackFromPathInternal(
647 const base::FilePath& path,
648 ScaleFactor scale_factor,
649 bool optional,
650 bool has_only_material_assets) {
651 // Do not pass an empty |path| value to this method. If the absolute path is
652 // unknown pass just the pack file name.
653 DCHECK(!path.empty());
655 base::FilePath pack_path = path;
656 if (delegate_)
657 pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor);
659 // Don't try to load empty values or values that are not absolute paths.
660 if (pack_path.empty() || !pack_path.IsAbsolute())
661 return;
663 scoped_ptr<DataPack> data_pack(new DataPack(scale_factor));
664 data_pack->set_has_only_material_design_assets(has_only_material_assets);
665 if (data_pack->LoadFromPath(pack_path)) {
666 AddDataPack(data_pack.release());
667 } else if (!optional) {
668 LOG(ERROR) << "Failed to load " << pack_path.value()
669 << "\nSome features may not be available.";
673 void ResourceBundle::AddDataPack(DataPack* data_pack) {
674 #if DCHECK_IS_ON()
675 data_pack->CheckForDuplicateResources(data_packs_);
676 #endif
677 data_packs_.push_back(data_pack);
679 if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
680 GetScaleForScaleFactor(max_scale_factor_))
681 max_scale_factor_ = data_pack->GetScaleFactor();
684 void ResourceBundle::InitDefaultFontList() {
685 #if defined(OS_CHROMEOS)
686 std::string font_family = base::UTF16ToUTF8(
687 GetLocalizedString(IDS_UI_FONT_FAMILY_CROS));
688 gfx::FontList::SetDefaultFontDescription(font_family);
690 // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to
691 // the font list is done. We will no longer need SetDefaultFontDescription()
692 // after every client gets started using a FontList instead of a Font.
693 gfx::PlatformFontLinux::SetDefaultFontDescription(font_family);
694 #else
695 // Use a single default font as the default font list.
696 gfx::FontList::SetDefaultFontDescription(std::string());
697 #endif
700 void ResourceBundle::LoadFontsIfNecessary() {
701 images_and_fonts_lock_->AssertAcquired();
702 if (!base_font_list_.get()) {
703 if (delegate_) {
704 base_font_list_ = GetFontListFromDelegate(BaseFont);
705 bold_font_list_ = GetFontListFromDelegate(BoldFont);
706 small_font_list_ = GetFontListFromDelegate(SmallFont);
707 small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont);
708 medium_font_list_ = GetFontListFromDelegate(MediumFont);
709 medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont);
710 large_font_list_ = GetFontListFromDelegate(LargeFont);
711 large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
714 if (!base_font_list_.get())
715 base_font_list_.reset(new gfx::FontList());
717 if (!bold_font_list_.get()) {
718 bold_font_list_.reset(new gfx::FontList());
719 *bold_font_list_ = base_font_list_->DeriveWithStyle(
720 base_font_list_->GetFontStyle() | gfx::Font::BOLD);
723 if (!small_font_list_.get()) {
724 small_font_list_.reset(new gfx::FontList());
725 *small_font_list_ =
726 base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta);
729 if (!small_bold_font_list_.get()) {
730 small_bold_font_list_.reset(new gfx::FontList());
731 *small_bold_font_list_ = small_font_list_->DeriveWithStyle(
732 small_font_list_->GetFontStyle() | gfx::Font::BOLD);
735 if (!medium_font_list_.get()) {
736 medium_font_list_.reset(new gfx::FontList());
737 *medium_font_list_ =
738 base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta);
741 if (!medium_bold_font_list_.get()) {
742 medium_bold_font_list_.reset(new gfx::FontList());
743 *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle(
744 medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
747 if (!large_font_list_.get()) {
748 large_font_list_.reset(new gfx::FontList());
749 *large_font_list_ =
750 base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta);
753 if (!large_bold_font_list_.get()) {
754 large_bold_font_list_.reset(new gfx::FontList());
755 *large_bold_font_list_ = large_font_list_->DeriveWithStyle(
756 large_font_list_->GetFontStyle() | gfx::Font::BOLD);
761 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate(
762 FontStyle style) {
763 DCHECK(delegate_);
764 scoped_ptr<gfx::Font> font = delegate_->GetFont(style);
765 if (font.get())
766 return make_scoped_ptr(new gfx::FontList(*font));
767 return nullptr;
770 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle,
771 int resource_id,
772 SkBitmap* bitmap,
773 bool* fell_back_to_1x) const {
774 DCHECK(fell_back_to_1x);
775 scoped_refptr<base::RefCountedMemory> memory(
776 data_handle.GetStaticMemory(static_cast<uint16>(resource_id)));
777 if (!memory.get())
778 return false;
780 if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x))
781 return true;
783 #if !defined(OS_IOS)
784 // iOS does not compile or use the JPEG codec. On other platforms,
785 // 99% of our assets are PNGs, however fallback to JPEG.
786 scoped_ptr<SkBitmap> jpeg_bitmap(
787 gfx::JPEGCodec::Decode(memory->front(), memory->size()));
788 if (jpeg_bitmap.get()) {
789 bitmap->swap(*jpeg_bitmap.get());
790 *fell_back_to_1x = false;
791 return true;
793 #endif
795 NOTREACHED() << "Unable to decode theme image resource " << resource_id;
796 return false;
799 bool ResourceBundle::LoadBitmap(int resource_id,
800 ScaleFactor* scale_factor,
801 SkBitmap* bitmap,
802 bool* fell_back_to_1x) const {
803 DCHECK(fell_back_to_1x);
804 for (size_t i = 0; i < data_packs_.size(); ++i) {
805 if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
806 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
807 DCHECK(!*fell_back_to_1x);
808 *scale_factor = ui::SCALE_FACTOR_NONE;
809 return true;
811 if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
812 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
813 return true;
816 return false;
819 gfx::Image& ResourceBundle::GetEmptyImage() {
820 base::AutoLock lock(*images_and_fonts_lock_);
822 if (empty_image_.IsEmpty()) {
823 // The placeholder bitmap is bright red so people notice the problem.
824 SkBitmap bitmap;
825 bitmap.allocN32Pixels(32, 32);
826 bitmap.eraseARGB(255, 255, 0, 0);
827 empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
829 return empty_image_;
832 // static
833 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
834 size_t size) {
835 if (size < arraysize(kPngMagic) ||
836 memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
837 // Data invalid or a JPEG.
838 return false;
840 size_t pos = arraysize(kPngMagic);
842 // Scan for custom chunks until we find one, find the IDAT chunk, or run out
843 // of chunks.
844 for (;;) {
845 if (size - pos < kPngChunkMetadataSize)
846 break;
847 uint32 length = 0;
848 base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
849 if (size - pos - kPngChunkMetadataSize < length)
850 break;
851 if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,
852 arraysize(kPngScaleChunkType)) == 0) {
853 return true;
855 if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType,
856 arraysize(kPngDataChunkType)) == 0) {
857 // Stop looking for custom chunks, any custom chunks should be before an
858 // IDAT chunk.
859 break;
861 pos += length + kPngChunkMetadataSize;
863 return false;
866 // static
867 bool ResourceBundle::DecodePNG(const unsigned char* buf,
868 size_t size,
869 SkBitmap* bitmap,
870 bool* fell_back_to_1x) {
871 *fell_back_to_1x = PNGContainsFallbackMarker(buf, size);
872 return gfx::PNGCodec::Decode(buf, size, bitmap);
875 } // namespace ui