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