| OLD | NEW |
| (Empty) |
| 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/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/image/image_skia.h" | |
| 33 #include "ui/gfx/image/image_skia_source.h" | |
| 34 #include "ui/gfx/safe_integer_conversions.h" | |
| 35 #include "ui/gfx/screen.h" | |
| 36 #include "ui/gfx/size_conversions.h" | |
| 37 #include "ui/strings/grit/app_locale_settings.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 ~ResourceBundleImageSource() override {} | |
| 128 | |
| 129 // gfx::ImageSkiaSource overrides: | |
| 130 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 DCHECK(!ui::GetSupportedScaleFactors().empty()); | |
| 325 const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]); | |
| 326 // Use the given resource pak for both common and localized resources. | |
| 327 scoped_ptr<DataPack> data_pack(new DataPack(scale_factor)); | |
| 328 if (!path.empty() && data_pack->LoadFromPath(path)) | |
| 329 AddDataPack(data_pack.release()); | |
| 330 | |
| 331 data_pack.reset(new DataPack(ui::SCALE_FACTOR_NONE)); | |
| 332 if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) { | |
| 333 locale_resources_data_.reset(data_pack.release()); | |
| 334 } else { | |
| 335 locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE)); | |
| 336 } | |
| 337 } | |
| 338 | |
| 339 void ResourceBundle::UnloadLocaleResources() { | |
| 340 locale_resources_data_.reset(); | |
| 341 } | |
| 342 | |
| 343 void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) { | |
| 344 overridden_pak_path_ = pak_path; | |
| 345 } | |
| 346 | |
| 347 void ResourceBundle::OverrideLocaleStringResource( | |
| 348 int message_id, | |
| 349 const base::string16& string) { | |
| 350 overridden_locale_strings_[message_id] = string; | |
| 351 } | |
| 352 | |
| 353 const base::FilePath& ResourceBundle::GetOverriddenPakPath() { | |
| 354 return overridden_pak_path_; | |
| 355 } | |
| 356 | |
| 357 std::string ResourceBundle::ReloadLocaleResources( | |
| 358 const std::string& pref_locale) { | |
| 359 base::AutoLock lock_scope(*locale_resources_data_lock_); | |
| 360 | |
| 361 // Remove all overriden strings, as they will not be valid for the new locale. | |
| 362 overridden_locale_strings_.clear(); | |
| 363 | |
| 364 UnloadLocaleResources(); | |
| 365 return LoadLocaleResources(pref_locale); | |
| 366 } | |
| 367 | |
| 368 gfx::ImageSkia* ResourceBundle::GetImageSkiaNamed(int resource_id) { | |
| 369 const gfx::ImageSkia* image = GetImageNamed(resource_id).ToImageSkia(); | |
| 370 return const_cast<gfx::ImageSkia*>(image); | |
| 371 } | |
| 372 | |
| 373 gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { | |
| 374 // Check to see if the image is already in the cache. | |
| 375 { | |
| 376 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
| 377 if (images_.count(resource_id)) | |
| 378 return images_[resource_id]; | |
| 379 } | |
| 380 | |
| 381 gfx::Image image; | |
| 382 if (delegate_) | |
| 383 image = delegate_->GetImageNamed(resource_id); | |
| 384 | |
| 385 if (image.IsEmpty()) { | |
| 386 DCHECK(!data_packs_.empty()) << | |
| 387 "Missing call to SetResourcesDataDLL?"; | |
| 388 | |
| 389 #if defined(OS_CHROMEOS) || defined(OS_WIN) | |
| 390 ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor(); | |
| 391 #else | |
| 392 ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P; | |
| 393 #endif | |
| 394 | |
| 395 // TODO(oshima): Consider reading the image size from png IHDR chunk and | |
| 396 // skip decoding here and remove #ifdef below. | |
| 397 // ResourceBundle::GetSharedInstance() is destroyed after the | |
| 398 // BrowserMainLoop has finished running. |image_skia| is guaranteed to be | |
| 399 // destroyed before the resource bundle is destroyed. | |
| 400 gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id), | |
| 401 GetScaleForScaleFactor(scale_factor_to_load)); | |
| 402 if (image_skia.isNull()) { | |
| 403 LOG(WARNING) << "Unable to load image with id " << resource_id; | |
| 404 NOTREACHED(); // Want to assert in debug mode. | |
| 405 // The load failed to retrieve the image; show a debugging red square. | |
| 406 return GetEmptyImage(); | |
| 407 } | |
| 408 image_skia.SetReadOnly(); | |
| 409 image = gfx::Image(image_skia); | |
| 410 } | |
| 411 | |
| 412 // The load was successful, so cache the image. | |
| 413 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
| 414 | |
| 415 // Another thread raced the load and has already cached the image. | |
| 416 if (images_.count(resource_id)) | |
| 417 return images_[resource_id]; | |
| 418 | |
| 419 images_[resource_id] = image; | |
| 420 return images_[resource_id]; | |
| 421 } | |
| 422 | |
| 423 gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { | |
| 424 return GetNativeImageNamed(resource_id, RTL_DISABLED); | |
| 425 } | |
| 426 | |
| 427 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes( | |
| 428 int resource_id) const { | |
| 429 return LoadDataResourceBytesForScale(resource_id, ui::SCALE_FACTOR_NONE); | |
| 430 } | |
| 431 | |
| 432 base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytesForScale( | |
| 433 int resource_id, | |
| 434 ScaleFactor scale_factor) const { | |
| 435 base::RefCountedStaticMemory* bytes = NULL; | |
| 436 if (delegate_) | |
| 437 bytes = delegate_->LoadDataResourceBytes(resource_id, scale_factor); | |
| 438 | |
| 439 if (!bytes) { | |
| 440 base::StringPiece data = | |
| 441 GetRawDataResourceForScale(resource_id, scale_factor); | |
| 442 if (!data.empty()) { | |
| 443 bytes = new base::RefCountedStaticMemory(data.data(), data.length()); | |
| 444 } | |
| 445 } | |
| 446 | |
| 447 return bytes; | |
| 448 } | |
| 449 | |
| 450 base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const { | |
| 451 return GetRawDataResourceForScale(resource_id, ui::SCALE_FACTOR_NONE); | |
| 452 } | |
| 453 | |
| 454 base::StringPiece ResourceBundle::GetRawDataResourceForScale( | |
| 455 int resource_id, | |
| 456 ScaleFactor scale_factor) const { | |
| 457 base::StringPiece data; | |
| 458 if (delegate_ && | |
| 459 delegate_->GetRawDataResource(resource_id, scale_factor, &data)) | |
| 460 return data; | |
| 461 | |
| 462 if (scale_factor != ui::SCALE_FACTOR_100P) { | |
| 463 for (size_t i = 0; i < data_packs_.size(); i++) { | |
| 464 if (data_packs_[i]->GetScaleFactor() == scale_factor && | |
| 465 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id), | |
| 466 &data)) | |
| 467 return data; | |
| 468 } | |
| 469 } | |
| 470 for (size_t i = 0; i < data_packs_.size(); i++) { | |
| 471 if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P || | |
| 472 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P || | |
| 473 data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) && | |
| 474 data_packs_[i]->GetStringPiece(static_cast<uint16>(resource_id), | |
| 475 &data)) | |
| 476 return data; | |
| 477 } | |
| 478 | |
| 479 return base::StringPiece(); | |
| 480 } | |
| 481 | |
| 482 base::string16 ResourceBundle::GetLocalizedString(int message_id) { | |
| 483 base::string16 string; | |
| 484 if (delegate_ && delegate_->GetLocalizedString(message_id, &string)) | |
| 485 return string; | |
| 486 | |
| 487 // Ensure that ReloadLocaleResources() doesn't drop the resources while | |
| 488 // we're using them. | |
| 489 base::AutoLock lock_scope(*locale_resources_data_lock_); | |
| 490 | |
| 491 IdToStringMap::const_iterator it = | |
| 492 overridden_locale_strings_.find(message_id); | |
| 493 if (it != overridden_locale_strings_.end()) | |
| 494 return it->second; | |
| 495 | |
| 496 // If for some reason we were unable to load the resources , return an empty | |
| 497 // string (better than crashing). | |
| 498 if (!locale_resources_data_.get()) { | |
| 499 LOG(WARNING) << "locale resources are not loaded"; | |
| 500 return base::string16(); | |
| 501 } | |
| 502 | |
| 503 base::StringPiece data; | |
| 504 if (!locale_resources_data_->GetStringPiece(static_cast<uint16>(message_id), | |
| 505 &data)) { | |
| 506 // Fall back on the main data pack (shouldn't be any strings here except in | |
| 507 // unittests). | |
| 508 data = GetRawDataResource(message_id); | |
| 509 if (data.empty()) { | |
| 510 NOTREACHED() << "unable to find resource: " << message_id; | |
| 511 return base::string16(); | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 // Strings should not be loaded from a data pack that contains binary data. | |
| 516 ResourceHandle::TextEncodingType encoding = | |
| 517 locale_resources_data_->GetTextEncodingType(); | |
| 518 DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) | |
| 519 << "requested localized string from binary pack file"; | |
| 520 | |
| 521 // Data pack encodes strings as either UTF8 or UTF16. | |
| 522 base::string16 msg; | |
| 523 if (encoding == ResourceHandle::UTF16) { | |
| 524 msg = base::string16(reinterpret_cast<const base::char16*>(data.data()), | |
| 525 data.length() / 2); | |
| 526 } else if (encoding == ResourceHandle::UTF8) { | |
| 527 msg = base::UTF8ToUTF16(data); | |
| 528 } | |
| 529 return msg; | |
| 530 } | |
| 531 | |
| 532 const gfx::FontList& ResourceBundle::GetFontList(FontStyle style) { | |
| 533 { | |
| 534 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
| 535 LoadFontsIfNecessary(); | |
| 536 } | |
| 537 switch (style) { | |
| 538 case BoldFont: | |
| 539 return *bold_font_list_; | |
| 540 case SmallFont: | |
| 541 return *small_font_list_; | |
| 542 case MediumFont: | |
| 543 return *medium_font_list_; | |
| 544 case SmallBoldFont: | |
| 545 return *small_bold_font_list_; | |
| 546 case MediumBoldFont: | |
| 547 return *medium_bold_font_list_; | |
| 548 case LargeFont: | |
| 549 return *large_font_list_; | |
| 550 case LargeBoldFont: | |
| 551 return *large_bold_font_list_; | |
| 552 default: | |
| 553 return *base_font_list_; | |
| 554 } | |
| 555 } | |
| 556 | |
| 557 const gfx::Font& ResourceBundle::GetFont(FontStyle style) { | |
| 558 return GetFontList(style).GetPrimaryFont(); | |
| 559 } | |
| 560 | |
| 561 void ResourceBundle::ReloadFonts() { | |
| 562 base::AutoLock lock_scope(*images_and_fonts_lock_); | |
| 563 base_font_list_.reset(); | |
| 564 LoadFontsIfNecessary(); | |
| 565 } | |
| 566 | |
| 567 ScaleFactor ResourceBundle::GetMaxScaleFactor() const { | |
| 568 #if defined(OS_CHROMEOS) || defined(OS_WIN) | |
| 569 return max_scale_factor_; | |
| 570 #else | |
| 571 return GetSupportedScaleFactors().back(); | |
| 572 #endif | |
| 573 } | |
| 574 | |
| 575 bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) { | |
| 576 const std::vector<ScaleFactor>& supported_scale_factors = | |
| 577 ui::GetSupportedScaleFactors(); | |
| 578 return std::find(supported_scale_factors.begin(), | |
| 579 supported_scale_factors.end(), | |
| 580 scale_factor) != supported_scale_factors.end(); | |
| 581 } | |
| 582 | |
| 583 ResourceBundle::ResourceBundle(Delegate* delegate) | |
| 584 : delegate_(delegate), | |
| 585 images_and_fonts_lock_(new base::Lock), | |
| 586 locale_resources_data_lock_(new base::Lock), | |
| 587 max_scale_factor_(SCALE_FACTOR_100P) { | |
| 588 } | |
| 589 | |
| 590 ResourceBundle::~ResourceBundle() { | |
| 591 FreeImages(); | |
| 592 UnloadLocaleResources(); | |
| 593 } | |
| 594 | |
| 595 // static | |
| 596 void ResourceBundle::InitSharedInstance(Delegate* delegate) { | |
| 597 DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; | |
| 598 g_shared_instance_ = new ResourceBundle(delegate); | |
| 599 static std::vector<ScaleFactor> supported_scale_factors; | |
| 600 #if !defined(OS_IOS) && !defined(OS_WIN) | |
| 601 // On platforms other than iOS, 100P is always a supported scale factor. | |
| 602 // For Windows we have a separate case in this function. | |
| 603 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
| 604 #endif | |
| 605 #if defined(OS_ANDROID) | |
| 606 const gfx::Display display = | |
| 607 gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); | |
| 608 const float display_density = display.device_scale_factor(); | |
| 609 const ScaleFactor closest = FindClosestScaleFactorUnsafe(display_density); | |
| 610 if (closest != SCALE_FACTOR_100P) | |
| 611 supported_scale_factors.push_back(closest); | |
| 612 #elif defined(OS_IOS) | |
| 613 gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay(); | |
| 614 if (display.device_scale_factor() > 2.0) { | |
| 615 DCHECK_EQ(3.0, display.device_scale_factor()); | |
| 616 supported_scale_factors.push_back(SCALE_FACTOR_300P); | |
| 617 } else if (display.device_scale_factor() > 1.0) { | |
| 618 DCHECK_EQ(2.0, display.device_scale_factor()); | |
| 619 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
| 620 } else { | |
| 621 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
| 622 } | |
| 623 #elif defined(OS_MACOSX) | |
| 624 if (base::mac::IsOSLionOrLater()) | |
| 625 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
| 626 #elif defined(OS_CHROMEOS) | |
| 627 // TODO(oshima): Include 200P only if the device support 200P | |
| 628 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
| 629 #elif defined(OS_LINUX) && defined(ENABLE_HIDPI) | |
| 630 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
| 631 #elif defined(OS_WIN) | |
| 632 bool default_to_100P = true; | |
| 633 if (gfx::IsHighDPIEnabled()) { | |
| 634 // On Windows if the dpi scale is greater than 1.25 on high dpi machines | |
| 635 // downscaling from 200 percent looks better than scaling up from 100 | |
| 636 // percent. | |
| 637 if (gfx::GetDPIScale() > 1.25) { | |
| 638 supported_scale_factors.push_back(SCALE_FACTOR_200P); | |
| 639 default_to_100P = false; | |
| 640 } | |
| 641 } | |
| 642 if (default_to_100P) | |
| 643 supported_scale_factors.push_back(SCALE_FACTOR_100P); | |
| 644 #endif | |
| 645 ui::SetSupportedScaleFactors(supported_scale_factors); | |
| 646 #if defined(OS_WIN) | |
| 647 // Must be called _after_ supported scale factors are set since it | |
| 648 // uses them. | |
| 649 // Don't initialize the device scale factor if it has already been | |
| 650 // initialized. | |
| 651 if (!gfx::win::IsDeviceScaleFactorSet()) | |
| 652 ui::win::InitDeviceScaleFactor(); | |
| 653 #endif | |
| 654 } | |
| 655 | |
| 656 void ResourceBundle::FreeImages() { | |
| 657 images_.clear(); | |
| 658 } | |
| 659 | |
| 660 void ResourceBundle::AddDataPackFromPathInternal(const base::FilePath& path, | |
| 661 ScaleFactor scale_factor, | |
| 662 bool optional) { | |
| 663 // Do not pass an empty |path| value to this method. If the absolute path is | |
| 664 // unknown pass just the pack file name. | |
| 665 DCHECK(!path.empty()); | |
| 666 | |
| 667 base::FilePath pack_path = path; | |
| 668 if (delegate_) | |
| 669 pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor); | |
| 670 | |
| 671 // Don't try to load empty values or values that are not absolute paths. | |
| 672 if (pack_path.empty() || !pack_path.IsAbsolute()) | |
| 673 return; | |
| 674 | |
| 675 scoped_ptr<DataPack> data_pack( | |
| 676 new DataPack(scale_factor)); | |
| 677 if (data_pack->LoadFromPath(pack_path)) { | |
| 678 AddDataPack(data_pack.release()); | |
| 679 } else if (!optional) { | |
| 680 LOG(ERROR) << "Failed to load " << pack_path.value() | |
| 681 << "\nSome features may not be available."; | |
| 682 } | |
| 683 } | |
| 684 | |
| 685 void ResourceBundle::AddDataPack(DataPack* data_pack) { | |
| 686 data_packs_.push_back(data_pack); | |
| 687 | |
| 688 if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) > | |
| 689 GetScaleForScaleFactor(max_scale_factor_)) | |
| 690 max_scale_factor_ = data_pack->GetScaleFactor(); | |
| 691 } | |
| 692 | |
| 693 void ResourceBundle::LoadFontsIfNecessary() { | |
| 694 images_and_fonts_lock_->AssertAcquired(); | |
| 695 if (!base_font_list_.get()) { | |
| 696 if (delegate_) { | |
| 697 base_font_list_ = GetFontListFromDelegate(BaseFont); | |
| 698 bold_font_list_ = GetFontListFromDelegate(BoldFont); | |
| 699 small_font_list_ = GetFontListFromDelegate(SmallFont); | |
| 700 small_bold_font_list_ = GetFontListFromDelegate(SmallBoldFont); | |
| 701 medium_font_list_ = GetFontListFromDelegate(MediumFont); | |
| 702 medium_bold_font_list_ = GetFontListFromDelegate(MediumBoldFont); | |
| 703 large_font_list_ = GetFontListFromDelegate(LargeFont); | |
| 704 large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont); | |
| 705 } | |
| 706 | |
| 707 if (!base_font_list_.get()) | |
| 708 base_font_list_.reset(new gfx::FontList()); | |
| 709 | |
| 710 if (!bold_font_list_.get()) { | |
| 711 bold_font_list_.reset(new gfx::FontList()); | |
| 712 *bold_font_list_ = base_font_list_->DeriveWithStyle( | |
| 713 base_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
| 714 } | |
| 715 | |
| 716 if (!small_font_list_.get()) { | |
| 717 small_font_list_.reset(new gfx::FontList()); | |
| 718 *small_font_list_ = | |
| 719 base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta); | |
| 720 } | |
| 721 | |
| 722 if (!small_bold_font_list_.get()) { | |
| 723 small_bold_font_list_.reset(new gfx::FontList()); | |
| 724 *small_bold_font_list_ = small_font_list_->DeriveWithStyle( | |
| 725 small_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
| 726 } | |
| 727 | |
| 728 if (!medium_font_list_.get()) { | |
| 729 medium_font_list_.reset(new gfx::FontList()); | |
| 730 *medium_font_list_ = | |
| 731 base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta); | |
| 732 } | |
| 733 | |
| 734 if (!medium_bold_font_list_.get()) { | |
| 735 medium_bold_font_list_.reset(new gfx::FontList()); | |
| 736 *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle( | |
| 737 medium_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
| 738 } | |
| 739 | |
| 740 if (!large_font_list_.get()) { | |
| 741 large_font_list_.reset(new gfx::FontList()); | |
| 742 *large_font_list_ = | |
| 743 base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta); | |
| 744 } | |
| 745 | |
| 746 if (!large_bold_font_list_.get()) { | |
| 747 large_bold_font_list_.reset(new gfx::FontList()); | |
| 748 *large_bold_font_list_ = large_font_list_->DeriveWithStyle( | |
| 749 large_font_list_->GetFontStyle() | gfx::Font::BOLD); | |
| 750 } | |
| 751 } | |
| 752 } | |
| 753 | |
| 754 scoped_ptr<gfx::FontList> ResourceBundle::GetFontListFromDelegate( | |
| 755 FontStyle style) { | |
| 756 DCHECK(delegate_); | |
| 757 scoped_ptr<gfx::Font> font = delegate_->GetFont(style); | |
| 758 if (font.get()) | |
| 759 return scoped_ptr<gfx::FontList>(new gfx::FontList(*font)); | |
| 760 return scoped_ptr<gfx::FontList>(); | |
| 761 } | |
| 762 | |
| 763 bool ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, | |
| 764 int resource_id, | |
| 765 SkBitmap* bitmap, | |
| 766 bool* fell_back_to_1x) const { | |
| 767 DCHECK(fell_back_to_1x); | |
| 768 scoped_refptr<base::RefCountedMemory> memory( | |
| 769 data_handle.GetStaticMemory(static_cast<uint16>(resource_id))); | |
| 770 if (!memory.get()) | |
| 771 return false; | |
| 772 | |
| 773 if (DecodePNG(memory->front(), memory->size(), bitmap, fell_back_to_1x)) | |
| 774 return true; | |
| 775 | |
| 776 #if !defined(OS_IOS) | |
| 777 // iOS does not compile or use the JPEG codec. On other platforms, | |
| 778 // 99% of our assets are PNGs, however fallback to JPEG. | |
| 779 scoped_ptr<SkBitmap> jpeg_bitmap( | |
| 780 gfx::JPEGCodec::Decode(memory->front(), memory->size())); | |
| 781 if (jpeg_bitmap.get()) { | |
| 782 bitmap->swap(*jpeg_bitmap.get()); | |
| 783 *fell_back_to_1x = false; | |
| 784 return true; | |
| 785 } | |
| 786 #endif | |
| 787 | |
| 788 NOTREACHED() << "Unable to decode theme image resource " << resource_id; | |
| 789 return false; | |
| 790 } | |
| 791 | |
| 792 bool ResourceBundle::LoadBitmap(int resource_id, | |
| 793 ScaleFactor* scale_factor, | |
| 794 SkBitmap* bitmap, | |
| 795 bool* fell_back_to_1x) const { | |
| 796 DCHECK(fell_back_to_1x); | |
| 797 for (size_t i = 0; i < data_packs_.size(); ++i) { | |
| 798 if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE && | |
| 799 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) { | |
| 800 DCHECK(!*fell_back_to_1x); | |
| 801 *scale_factor = ui::SCALE_FACTOR_NONE; | |
| 802 return true; | |
| 803 } | |
| 804 if (data_packs_[i]->GetScaleFactor() == *scale_factor && | |
| 805 LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) { | |
| 806 return true; | |
| 807 } | |
| 808 } | |
| 809 return false; | |
| 810 } | |
| 811 | |
| 812 gfx::Image& ResourceBundle::GetEmptyImage() { | |
| 813 base::AutoLock lock(*images_and_fonts_lock_); | |
| 814 | |
| 815 if (empty_image_.IsEmpty()) { | |
| 816 // The placeholder bitmap is bright red so people notice the problem. | |
| 817 SkBitmap bitmap; | |
| 818 bitmap.allocN32Pixels(32, 32); | |
| 819 bitmap.eraseARGB(255, 255, 0, 0); | |
| 820 empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap); | |
| 821 } | |
| 822 return empty_image_; | |
| 823 } | |
| 824 | |
| 825 // static | |
| 826 bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf, | |
| 827 size_t size) { | |
| 828 if (size < arraysize(kPngMagic) || | |
| 829 memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) { | |
| 830 // Data invalid or a JPEG. | |
| 831 return false; | |
| 832 } | |
| 833 size_t pos = arraysize(kPngMagic); | |
| 834 | |
| 835 // Scan for custom chunks until we find one, find the IDAT chunk, or run out | |
| 836 // of chunks. | |
| 837 for (;;) { | |
| 838 if (size - pos < kPngChunkMetadataSize) | |
| 839 break; | |
| 840 uint32 length = 0; | |
| 841 base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length); | |
| 842 if (size - pos - kPngChunkMetadataSize < length) | |
| 843 break; | |
| 844 if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType, | |
| 845 arraysize(kPngScaleChunkType)) == 0) { | |
| 846 return true; | |
| 847 } | |
| 848 if (memcmp(buf + pos + sizeof(uint32), kPngDataChunkType, | |
| 849 arraysize(kPngDataChunkType)) == 0) { | |
| 850 // Stop looking for custom chunks, any custom chunks should be before an | |
| 851 // IDAT chunk. | |
| 852 break; | |
| 853 } | |
| 854 pos += length + kPngChunkMetadataSize; | |
| 855 } | |
| 856 return false; | |
| 857 } | |
| 858 | |
| 859 // static | |
| 860 bool ResourceBundle::DecodePNG(const unsigned char* buf, | |
| 861 size_t size, | |
| 862 SkBitmap* bitmap, | |
| 863 bool* fell_back_to_1x) { | |
| 864 *fell_back_to_1x = PNGContainsFallbackMarker(buf, size); | |
| 865 return gfx::PNGCodec::Decode(buf, size, bitmap); | |
| 866 } | |
| 867 | |
| 868 } // namespace ui | |
| OLD | NEW |