diff options
author | marshall@chromium.org <marshall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-09 15:57:47 +0000 |
---|---|---|
committer | marshall@chromium.org <marshall@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-09 15:57:47 +0000 |
commit | fb317a5d27a76cb0cc21c2eb8312fbfaabd811c6 (patch) | |
tree | fd0fe106112e04a0250ba7554d39d005d17e2e2c /ui/base | |
parent | 91bea1dd2206ae7cd506eb4e40ba7445852d6cf7 (diff) | |
download | chromium_src-fb317a5d27a76cb0cc21c2eb8312fbfaabd811c6.zip chromium_src-fb317a5d27a76cb0cc21c2eb8312fbfaabd811c6.tar.gz chromium_src-fb317a5d27a76cb0cc21c2eb8312fbfaabd811c6.tar.bz2 |
Add new ResourceBundle::Delegate interface.
BUG=125351
TEST=ResourceBundle.*
Review URL: https://chromiumcodereview.appspot.com/10270023
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@136039 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/base')
-rw-r--r-- | ui/base/l10n/l10n_util.cc | 5 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle.cc | 279 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle.h | 92 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_android.cc | 8 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_aurax11.cc | 4 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_gtk.cc | 45 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_mac.mm | 93 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_unittest.cc | 239 | ||||
-rw-r--r-- | ui/base/resource/resource_bundle_win.cc | 13 |
9 files changed, 566 insertions, 212 deletions
diff --git a/ui/base/l10n/l10n_util.cc b/ui/base/l10n/l10n_util.cc index eef75c4..534bf1e 100644 --- a/ui/base/l10n/l10n_util.cc +++ b/ui/base/l10n/l10n_util.cc @@ -246,7 +246,7 @@ bool IsLocaleAvailable(const std::string& locale) { if (!l10n_util::IsLocaleSupportedByOS(locale)) return false; - return ResourceBundle::LocaleDataPakExists(locale); + return ResourceBundle::GetSharedInstance().LocaleDataPakExists(locale); } bool CheckAndResolveLocale(const std::string& locale, @@ -452,9 +452,6 @@ std::string GetApplicationLocale(const std::string& pref_locale) { return fallback_locale; } - // No locale data file was found; we shouldn't get here. - NOTREACHED(); - return std::string(); #endif diff --git a/ui/base/resource/resource_bundle.cc b/ui/base/resource/resource_bundle.cc index 24c0560..58765b1 100644 --- a/ui/base/resource/resource_bundle.cc +++ b/ui/base/resource/resource_bundle.cc @@ -24,8 +24,6 @@ #include "ui/base/ui_base_switches.h" #include "ui/gfx/codec/jpeg_codec.h" #include "ui/gfx/codec/png_codec.h" -#include "ui/gfx/font.h" -#include "ui/gfx/image/image.h" namespace ui { @@ -48,9 +46,9 @@ ResourceBundle* ResourceBundle::g_shared_instance_ = NULL; // static std::string ResourceBundle::InitSharedInstanceWithLocale( - const std::string& pref_locale) { + const std::string& pref_locale, Delegate* delegate) { DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; - g_shared_instance_ = new ResourceBundle(); + g_shared_instance_ = new ResourceBundle(delegate); g_shared_instance_->LoadCommonResources(); return g_shared_instance_->LoadLocaleResources(pref_locale); @@ -59,7 +57,7 @@ std::string ResourceBundle::InitSharedInstanceWithLocale( // static void ResourceBundle::InitSharedInstanceWithPakFile(const FilePath& path) { DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; - g_shared_instance_ = new ResourceBundle(); + g_shared_instance_ = new ResourceBundle(NULL); g_shared_instance_->LoadTestResources(path); } @@ -84,39 +82,62 @@ ResourceBundle& ResourceBundle::GetSharedInstance() { return *g_shared_instance_; } -// static bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { return !GetLocaleFilePath(locale).empty(); } void ResourceBundle::AddDataPack(const FilePath& path, float scale_factor) { + // Do not pass an empty |path| value to this method. If the absolute path is + // unknown pass just the pack file name. + DCHECK(!path.empty()); + + FilePath pack_path = path; + if (delegate_) + pack_path = delegate_->GetPathForResourcePack(pack_path, scale_factor); + + // Don't try to load empty values or values that are not absolute paths. + if (pack_path.empty() || !pack_path.IsAbsolute()) + return; + scoped_ptr<DataPack> data_pack( new DataPack(ResourceHandle::kScaleFactor100x)); - if (data_pack->Load(path)) { + if (data_pack->Load(pack_path)) { data_packs_.push_back(data_pack.release()); } else { - LOG(ERROR) << "Failed to load " << path.value() + LOG(ERROR) << "Failed to load " << pack_path.value() << "\nSome features may not be available."; } } #if !defined(OS_MACOSX) -// static FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale) { + if (app_locale.empty()) + return FilePath(); + FilePath locale_file_path; + #if defined(OS_ANDROID) PathService::Get(base::DIR_ANDROID_APP_DATA, &locale_file_path); locale_file_path = locale_file_path.Append(FILE_PATH_LITERAL("paks")); #else PathService::Get(ui::DIR_LOCALES, &locale_file_path); #endif - if (locale_file_path.empty()) - return locale_file_path; - if (app_locale.empty()) + + if (!locale_file_path.empty()) + locale_file_path = locale_file_path.AppendASCII(app_locale + ".pak"); + + if (delegate_) { + locale_file_path = + delegate_->GetPathForLocalePack(locale_file_path, app_locale); + } + + // Don't try to load empty values or values that are not absolute paths. + if (locale_file_path.empty() || !locale_file_path.IsAbsolute()) return FilePath(); - locale_file_path = locale_file_path.AppendASCII(app_locale + ".pak"); + if (!file_util::PathExists(locale_file_path)) return FilePath(); + return locale_file_path; } #endif @@ -127,7 +148,7 @@ std::string ResourceBundle::LoadLocaleResources( std::string app_locale = l10n_util::GetApplicationLocale(pref_locale); FilePath locale_file_path = GetOverriddenPakPath(); if (locale_file_path.empty()) { - CommandLine *command_line = CommandLine::ForCurrentProcess(); + CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kLocalePak)) { locale_file_path = command_line->GetSwitchValuePath(switches::kLocalePak); @@ -138,7 +159,6 @@ std::string ResourceBundle::LoadLocaleResources( if (locale_file_path.empty()) { // It's possible that there is no locale.pak. - NOTREACHED(); return std::string(); } @@ -171,46 +191,6 @@ void ResourceBundle::UnloadLocaleResources() { locale_resources_data_.reset(); } -string16 ResourceBundle::GetLocalizedString(int message_id) { - // Ensure that ReloadLocaleResources() doesn't drop the resources while - // we're using them. - base::AutoLock lock_scope(*locale_resources_data_lock_); - - // If for some reason we were unable to load the resources , return an empty - // string (better than crashing). - if (!locale_resources_data_.get()) { - LOG(WARNING) << "locale resources are not loaded"; - return string16(); - } - - base::StringPiece data; - if (!locale_resources_data_->GetStringPiece(message_id, &data)) { - // Fall back on the main data pack (shouldn't be any strings here except in - // unittests). - data = GetRawDataResource(message_id); - if (data.empty()) { - NOTREACHED() << "unable to find resource: " << message_id; - return string16(); - } - } - - // Strings should not be loaded from a data pack that contains binary data. - ResourceHandle::TextEncodingType encoding = - locale_resources_data_->GetTextEncodingType(); - DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) - << "requested localized string from binary pack file"; - - // Data pack encodes strings as either UTF8 or UTF16. - string16 msg; - if (encoding == ResourceHandle::UTF16) { - msg = string16(reinterpret_cast<const char16*>(data.data()), - data.length() / 2); - } else if (encoding == ResourceHandle::UTF8) { - msg = UTF8ToUTF16(data); - } - return msg; -} - void ResourceBundle::OverrideLocalePakForTest(const FilePath& pak_path) { overridden_pak_path_ = pak_path; } @@ -235,24 +215,36 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { // Check to see if the image is already in the cache. { base::AutoLock lock_scope(*images_and_fonts_lock_); - ImageMap::const_iterator found = images_.find(resource_id); - if (found != images_.end()) - return *found->second; + if (images_.count(resource_id)) + return images_[resource_id]; } - DCHECK(!data_packs_.empty()) << "Missing call to SetResourcesDataDLL?"; - ScopedVector<const SkBitmap> bitmaps; - for (size_t i = 0; i < data_packs_.size(); ++i) { - SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); - if (bitmap) - bitmaps.push_back(bitmap); - } + gfx::Image image; + if (delegate_) + image = delegate_->GetImageNamed(resource_id); + + if (image.IsEmpty()) { + DCHECK(!delegate_ && !data_packs_.empty()) << + "Missing call to SetResourcesDataDLL?"; + ScopedVector<const SkBitmap> bitmaps; + for (size_t i = 0; i < data_packs_.size(); ++i) { + SkBitmap* bitmap = LoadBitmap(*data_packs_[i], resource_id); + if (bitmap) + bitmaps.push_back(bitmap); + } - if (bitmaps.empty()) { - LOG(WARNING) << "Unable to load image with id " << resource_id; - NOTREACHED(); // Want to assert in debug mode. - // The load failed to retrieve the image; show a debugging red square. - return *GetEmptyImage(); + if (bitmaps.empty()) { + LOG(WARNING) << "Unable to load image with id " << resource_id; + NOTREACHED(); // Want to assert in debug mode. + // The load failed to retrieve the image; show a debugging red square. + return GetEmptyImage(); + } + + std::vector<const SkBitmap*> tmp_bitmaps; + bitmaps.release(&tmp_bitmaps); + + // Takes ownership of bitmaps. + image = gfx::Image(tmp_bitmaps); } // The load was successful, so cache the image. @@ -260,14 +252,10 @@ gfx::Image& ResourceBundle::GetImageNamed(int resource_id) { // Another thread raced the load and has already cached the image. if (images_.count(resource_id)) - return *images_[resource_id]; + return images_[resource_id]; - std::vector<const SkBitmap*> tmp_bitmaps; - bitmaps.release(&tmp_bitmaps); - // Takes ownership of bitmaps. - gfx::Image* image = new gfx::Image(tmp_bitmaps); images_[resource_id] = image; - return *image; + return images_[resource_id]; } gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { @@ -276,19 +264,24 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id) { base::RefCountedStaticMemory* ResourceBundle::LoadDataResourceBytes( int resource_id) const { - for (size_t i = 0; i < data_packs_.size(); ++i) { - base::RefCountedStaticMemory* bytes = - data_packs_[i]->GetStaticMemory(resource_id); - if (bytes) - return bytes; + base::RefCountedStaticMemory* bytes = NULL; + if (delegate_) + bytes = delegate_->LoadDataResourceBytes(resource_id); + + if (!bytes) { + for (size_t i = 0; i < data_packs_.size() && !bytes; ++i) + bytes = data_packs_[i]->GetStaticMemory(resource_id); } - return NULL; + return bytes; } base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const { - DCHECK(locale_resources_data_.get()); base::StringPiece data; + if (delegate_ && delegate_->GetRawDataResource(resource_id, &data)) + return data; + + DCHECK(locale_resources_data_.get()); if (locale_resources_data_->GetStringPiece(resource_id, &data)) return data; @@ -300,6 +293,50 @@ base::StringPiece ResourceBundle::GetRawDataResource(int resource_id) const { return base::StringPiece(); } +string16 ResourceBundle::GetLocalizedString(int message_id) { + string16 string; + if (delegate_ && delegate_->GetLocalizedString(message_id, &string)) + return string; + + // Ensure that ReloadLocaleResources() doesn't drop the resources while + // we're using them. + base::AutoLock lock_scope(*locale_resources_data_lock_); + + // If for some reason we were unable to load the resources , return an empty + // string (better than crashing). + if (!locale_resources_data_.get()) { + LOG(WARNING) << "locale resources are not loaded"; + return string16(); + } + + base::StringPiece data; + if (!locale_resources_data_->GetStringPiece(message_id, &data)) { + // Fall back on the main data pack (shouldn't be any strings here except in + // unittests). + data = GetRawDataResource(message_id); + if (data.empty()) { + NOTREACHED() << "unable to find resource: " << message_id; + return string16(); + } + } + + // Strings should not be loaded from a data pack that contains binary data. + ResourceHandle::TextEncodingType encoding = + locale_resources_data_->GetTextEncodingType(); + DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) + << "requested localized string from binary pack file"; + + // Data pack encodes strings as either UTF8 or UTF16. + string16 msg; + if (encoding == ResourceHandle::UTF16) { + msg = string16(reinterpret_cast<const char16*>(data.data()), + data.length() / 2); + } else if (encoding == ResourceHandle::UTF8) { + msg = UTF8ToUTF16(data); + } + return msg; +} + const gfx::Font& ResourceBundle::GetFont(FontStyle style) { { base::AutoLock lock_scope(*images_and_fonts_lock_); @@ -329,8 +366,9 @@ void ResourceBundle::ReloadFonts() { LoadFontsIfNecessary(); } -ResourceBundle::ResourceBundle() - : images_and_fonts_lock_(new base::Lock), +ResourceBundle::ResourceBundle(Delegate* delegate) + : delegate_(delegate), + images_and_fonts_lock_(new base::Lock), locale_resources_data_lock_(new base::Lock) { } @@ -340,38 +378,59 @@ ResourceBundle::~ResourceBundle() { } void ResourceBundle::FreeImages() { - STLDeleteContainerPairSecondPointers(images_.begin(), - images_.end()); images_.clear(); } void ResourceBundle::LoadFontsIfNecessary() { images_and_fonts_lock_->AssertAcquired(); if (!base_font_.get()) { - base_font_.reset(new gfx::Font()); + if (delegate_) { + base_font_.reset(delegate_->GetFont(BaseFont).release()); + bold_font_.reset(delegate_->GetFont(BoldFont).release()); + small_font_.reset(delegate_->GetFont(SmallFont).release()); + medium_font_.reset(delegate_->GetFont(MediumFont).release()); + medium_bold_font_.reset(delegate_->GetFont(MediumBoldFont).release()); + large_font_.reset(delegate_->GetFont(LargeFont).release()); + large_bold_font_.reset(delegate_->GetFont(LargeBoldFont).release()); + } - bold_font_.reset(new gfx::Font()); - *bold_font_ = - base_font_->DeriveFont(0, base_font_->GetStyle() | gfx::Font::BOLD); + if (!base_font_.get()) + base_font_.reset(new gfx::Font()); - small_font_.reset(new gfx::Font()); - *small_font_ = base_font_->DeriveFont(kSmallFontSizeDelta); + if (!bold_font_.get()) { + bold_font_.reset(new gfx::Font()); + *bold_font_ = + base_font_->DeriveFont(0, base_font_->GetStyle() | gfx::Font::BOLD); + } - medium_font_.reset(new gfx::Font()); - *medium_font_ = base_font_->DeriveFont(kMediumFontSizeDelta); + if (!small_font_.get()) { + small_font_.reset(new gfx::Font()); + *small_font_ = base_font_->DeriveFont(kSmallFontSizeDelta); + } - medium_bold_font_.reset(new gfx::Font()); - *medium_bold_font_ = - base_font_->DeriveFont(kMediumFontSizeDelta, - base_font_->GetStyle() | gfx::Font::BOLD); + if (!medium_font_.get()) { + medium_font_.reset(new gfx::Font()); + *medium_font_ = base_font_->DeriveFont(kMediumFontSizeDelta); + } - large_font_.reset(new gfx::Font()); - *large_font_ = base_font_->DeriveFont(kLargeFontSizeDelta); + if (!medium_bold_font_.get()) { + medium_bold_font_.reset(new gfx::Font()); + *medium_bold_font_ = + base_font_->DeriveFont(kMediumFontSizeDelta, + base_font_->GetStyle() | gfx::Font::BOLD); + } - large_bold_font_.reset(new gfx::Font()); - *large_bold_font_ = - base_font_->DeriveFont(kLargeFontSizeDelta, - base_font_->GetStyle() | gfx::Font::BOLD); + if (!large_font_.get()) { + large_font_.reset(new gfx::Font()); + *large_font_ = base_font_->DeriveFont(kLargeFontSizeDelta); + } + + if (!large_bold_font_.get()) { + large_bold_font_.reset(new gfx::Font()); + *large_bold_font_ = + base_font_->DeriveFont(kLargeFontSizeDelta, + base_font_->GetStyle() | gfx::Font::BOLD); + } } } @@ -396,20 +455,18 @@ SkBitmap* ResourceBundle::LoadBitmap(const ResourceHandle& data_handle, return NULL; } -gfx::Image* ResourceBundle::GetEmptyImage() { +gfx::Image& ResourceBundle::GetEmptyImage() { base::AutoLock lock(*images_and_fonts_lock_); - static gfx::Image* empty_image = NULL; - if (!empty_image) { + if (empty_image_.IsEmpty()) { // The placeholder bitmap is bright red so people notice the problem. - // This bitmap will be leaked, but this code should never be hit. SkBitmap* bitmap = new SkBitmap(); bitmap->setConfig(SkBitmap::kARGB_8888_Config, 32, 32); bitmap->allocPixels(); bitmap->eraseARGB(255, 255, 0, 0); - empty_image = new gfx::Image(bitmap); + empty_image_ = gfx::Image(bitmap); } - return empty_image; + return empty_image_; } } // namespace ui diff --git a/ui/base/resource/resource_bundle.h b/ui/base/resource/resource_bundle.h index 7b06e5e9..41d2634 100644 --- a/ui/base/resource/resource_bundle.h +++ b/ui/base/resource/resource_bundle.h @@ -19,6 +19,8 @@ #include "base/string16.h" #include "base/string_piece.h" #include "ui/base/ui_export.h" +#include "ui/gfx/font.h" +#include "ui/gfx/image/image.h" #include "ui/gfx/native_widget_types.h" class SkBitmap; @@ -28,11 +30,6 @@ class Lock; class RefCountedStaticMemory; } -namespace gfx { -class Font; -class Image; -} - namespace ui { class ResourceHandle; @@ -63,12 +60,60 @@ class UI_EXPORT ResourceBundle { RTL_DISABLED, }; - // Initialize the ResourceBundle for this process. Returns the language - // selected. + // Delegate class that allows interception of pack file loading and resource + // requests. The methods of this class may be called on multiple threads. + class Delegate { + public: + // Called before a resource pack file is loaded. Return the full path for + // the pack file to continue loading or an empty value to cancel loading. + // |pack_path| will contain the complete default path for the pack file if + // known or just the pack file name otherwise. + virtual FilePath GetPathForResourcePack(const FilePath& pack_path, + float scale_factor) = 0; + + // Called before a locale pack file is loaded. Return the full path for + // the pack file to continue loading or an empty value to cancel loading. + // |pack_path| will contain the complete default path for the pack file if + // known or just the pack file name otherwise. + virtual FilePath GetPathForLocalePack(const FilePath& pack_path, + const std::string& locale) = 0; + + // Return an image resource or an empty value to attempt retrieval of the + // default resource. + virtual gfx::Image GetImageNamed(int resource_id) = 0; + + // Return an image resource or an empty value to attempt retrieval of the + // default resource. + virtual gfx::Image GetNativeImageNamed(int resource_id, ImageRTL rtl) = 0; + + // Return a static memory resource or NULL to attempt retrieval of the + // default resource. + virtual base::RefCountedStaticMemory* LoadDataResourceBytes( + int resource_id) = 0; + + // Retrieve a raw data resource. Return true if a resource was provided or + // false to attempt retrieval of the default resource. + virtual bool GetRawDataResource(int resource_id, + base::StringPiece* value) = 0; + + // Retrieve a localized string. Return true if a string was provided or + // false to attempt retrieval of the default string. + virtual bool GetLocalizedString(int message_id, string16* value) = 0; + + // Return a font resource or NULL to attempt retrieval of the default + // resource. + virtual scoped_ptr<gfx::Font> GetFont(FontStyle style) = 0; + + protected: + virtual ~Delegate() {} + }; + + // Initialize the ResourceBundle for this process. Does not take ownership of + // the |delegate| value. Returns the language selected. // NOTE: Mac ignores this and always loads up resources for the language // defined by the Cocoa UI (i.e., NSBundle does the language work). static std::string InitSharedInstanceWithLocale( - const std::string& pref_locale); + const std::string& pref_locale, Delegate* delegate); // Initialize the ResourceBundle using given data pack path for testing. static void InitSharedInstanceWithPakFile(const FilePath& path); @@ -83,11 +128,13 @@ class UI_EXPORT ResourceBundle { static ResourceBundle& GetSharedInstance(); // Check if the .pak for the given locale exists. - static bool LocaleDataPakExists(const std::string& locale); + bool LocaleDataPakExists(const std::string& locale); - // Registers additional data pack files with the global ResourceBundle. When + // Registers additional data pack files with this ResourceBundle. When // looking for a DataResource, we will search these files after searching the - // main module. |scale_factor| is the scale of images in this resource pak + // main module. |path| should be the complete path to the pack file if known + // or just the pack file name otherwise (the delegate may optionally override + // this value). |scale_factor| is the scale of images in this resource pak // relative to the images in the 1x resource pak. This method is not thread // safe! You should call it immediately after calling InitSharedInstance. void AddDataPack(const FilePath& path, float scale_factor); @@ -149,10 +196,19 @@ class UI_EXPORT ResourceBundle { void OverrideLocalePakForTest(const FilePath& pak_path); private: + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetPathForResourcePack); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetPathForLocalePack); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetImageNamed); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetNativeImageNamed); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateLoadDataResourceBytes); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetRawDataResource); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetLocalizedString); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, DelegateGetFont); FRIEND_TEST_ALL_PREFIXES(ResourceBundle, LoadDataResourceBytes); + FRIEND_TEST_ALL_PREFIXES(ResourceBundle, LocaleDataPakExists); // Ctor/dtor are private, since we're a singleton. - ResourceBundle(); + explicit ResourceBundle(Delegate* delegate); ~ResourceBundle(); // Free skia_images_. @@ -177,7 +233,7 @@ class UI_EXPORT ResourceBundle { // Returns the full pathname of the locale file to load. May return an empty // string if no locale data files are found. - static FilePath GetLocaleFilePath(const std::string& app_locale); + FilePath GetLocaleFilePath(const std::string& app_locale); // Creates and returns a new SkBitmap given the data file to look in and the // resource id. It's up to the caller to free the returned bitmap when @@ -186,10 +242,14 @@ class UI_EXPORT ResourceBundle { // Returns an empty image for when a resource cannot be loaded. This is a // bright red bitmap. - gfx::Image* GetEmptyImage(); + gfx::Image& GetEmptyImage(); const FilePath& GetOverriddenPakPath(); + // This pointer is guaranteed to outlive the ResourceBundle instance and may + // be NULL. + Delegate* delegate_; + // Protects |images_| and font-related members. scoped_ptr<base::Lock> images_and_fonts_lock_; @@ -202,9 +262,11 @@ class UI_EXPORT ResourceBundle { // Cached images. The ResourceBundle caches all retrieved images and keeps // ownership of the pointers. - typedef std::map<int, gfx::Image*> ImageMap; + typedef std::map<int, gfx::Image> ImageMap; ImageMap images_; + gfx::Image empty_image_; + // The various fonts used. Cached to avoid repeated GDI creation/destruction. scoped_ptr<gfx::Font> base_font_; scoped_ptr<gfx::Font> bold_font_; diff --git a/ui/base/resource/resource_bundle_android.cc b/ui/base/resource/resource_bundle_android.cc index 6825c40..d770d3e 100644 --- a/ui/base/resource/resource_bundle_android.cc +++ b/ui/base/resource/resource_bundle_android.cc @@ -17,9 +17,11 @@ namespace { FilePath GetResourcesPakFilePath(const std::string& pak_name) { FilePath path; - PathService::Get(base::DIR_ANDROID_APP_DATA, &path); - DCHECK(!path.empty()); - return path.AppendASCII("paks").AppendASCII(pak_name.c_str()); + if (PathService::Get(base::DIR_ANDROID_APP_DATA, &path)) + return path.AppendASCII("paks").AppendASCII(pak_name.c_str()); + + // Return just the name of the pack file. + return FilePath(pak_name.c_str()); } } // namespace diff --git a/ui/base/resource/resource_bundle_aurax11.cc b/ui/base/resource/resource_bundle_aurax11.cc index 9dd3137..6f63dff 100644 --- a/ui/base/resource/resource_bundle_aurax11.cc +++ b/ui/base/resource/resource_bundle_aurax11.cc @@ -17,7 +17,9 @@ FilePath GetResourcesPakFilePath(const std::string& pak_name) { FilePath path; if (PathService::Get(base::DIR_MODULE, &path)) return path.AppendASCII(pak_name.c_str()); - return FilePath(); + + // Return just the name of the pack file. + return FilePath(pak_name.c_str()); } } // namespace diff --git a/ui/base/resource/resource_bundle_gtk.cc b/ui/base/resource/resource_bundle_gtk.cc index 6125dd7..1c64e4d 100644 --- a/ui/base/resource/resource_bundle_gtk.cc +++ b/ui/base/resource/resource_bundle_gtk.cc @@ -56,7 +56,9 @@ FilePath GetResourcesPakFilePath(const std::string& pak_name) { FilePath path; if (PathService::Get(base::DIR_MODULE, &path)) return path.AppendASCII(pak_name.c_str()); - return FilePath(); + + // Return just the name of the pack file. + return FilePath(pak_name.c_str()); } } // namespace @@ -77,33 +79,36 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { // Check to see if the image is already in the cache. { base::AutoLock lock_scope(*images_and_fonts_lock_); - ImageMap::const_iterator found = images_.find(key); - if (found != images_.end()) - return *found->second; + if (images_.count(key)) + return images_[key]; } - scoped_refptr<base::RefCountedStaticMemory> data( - LoadDataResourceBytes(resource_id)); - GdkPixbuf* pixbuf = LoadPixbuf(data.get(), rtl == RTL_ENABLED); + gfx::Image image; + if (delegate_) + image = delegate_->GetNativeImageNamed(resource_id, rtl); - // The load was successful, so cache the image. - if (pixbuf) { - base::AutoLock lock_scope(*images_and_fonts_lock_); + if (image.IsEmpty()) { + scoped_refptr<base::RefCountedStaticMemory> data( + LoadDataResourceBytes(resource_id)); + GdkPixbuf* pixbuf = LoadPixbuf(data.get(), rtl == RTL_ENABLED); - // Another thread raced the load and has already cached the image. - if (images_.count(key)) { - g_object_unref(pixbuf); - return *images_[key]; + if (!pixbuf) { + LOG(WARNING) << "Unable to load pixbuf with id " << resource_id; + NOTREACHED(); // Want to assert in debug mode. + return GetEmptyImage(); } - gfx::Image* image = new gfx::Image(pixbuf); // Takes ownership. - images_[key] = image; - return *image; + image = gfx::Image(pixbuf); // Takes ownership. } - LOG(WARNING) << "Unable to pixbuf with id " << resource_id; - NOTREACHED(); // Want to assert in debug mode. - return *GetEmptyImage(); + base::AutoLock lock_scope(*images_and_fonts_lock_); + + // Another thread raced the load and has already cached the image. + if (images_.count(key)) + return images_[key]; + + images_[key] = image; + return images_[key]; } } // namespace ui diff --git a/ui/base/resource/resource_bundle_mac.mm b/ui/base/resource/resource_bundle_mac.mm index cd89433..8a559c4 100644 --- a/ui/base/resource/resource_bundle_mac.mm +++ b/ui/base/resource/resource_bundle_mac.mm @@ -8,6 +8,7 @@ #include "base/basictypes.h" #include "base/file_path.h" +#include "base/file_util.h" #include "base/mac/bundle_locations.h" #include "base/mac/mac_util.h" #include "base/memory/ref_counted_memory.h" @@ -36,8 +37,12 @@ FilePath GetResourcesPakFilePath(NSString* name, NSString* mac_locale) { resource_path = [base::mac::FrameworkBundle() pathForResource:name ofType:@"pak"]; } - if (!resource_path) - return FilePath(); + + if (!resource_path) { + // Return just the name of the pack file. + return FilePath(base::SysNSStringToUTF8(name) + ".pak"); + } + return FilePath([resource_path fileSystemRepresentation]); } @@ -56,16 +61,15 @@ void ResourceBundle::LoadCommonResources() { #if defined(ENABLE_HIDPI) if (base::mac::IsOSLionOrLater()) { AddDataPack(GetResourcesPakFilePath(@"theme_resources_2x", nil), - ResourceHandle::kScaleFactor200x); + ResourceHandle::kScaleFactor200x); AddDataPack(GetResourcesPakFilePath(@"theme_resources_standard_2x", nil), - ResourceHandle::kScaleFactor200x); + ResourceHandle::kScaleFactor200x); AddDataPack(GetResourcesPakFilePath(@"ui_resources_standard_2x", nil), - ResourceHandle::kScaleFactor200x); + ResourceHandle::kScaleFactor200x); } #endif } -// static FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale) { NSString* mac_locale = base::SysUTF8ToNSString(app_locale); @@ -77,7 +81,21 @@ FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale) { if ([mac_locale isEqual:@"en_US"]) mac_locale = @"en"; - return GetResourcesPakFilePath(@"locale", mac_locale); + FilePath locale_file_path = GetResourcesPakFilePath(@"locale", mac_locale); + + if (delegate_) { + locale_file_path = + delegate_->GetPathForLocalePack(locale_file_path, app_locale); + } + + // Don't try to load empty values or values that are not absolute paths. + if (locale_file_path.empty() || !locale_file_path.IsAbsolute()) + return FilePath(); + + if (!file_util::PathExists(locale_file_path)) + return FilePath(); + + return locale_file_path; } gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { @@ -87,53 +105,58 @@ gfx::Image& ResourceBundle::GetNativeImageNamed(int resource_id, ImageRTL rtl) { // Check to see if the image is already in the cache. { base::AutoLock lock(*images_and_fonts_lock_); - ImageMap::const_iterator found = images_.find(resource_id); - if (found != images_.end()) { - if (!found->second->HasRepresentation(gfx::Image::kImageRepCocoa)) { + if (images_.count(resource_id)) { + if (!images_[resource_id].HasRepresentation(gfx::Image::kImageRepCocoa)) { DLOG(WARNING) << "ResourceBundle::GetNativeImageNamed() is returning a" << " cached gfx::Image that isn't backed by an NSImage. The image" << " will be converted, rather than going through the NSImage loader." << " resource_id = " << resource_id; } - return *found->second; + return images_[resource_id]; } } - scoped_nsobject<NSImage> ns_image; - for (size_t i = 0; i < data_packs_.size(); ++i) { - scoped_refptr<base::RefCountedStaticMemory> data( - data_packs_[i]->GetStaticMemory(resource_id)); - if (!data.get()) - continue; + gfx::Image image; + if (delegate_) + image = delegate_->GetNativeImageNamed(resource_id, rtl); + + if (image.IsEmpty()) { + scoped_nsobject<NSImage> ns_image; + for (size_t i = 0; i < data_packs_.size(); ++i) { + scoped_refptr<base::RefCountedStaticMemory> data( + data_packs_[i]->GetStaticMemory(resource_id)); + if (!data.get()) + continue; + + scoped_nsobject<NSData> ns_data( + [[NSData alloc] initWithBytes:data->front() + length:data->size()]); + if (!ns_image.get()) { + ns_image.reset([[NSImage alloc] initWithData:ns_data]); + } else { + NSImageRep* image_rep = [NSBitmapImageRep imageRepWithData:ns_data]; + if (image_rep) + [ns_image addRepresentation:image_rep]; + } + } - scoped_nsobject<NSData> ns_data( - [[NSData alloc] initWithBytes:data->front() - length:data->size()]); if (!ns_image.get()) { - ns_image.reset([[NSImage alloc] initWithData:ns_data]); - } else { - NSImageRep* image_rep = [NSBitmapImageRep imageRepWithData:ns_data]; - if (image_rep) - [ns_image addRepresentation:image_rep]; + LOG(WARNING) << "Unable to load image with id " << resource_id; + NOTREACHED(); // Want to assert in debug mode. + return GetEmptyImage(); } - } - if (!ns_image.get()) { - LOG(WARNING) << "Unable to load image with id " << resource_id; - NOTREACHED(); // Want to assert in debug mode. - return *GetEmptyImage(); + image = gfx::Image(ns_image.release()); } base::AutoLock lock(*images_and_fonts_lock_); // Another thread raced the load and has already cached the image. - if (images_.count(resource_id)) { - return *images_[resource_id]; - } + if (images_.count(resource_id)) + return images_[resource_id]; - gfx::Image* image = new gfx::Image(ns_image.release()); images_[resource_id] = image; - return *image; + return images_[resource_id]; } } // namespace ui diff --git a/ui/base/resource/resource_bundle_unittest.cc b/ui/base/resource/resource_bundle_unittest.cc index 5e2598ed..1d72785 100644 --- a/ui/base/resource/resource_bundle_unittest.cc +++ b/ui/base/resource/resource_bundle_unittest.cc @@ -7,47 +7,250 @@ #include "base/base_paths.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/memory/ref_counted_memory.h" #include "base/path_service.h" #include "base/scoped_temp_dir.h" +#include "base/utf_string_conversions.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" + +using ::testing::_; +using ::testing::Between; +using ::testing::Property; +using ::testing::Return; +using ::testing::ReturnArg; namespace ui { extern const char kSamplePakContents[]; extern const size_t kSamplePakSize; -TEST(ResourceBundle, LoadDataResourceBytes) { - // Verify that we don't crash when trying to load a resource that is not - // found. In some cases, we fail to mmap resources.pak, but try to keep - // going anyway. - ResourceBundle resource_bundle; +namespace { + +// Mock for the ResourceBundle::Delegate class. +class MockResourceBundleDelegate : public ui::ResourceBundle::Delegate { + public: + MockResourceBundleDelegate() { + } + virtual ~MockResourceBundleDelegate() { + } + + MOCK_METHOD2(GetPathForResourcePack, FilePath(const FilePath& pack_path, + float scale_factor)); + MOCK_METHOD2(GetPathForLocalePack, FilePath(const FilePath& pack_path, + const std::string& locale)); + MOCK_METHOD1(GetImageNamed, gfx::Image(int resource_id)); + MOCK_METHOD2(GetNativeImageNamed, + gfx::Image(int resource_id, + ui::ResourceBundle::ImageRTL rtl)); + MOCK_METHOD1(LoadDataResourceBytes, + base::RefCountedStaticMemory*(int resource_id)); + MOCK_METHOD1(GetRawDataResourceMock, base::StringPiece(int resource_id)); + virtual bool GetRawDataResource(int resource_id, + base::StringPiece* value) OVERRIDE { + *value = GetRawDataResourceMock(resource_id); + return true; + } + MOCK_METHOD1(GetLocalizedStringMock, string16(int message_id)); + virtual bool GetLocalizedString(int message_id, string16* value) OVERRIDE { + *value = GetLocalizedStringMock(message_id); + return true; + } + MOCK_METHOD1(GetFontMock, gfx::Font*(ui::ResourceBundle::FontStyle style)); + virtual scoped_ptr<gfx::Font> GetFont( + ui::ResourceBundle::FontStyle style) OVERRIDE { + return scoped_ptr<gfx::Font>(GetFontMock(style)); + } +}; + +} // namespace + +TEST(ResourceBundle, DelegateGetPathForResourcePack) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + FilePath pack_path(FILE_PATH_LITERAL("/path/to/test_path.pak")); + double pack_scale_factor = 2.0; + + EXPECT_CALL(delegate, + GetPathForResourcePack( + Property(&FilePath::value, pack_path.value()), + pack_scale_factor)) + .Times(1) + .WillOnce(Return(pack_path)); + + resource_bundle.AddDataPack(pack_path, pack_scale_factor); +} + +TEST(ResourceBundle, DelegateGetPathForLocalePack) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + std::string locale = "en-US"; + + // Cancel the load. + EXPECT_CALL(delegate, GetPathForLocalePack(_, locale)) + .Times(2) + .WillRepeatedly(Return(FilePath())) + .RetiresOnSaturation(); + EXPECT_FALSE(resource_bundle.LocaleDataPakExists(locale)); + EXPECT_EQ("", resource_bundle.LoadLocaleResources(locale)); + + // Allow the load to proceed. + EXPECT_CALL(delegate, GetPathForLocalePack(_, locale)) + .Times(2) + .WillRepeatedly(ReturnArg<0>()); + + EXPECT_TRUE(resource_bundle.LocaleDataPakExists(locale)); + EXPECT_EQ(locale, resource_bundle.LoadLocaleResources(locale)); +} + +TEST(ResourceBundle, DelegateGetImageNamed) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + gfx::Image empty_image = resource_bundle.GetEmptyImage(); + int resource_id = 5; + + EXPECT_CALL(delegate, GetImageNamed(resource_id)) + .Times(1) + .WillOnce(Return(empty_image)); + + gfx::Image result = resource_bundle.GetImageNamed(resource_id); + EXPECT_EQ(empty_image.ToSkBitmap(), result.ToSkBitmap()); +} + +TEST(ResourceBundle, DelegateGetNativeImageNamed) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + gfx::Image empty_image = resource_bundle.GetEmptyImage(); + int resource_id = 5; + + // Some platforms delegate GetNativeImageNamed calls to GetImageNamed. + EXPECT_CALL(delegate, GetImageNamed(resource_id)) + .Times(Between(0, 1)) + .WillOnce(Return(empty_image)); + EXPECT_CALL(delegate, + GetNativeImageNamed(resource_id, ui::ResourceBundle::RTL_DISABLED)) + .Times(Between(0, 1)) + .WillOnce(Return(empty_image)); + + gfx::Image result = resource_bundle.GetNativeImageNamed(resource_id); + EXPECT_EQ(empty_image.ToSkBitmap(), result.ToSkBitmap()); +} + +TEST(ResourceBundle, DelegateLoadDataResourceBytes) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + // Create the data resource for testing purposes. + unsigned char data[] = "My test data"; + scoped_refptr<base::RefCountedStaticMemory> static_memory( + new base::RefCountedStaticMemory(data, sizeof(data))); + + int resource_id = 5; + + EXPECT_CALL(delegate, LoadDataResourceBytes(resource_id)) + .Times(1) + .WillOnce(Return(static_memory)); + + scoped_refptr<base::RefCountedStaticMemory> result = + resource_bundle.LoadDataResourceBytes(resource_id); + EXPECT_EQ(static_memory, result); +} + +TEST(ResourceBundle, DelegateGetRawDataResource) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + // Create the string piece for testing purposes. + char data[] = "My test data"; + base::StringPiece string_piece(data); + + int resource_id = 5; + + EXPECT_CALL(delegate, GetRawDataResourceMock(resource_id)) + .Times(1) + .WillOnce(Return(string_piece)); + + base::StringPiece result = resource_bundle.GetRawDataResource(resource_id); + EXPECT_EQ(string_piece.data(), result.data()); +} + +TEST(ResourceBundle, DelegateGetLocalizedString) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + string16 data = ASCIIToUTF16("My test data"); + int resource_id = 5; + + EXPECT_CALL(delegate, GetLocalizedStringMock(resource_id)) + .Times(1) + .WillOnce(Return(data)); + + string16 result = resource_bundle.GetLocalizedString(resource_id); + EXPECT_EQ(data, result); +} + +TEST(ResourceBundle, DelegateGetFont) { + MockResourceBundleDelegate delegate; + ResourceBundle resource_bundle(&delegate); + + // Should be called once for each font type. When we return NULL the default + // font will be created. + gfx::Font* test_font = NULL; + EXPECT_CALL(delegate, GetFontMock(_)) + .Times(7) + .WillRepeatedly(Return(test_font)); + + const gfx::Font* font = + &resource_bundle.GetFont(ui::ResourceBundle::BaseFont); + EXPECT_TRUE(font); +} + +TEST(ResourceBundle, LoadDataResourceBytes) { // On Windows, the default data is compiled into the binary so this does // nothing. ScopedTempDir dir; ASSERT_TRUE(dir.CreateUniqueTempDir()); FilePath data_path = dir.path().Append(FILE_PATH_LITERAL("sample.pak")); - // Dump contents into the pak file. - ASSERT_EQ(file_util::WriteFile(data_path, kSamplePakContents, kSamplePakSize), - static_cast<int>(kSamplePakSize)); + // Put the ResourceBundle in a different scope so that it's destroyed before + // the ScopedTempDir. + { + // Verify that we don't crash when trying to load a resource that is not + // found. In some cases, we fail to mmap resources.pak, but try to keep + // going anyway. + ResourceBundle resource_bundle(NULL); + + // Dump contents into the pak file. + ASSERT_EQ(file_util::WriteFile(data_path, kSamplePakContents, + kSamplePakSize), + static_cast<int>(kSamplePakSize)); - // Create a resource bundle from the file. - resource_bundle.LoadTestResources(data_path); + // Create a resource bundle from the file. + resource_bundle.LoadTestResources(data_path); - const int kUnfoundResourceId = 10000; - EXPECT_EQ(NULL, resource_bundle.LoadDataResourceBytes(kUnfoundResourceId)); + const int kUnfoundResourceId = 10000; + EXPECT_EQ(NULL, resource_bundle.LoadDataResourceBytes(kUnfoundResourceId)); - // Give a .pak file that doesn't exist so we will fail to load it. - resource_bundle.AddDataPack(FilePath( - FILE_PATH_LITERAL("non-existant-file.pak")), 1.0); - EXPECT_EQ(NULL, resource_bundle.LoadDataResourceBytes(kUnfoundResourceId)); + // Give a .pak file that doesn't exist so we will fail to load it. + resource_bundle.AddDataPack( + FilePath(FILE_PATH_LITERAL("non-existant-file.pak")), + 1.0); + EXPECT_EQ(NULL, resource_bundle.LoadDataResourceBytes(kUnfoundResourceId)); + } } TEST(ResourceBundle, LocaleDataPakExists) { + ResourceBundle resource_bundle(NULL); + // Check that ResourceBundle::LocaleDataPakExists returns the correct results. - EXPECT_TRUE(ResourceBundle::LocaleDataPakExists("en-US")); - EXPECT_FALSE(ResourceBundle::LocaleDataPakExists("not_a_real_locale")); + EXPECT_TRUE(resource_bundle.LocaleDataPakExists("en-US")); + EXPECT_FALSE(resource_bundle.LocaleDataPakExists("not_a_real_locale")); } } // namespace ui diff --git a/ui/base/resource/resource_bundle_win.cc b/ui/base/resource/resource_bundle_win.cc index 4cdc8c3..e181e7d 100644 --- a/ui/base/resource/resource_bundle_win.cc +++ b/ui/base/resource/resource_bundle_win.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/path_service.h" +#include "base/utf_string_conversions.h" #include "ui/base/layout.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/resource/resource_data_dll_win.h" @@ -27,7 +28,9 @@ FilePath GetResourcesPakFilePath(const std::string& pak_name) { FilePath path; if (PathService::Get(base::DIR_MODULE, &path)) return path.AppendASCII(pak_name.c_str()); - return FilePath(); + + // Return just the name of the pack file. + return FilePath(ASCIIToUTF16(pak_name)); } } // end anonymous namespace @@ -53,14 +56,14 @@ void ResourceBundle::LoadCommonResources() { default: if (use_hidpi) { AddDataPack(GetResourcesPakFilePath("theme_resources_2x.pak"), - ResourceHandle::kScaleFactor200x); + ResourceHandle::kScaleFactor200x); AddDataPack(GetResourcesPakFilePath("ui_resources_2x.pak"), - ResourceHandle::kScaleFactor200x); + ResourceHandle::kScaleFactor200x); } else { AddDataPack(GetResourcesPakFilePath("theme_resources_standard.pak"), - ResourceHandle::kScaleFactor100x); + ResourceHandle::kScaleFactor100x); AddDataPack(GetResourcesPakFilePath("ui_resources_standard.pak"), - ResourceHandle::kScaleFactor100x); + ResourceHandle::kScaleFactor100x); } break; } |