#include "ui/base/resource/resource_bundle.h"
+#include <limits>
#include <vector>
+#include "base/big_endian.h"
#include "base/command_line.h"
-#include "base/file_util.h"
+#include "base/files/file.h"
+#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/memory/ref_counted_memory.h"
#include "base/metrics/histogram.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "build/build_config.h"
-#include "grit/app_locale_settings.h"
-#include "net/base/big_endian.h"
#include "skia/ext/image_operations.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/safe_integer_conversions.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/size_conversions.h"
-#include "ui/gfx/skbitmap_operations.h"
+#include "ui/strings/grit/app_locale_settings.h"
+
+#if defined(OS_ANDROID)
+#include "ui/base/resource/resource_bundle_android.h"
+#endif
#if defined(OS_CHROMEOS)
#include "ui/base/l10n/l10n_util.h"
const unsigned char kPngScaleChunkType[4] = { 'c', 's', 'C', 'l' };
const unsigned char kPngDataChunkType[4] = { 'I', 'D', 'A', 'T' };
+#if !defined(OS_MACOSX)
+const char kPakFileSuffix[] = ".pak";
+#endif
+
ResourceBundle* g_shared_instance_ = NULL;
-void InitDefaultFont() {
-#if defined(OS_CHROMEOS)
- gfx::PlatformFontPango::SetDefaultFontDescription(
- l10n_util::GetStringUTF8(IDS_UI_FONT_FAMILY_CROS));
+void InitDefaultFontList() {
+#if defined(OS_CHROMEOS) && defined(USE_PANGO)
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance();
+ std::string font_family = base::UTF16ToUTF8(
+ rb.GetLocalizedString(IDS_UI_FONT_FAMILY_CROS));
+ gfx::FontList::SetDefaultFontDescription(font_family);
+
+ // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to
+ // the font list is done. We will no longer need SetDefaultFontDescription()
+ // after every client gets started using a FontList instead of a Font.
+ gfx::PlatformFontPango::SetDefaultFontDescription(font_family);
+#else
+ // Use a single default font as the default font list.
+ gfx::FontList::SetDefaultFontDescription(std::string());
#endif
}
+#if defined(OS_ANDROID)
+// Returns the scale factor closest to |scale| from the full list of factors.
+// Note that it does NOT rely on the list of supported scale factors.
+// Finding the closest match is inefficient and shouldn't be done frequently.
+ScaleFactor FindClosestScaleFactorUnsafe(float scale) {
+ float smallest_diff = std::numeric_limits<float>::max();
+ ScaleFactor closest_match = SCALE_FACTOR_100P;
+ for (int i = SCALE_FACTOR_100P; i < NUM_SCALE_FACTORS; ++i) {
+ const ScaleFactor scale_factor = static_cast<ScaleFactor>(i);
+ float diff = std::abs(GetScaleForScaleFactor(scale_factor) - scale);
+ if (diff < smallest_diff) {
+ closest_match = scale_factor;
+ smallest_diff = diff;
+ }
+ }
+ return closest_match;
+}
+#endif // OS_ANDROID
+
} // namespace
// An ImageSkiaSource that loads bitmaps for the requested scale factor from
ScaleFactor scale_factor = GetSupportedScaleFactor(scale);
bool found = rb_->LoadBitmap(resource_id_, &scale_factor,
&image, &fell_back_to_1x);
- // Force to a supported scale.
- scale = ui::GetImageScale(scale_factor);
if (!found)
return gfx::ImageSkiaRep();
+ // If the resource is in the package with SCALE_FACTOR_NONE, it
+ // can be used in any scale factor. The image is maked as "unscaled"
+ // so that the ImageSkia do not automatically scale.
+ if (scale_factor == ui::SCALE_FACTOR_NONE)
+ return gfx::ImageSkiaRep(image, 0.0f);
+
if (fell_back_to_1x) {
// GRIT fell back to the 100% image, so rescale it to the correct size.
image = skia::ImageOperations::Resize(
skia::ImageOperations::RESIZE_LANCZOS3,
gfx::ToCeiledInt(image.width() * scale),
gfx::ToCeiledInt(image.height() * scale));
- // If --highlight-missing-scaled-resources is specified, log the resource
- // id and blend the created resource with red.
- if (ShouldHighlightMissingScaledResources()) {
- LOG(ERROR) << "Missing " << scale << "x scaled resource. id="
- << resource_id_;
-
- SkBitmap mask;
- mask.setConfig(SkBitmap::kARGB_8888_Config,
- image.width(), image.height());
- mask.allocPixels();
- mask.eraseColor(SK_ColorRED);
- image = SkBitmapOperations::CreateBlendedBitmap(image, mask, 0.2);
- }
+ } else {
+ scale = GetScaleForScaleFactor(scale_factor);
}
-
return gfx::ImageSkiaRep(image, scale);
}
// static
std::string ResourceBundle::InitSharedInstanceWithLocale(
- const std::string& pref_locale, Delegate* delegate) {
- InitSharedInstance(delegate);
- g_shared_instance_->LoadCommonResources();
- std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
- InitDefaultFont();
- return result;
-}
-
-// static
-std::string ResourceBundle::InitSharedInstanceLocaleOnly(
- const std::string& pref_locale, Delegate* delegate) {
+ const std::string& pref_locale,
+ Delegate* delegate,
+ LoadResources load_resources) {
InitSharedInstance(delegate);
+ if (load_resources == LOAD_COMMON_RESOURCES)
+ g_shared_instance_->LoadCommonResources();
std::string result = g_shared_instance_->LoadLocaleResources(pref_locale);
- InitDefaultFont();
+ InitDefaultFontList();
return result;
}
// static
-void ResourceBundle::InitSharedInstanceWithPakFile(
- base::PlatformFile pak_file, bool should_load_common_resources) {
+void ResourceBundle::InitSharedInstanceWithPakFileRegion(
+ base::File pak_file,
+ const base::MemoryMappedFile::Region& region) {
InitSharedInstance(NULL);
- if (should_load_common_resources)
- g_shared_instance_->LoadCommonResources();
-
- scoped_ptr<DataPack> data_pack(
- new DataPack(SCALE_FACTOR_100P));
- if (!data_pack->LoadFromFile(pak_file)) {
+ scoped_ptr<DataPack> data_pack(new DataPack(SCALE_FACTOR_100P));
+ if (!data_pack->LoadFromFileRegion(pak_file.Pass(), region)) {
NOTREACHED() << "failed to load pak file";
return;
}
g_shared_instance_->locale_resources_data_.reset(data_pack.release());
- InitDefaultFont();
+ InitDefaultFontList();
}
// static
InitSharedInstance(NULL);
g_shared_instance_->LoadTestResources(path, path);
- InitDefaultFont();
+ InitDefaultFontList();
}
// static
}
bool ResourceBundle::LocaleDataPakExists(const std::string& locale) {
- return !GetLocaleFilePath(locale, true).empty();
+ bool locale_file_path_exists = !GetLocaleFilePath(locale, true).empty();
+#if defined(OS_ANDROID)
+ // TODO(mkosiba,primiano): Chrome should mmap the .pak files too, in which
+ // case we'd not need to check if locale_file_path_exists here.
+ // http://crbug.com/394502.
+ return locale_file_path_exists ||
+ AssetContainedInApk(locale + kPakFileSuffix);
+#else
+ return locale_file_path_exists;
+#endif
}
void ResourceBundle::AddDataPackFromPath(const base::FilePath& path,
AddDataPackFromPathInternal(path, scale_factor, true);
}
-void ResourceBundle::AddDataPackFromFile(base::PlatformFile file,
+void ResourceBundle::AddDataPackFromFile(base::File file,
ScaleFactor scale_factor) {
+ AddDataPackFromFileRegion(
+ file.Pass(), base::MemoryMappedFile::Region::kWholeFile, scale_factor);
+}
+
+void ResourceBundle::AddDataPackFromFileRegion(
+ base::File file,
+ const base::MemoryMappedFile::Region& region,
+ ScaleFactor scale_factor) {
scoped_ptr<DataPack> data_pack(
new DataPack(scale_factor));
- if (data_pack->LoadFromFile(file)) {
+ if (data_pack->LoadFromFileRegion(file.Pass(), region)) {
AddDataPack(data_pack.release());
} else {
LOG(ERROR) << "Failed to load data pack from file."
PathService::Get(ui::DIR_LOCALES, &locale_file_path);
- if (!locale_file_path.empty())
- locale_file_path = locale_file_path.AppendASCII(app_locale + ".pak");
+ if (!locale_file_path.empty()) {
+ locale_file_path =
+ locale_file_path.AppendASCII(app_locale + kPakFileSuffix);
+ }
if (delegate_) {
locale_file_path =
DCHECK(!locale_resources_data_.get()) << "locale.pak already loaded";
std::string app_locale = l10n_util::GetApplicationLocale(pref_locale);
base::FilePath locale_file_path = GetOverriddenPakPath();
- if (locale_file_path.empty()) {
- CommandLine* command_line = CommandLine::ForCurrentProcess();
- if (command_line->HasSwitch(switches::kLocalePak)) {
- locale_file_path =
- command_line->GetSwitchValuePath(switches::kLocalePak);
- } else {
- locale_file_path = GetLocaleFilePath(app_locale, true);
- }
- }
+ if (locale_file_path.empty())
+ locale_file_path = GetLocaleFilePath(app_locale, true);
if (locale_file_path.empty()) {
// It's possible that there is no locale.pak.
void ResourceBundle::LoadTestResources(const base::FilePath& path,
const base::FilePath& locale_path) {
+ DCHECK(!ui::GetSupportedScaleFactors().empty());
+ const ScaleFactor scale_factor(ui::GetSupportedScaleFactors()[0]);
// Use the given resource pak for both common and localized resources.
- scoped_ptr<DataPack> data_pack(
- new DataPack(SCALE_FACTOR_100P));
+ scoped_ptr<DataPack> data_pack(new DataPack(scale_factor));
if (!path.empty() && data_pack->LoadFromPath(path))
AddDataPack(data_pack.release());
if (!locale_path.empty() && data_pack->LoadFromPath(locale_path)) {
locale_resources_data_.reset(data_pack.release());
} else {
- locale_resources_data_.reset(
- new DataPack(ui::SCALE_FACTOR_NONE));
+ locale_resources_data_.reset(new DataPack(ui::SCALE_FACTOR_NONE));
}
}
overridden_pak_path_ = pak_path;
}
+void ResourceBundle::OverrideLocaleStringResource(
+ int message_id,
+ const base::string16& string) {
+ overridden_locale_strings_[message_id] = string;
+}
+
const base::FilePath& ResourceBundle::GetOverriddenPakPath() {
return overridden_pak_path_;
}
std::string ResourceBundle::ReloadLocaleResources(
const std::string& pref_locale) {
base::AutoLock lock_scope(*locale_resources_data_lock_);
+
+ // Remove all overriden strings, as they will not be valid for the new locale.
+ overridden_locale_strings_.clear();
+
UnloadLocaleResources();
return LoadLocaleResources(pref_locale);
}
DCHECK(!data_packs_.empty()) <<
"Missing call to SetResourcesDataDLL?";
-#if defined(OS_CHROMEOS)
- ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor();
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
+ ui::ScaleFactor scale_factor_to_load = GetMaxScaleFactor();
#else
- ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
+ ui::ScaleFactor scale_factor_to_load = ui::SCALE_FACTOR_100P;
#endif
- float scale = GetImageScale(scale_factor_to_load);
// TODO(oshima): Consider reading the image size from png IHDR chunk and
// skip decoding here and remove #ifdef below.
// ResourceBundle::GetSharedInstance() is destroyed after the
// BrowserMainLoop has finished running. |image_skia| is guaranteed to be
// destroyed before the resource bundle is destroyed.
gfx::ImageSkia image_skia(new ResourceBundleImageSource(this, resource_id),
- scale);
+ GetScaleForScaleFactor(scale_factor_to_load));
if (image_skia.isNull()) {
LOG(WARNING) << "Unable to load image with id " << resource_id;
NOTREACHED(); // Want to assert in debug mode.
base::StringPiece data =
GetRawDataResourceForScale(resource_id, scale_factor);
if (!data.empty()) {
- bytes = new base::RefCountedStaticMemory(
- reinterpret_cast<const unsigned char*>(data.data()), data.length());
+ bytes = new base::RefCountedStaticMemory(data.data(), data.length());
}
}
}
for (size_t i = 0; i < data_packs_.size(); i++) {
if ((data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_100P ||
+ data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_200P ||
data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE) &&
data_packs_[i]->GetStringPiece(resource_id, &data))
return data;
return base::StringPiece();
}
-string16 ResourceBundle::GetLocalizedString(int message_id) {
- string16 string;
+base::string16 ResourceBundle::GetLocalizedString(int message_id) {
+ base::string16 string;
if (delegate_ && delegate_->GetLocalizedString(message_id, &string))
return string;
// we're using them.
base::AutoLock lock_scope(*locale_resources_data_lock_);
+ IdToStringMap::const_iterator it =
+ overridden_locale_strings_.find(message_id);
+ if (it != overridden_locale_strings_.end())
+ return it->second;
+
// 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();
+ return base::string16();
}
base::StringPiece data;
data = GetRawDataResource(message_id);
if (data.empty()) {
NOTREACHED() << "unable to find resource: " << message_id;
- return string16();
+ return base::string16();
}
}
<< "requested localized string from binary pack file";
// Data pack encodes strings as either UTF8 or UTF16.
- string16 msg;
+ base::string16 msg;
if (encoding == ResourceHandle::UTF16) {
- msg = string16(reinterpret_cast<const char16*>(data.data()),
- data.length() / 2);
+ msg = base::string16(reinterpret_cast<const base::char16*>(data.data()),
+ data.length() / 2);
} else if (encoding == ResourceHandle::UTF8) {
- msg = UTF8ToUTF16(data);
+ msg = base::UTF8ToUTF16(data);
}
return msg;
}
}
ScaleFactor ResourceBundle::GetMaxScaleFactor() const {
-#if defined(OS_CHROMEOS)
+#if defined(OS_CHROMEOS) || defined(OS_WIN)
return max_scale_factor_;
#else
return GetSupportedScaleFactors().back();
#endif
}
+bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) {
+ const std::vector<ScaleFactor>& supported_scale_factors =
+ ui::GetSupportedScaleFactors();
+ return std::find(supported_scale_factors.begin(),
+ supported_scale_factors.end(),
+ scale_factor) != supported_scale_factors.end();
+}
+
ResourceBundle::ResourceBundle(Delegate* delegate)
: delegate_(delegate),
images_and_fonts_lock_(new base::Lock),
DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice";
g_shared_instance_ = new ResourceBundle(delegate);
static std::vector<ScaleFactor> supported_scale_factors;
-#if !defined(OS_IOS)
+#if !defined(OS_IOS) && !defined(OS_WIN)
// On platforms other than iOS, 100P is always a supported scale factor.
+ // For Windows we have a separate case in this function.
supported_scale_factors.push_back(SCALE_FACTOR_100P);
#endif
-
#if defined(OS_ANDROID)
const gfx::Display display =
gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
supported_scale_factors.push_back(closest);
#elif defined(OS_IOS)
gfx::Display display = gfx::Screen::GetNativeScreen()->GetPrimaryDisplay();
- if (display.device_scale_factor() > 1.0) {
+ if (display.device_scale_factor() > 2.0) {
+ DCHECK_EQ(3.0, display.device_scale_factor());
+ supported_scale_factors.push_back(SCALE_FACTOR_300P);
+ } else if (display.device_scale_factor() > 1.0) {
DCHECK_EQ(2.0, display.device_scale_factor());
supported_scale_factors.push_back(SCALE_FACTOR_200P);
} else {
#elif defined(OS_CHROMEOS)
// TODO(oshima): Include 200P only if the device support 200P
supported_scale_factors.push_back(SCALE_FACTOR_200P);
+#elif defined(OS_LINUX) && defined(ENABLE_HIDPI)
+ supported_scale_factors.push_back(SCALE_FACTOR_200P);
+#elif defined(OS_WIN)
+ bool default_to_100P = true;
+ if (gfx::IsHighDPIEnabled()) {
+ // On Windows if the dpi scale is greater than 1.25 on high dpi machines
+ // downscaling from 200 percent looks better than scaling up from 100
+ // percent.
+ if (gfx::GetDPIScale() > 1.25) {
+ supported_scale_factors.push_back(SCALE_FACTOR_200P);
+ default_to_100P = false;
+ }
+ }
+ if (default_to_100P)
+ supported_scale_factors.push_back(SCALE_FACTOR_100P);
#endif
ui::SetSupportedScaleFactors(supported_scale_factors);
#if defined(OS_WIN)
// Must be called _after_ supported scale factors are set since it
// uses them.
- ui::win::InitDeviceScaleFactor();
+ // Don't initialize the device scale factor if it has already been
+ // initialized.
+ if (!gfx::win::IsDeviceScaleFactorSet())
+ ui::win::InitDeviceScaleFactor();
#endif
}
void ResourceBundle::AddDataPack(DataPack* data_pack) {
data_packs_.push_back(data_pack);
- if (GetImageScale(data_pack->GetScaleFactor()) >
- GetImageScale(max_scale_factor_))
+ if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) >
+ GetScaleForScaleFactor(max_scale_factor_))
max_scale_factor_ = data_pack->GetScaleFactor();
}
large_bold_font_list_ = GetFontListFromDelegate(LargeBoldFont);
}
- if (!base_font_list_.get()) {
-#if defined(OS_CHROMEOS)
- base_font_list_.reset(new gfx::FontList(
- l10n_util::GetStringUTF8(IDS_UI_FONT_FAMILY_CROS)));
-#else
+ if (!base_font_list_.get())
base_font_list_.reset(new gfx::FontList());
-#endif
- }
if (!bold_font_list_.get()) {
bold_font_list_.reset(new gfx::FontList());
- *bold_font_list_ = base_font_list_->DeriveFontList(
+ *bold_font_list_ = base_font_list_->DeriveWithStyle(
base_font_list_->GetFontStyle() | gfx::Font::BOLD);
}
if (!small_font_list_.get()) {
small_font_list_.reset(new gfx::FontList());
- *small_font_list_ = base_font_list_->DeriveFontListWithSize(
- base_font_list_->GetFontSize() + kSmallFontSizeDelta);
+ *small_font_list_ =
+ base_font_list_->DeriveWithSizeDelta(kSmallFontSizeDelta);
}
if (!small_bold_font_list_.get()) {
small_bold_font_list_.reset(new gfx::FontList());
- *small_bold_font_list_ = small_font_list_->DeriveFontList(
+ *small_bold_font_list_ = small_font_list_->DeriveWithStyle(
small_font_list_->GetFontStyle() | gfx::Font::BOLD);
}
if (!medium_font_list_.get()) {
medium_font_list_.reset(new gfx::FontList());
- *medium_font_list_ = base_font_list_->DeriveFontListWithSize(
- base_font_list_->GetFontSize() + kMediumFontSizeDelta);
+ *medium_font_list_ =
+ base_font_list_->DeriveWithSizeDelta(kMediumFontSizeDelta);
}
if (!medium_bold_font_list_.get()) {
medium_bold_font_list_.reset(new gfx::FontList());
- *medium_bold_font_list_ = medium_font_list_->DeriveFontList(
+ *medium_bold_font_list_ = medium_font_list_->DeriveWithStyle(
medium_font_list_->GetFontStyle() | gfx::Font::BOLD);
}
if (!large_font_list_.get()) {
large_font_list_.reset(new gfx::FontList());
- *large_font_list_ = base_font_list_->DeriveFontListWithSize(
- base_font_list_->GetFontSize() + kLargeFontSizeDelta);
+ *large_font_list_ =
+ base_font_list_->DeriveWithSizeDelta(kLargeFontSizeDelta);
}
if (!large_bold_font_list_.get()) {
large_bold_font_list_.reset(new gfx::FontList());
- *large_bold_font_list_ = large_font_list_->DeriveFontList(
+ *large_bold_font_list_ = large_font_list_->DeriveWithStyle(
large_font_list_->GetFontStyle() | gfx::Font::BOLD);
}
}
bool* fell_back_to_1x) const {
DCHECK(fell_back_to_1x);
for (size_t i = 0; i < data_packs_.size(); ++i) {
- // If the resource is in the package with SCALE_FACTOR_NONE, it
- // can be used in any scale factor, but set 100P in ImageSkia so
- // that it will be scaled property.
if (data_packs_[i]->GetScaleFactor() == ui::SCALE_FACTOR_NONE &&
LoadBitmap(*data_packs_[i], resource_id, bitmap, fell_back_to_1x)) {
- *scale_factor = ui::SCALE_FACTOR_100P;
DCHECK(!*fell_back_to_1x);
+ *scale_factor = ui::SCALE_FACTOR_NONE;
return true;
}
if (data_packs_[i]->GetScaleFactor() == *scale_factor &&
if (empty_image_.IsEmpty()) {
// The placeholder bitmap is bright red so people notice the problem.
SkBitmap bitmap;
- bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32, 32);
- bitmap.allocPixels();
+ bitmap.allocN32Pixels(32, 32);
bitmap.eraseARGB(255, 255, 0, 0);
empty_image_ = gfx::Image::CreateFrom1xBitmap(bitmap);
}
}
// static
-bool ResourceBundle::ShouldHighlightMissingScaledResources() {
- return CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kHighlightMissingScaledResources);
-}
-
-// static
bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf,
- size_t size) {
+ size_t size) {
if (size < arraysize(kPngMagic) ||
memcmp(buf, kPngMagic, arraysize(kPngMagic)) != 0) {
// Data invalid or a JPEG.
if (size - pos < kPngChunkMetadataSize)
break;
uint32 length = 0;
- net::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
+ base::ReadBigEndian(reinterpret_cast<const char*>(buf + pos), &length);
if (size - pos - kPngChunkMetadataSize < length)
break;
if (length == 0 && memcmp(buf + pos + sizeof(uint32), kPngScaleChunkType,