Commit bf1b96ef authored by Tibor Goldschwendt's avatar Tibor Goldschwendt Committed by Commit Bot

[android] Init ICU with extra data if extra ICU module is installed

The purpose of this CL is to provide ICU support for 30 extra languages
(see crrev/c/1648623 for more details). crrev/c/1811973 adds a feature
module that delivers an extra ICU data file containing support for the
aforementioned extra languages. To make use of this extra data file, we
check at browser process startup whether the extra ICU feature module is
installed and if so init ICU with the extra data file inside the module.
We retrieve the module install state from the ContentBrowserClient as
feature modules are a chrome layer concept and the ICU initialization
happens in the content layer. The extra ICU data file is applied
_before_ the main ICU data file. Otherwise, the main ICU data file would
override the extra languages.

Requires crrev/c/1834464.

Bug: 1006794
Change-Id: Ic7c14bf3437309b90cf2ce205ade14da937203c0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1818876
Commit-Queue: Tibor Goldschwendt <tiborg@chromium.org>
Reviewed-by: default avatarBo <boliu@chromium.org>
Reviewed-by: default avatarTed Choc <tedchoc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#703491}
parent 70f997f7
...@@ -34,6 +34,7 @@ import("//build/nocompile.gni") ...@@ -34,6 +34,7 @@ import("//build/nocompile.gni")
import("//build/timestamp.gni") import("//build/timestamp.gni")
import("//testing/libfuzzer/fuzzer_test.gni") import("//testing/libfuzzer/fuzzer_test.gni")
import("//testing/test.gni") import("//testing/test.gni")
import("//third_party/icu/config.gni")
if (is_mac) { if (is_mac) {
# Used to generate fuzzer corpus :base_mach_port_rendezvous_convert_corpus. # Used to generate fuzzer corpus :base_mach_port_rendezvous_convert_corpus.
...@@ -2549,6 +2550,7 @@ test("base_unittests") { ...@@ -2549,6 +2550,7 @@ test("base_unittests") {
"i18n/character_encoding_unittest.cc", "i18n/character_encoding_unittest.cc",
"i18n/file_util_icu_unittest.cc", "i18n/file_util_icu_unittest.cc",
"i18n/icu_string_conversions_unittest.cc", "i18n/icu_string_conversions_unittest.cc",
"i18n/icu_util_unittest.cc",
"i18n/message_formatter_unittest.cc", "i18n/message_formatter_unittest.cc",
"i18n/number_formatting_unittest.cc", "i18n/number_formatting_unittest.cc",
"i18n/rtl_unittest.cc", "i18n/rtl_unittest.cc",
...@@ -2929,6 +2931,15 @@ test("base_unittests") { ...@@ -2929,6 +2931,15 @@ test("base_unittests") {
] ]
} }
if (icu_use_data_file) {
if (is_android) {
deps += [ "//third_party/icu:icu_extra_assets" ]
} else {
deps += [ "//third_party/icu:extra_icudata" ]
data += [ "$root_out_dir/icudtl_extra.dat" ]
}
}
if (is_ios) { if (is_ios) {
# ios does not use test_launcher to run gtests. # ios does not use test_launcher to run gtests.
sources -= [ sources -= [
......
...@@ -69,9 +69,11 @@ wchar_t g_debug_icu_pf_filename[_MAX_PATH]; ...@@ -69,9 +69,11 @@ wchar_t g_debug_icu_pf_filename[_MAX_PATH];
// build pkg configurations, etc). 'l' stands for Little Endian. // build pkg configurations, etc). 'l' stands for Little Endian.
// This variable is exported through the header file. // This variable is exported through the header file.
const char kIcuDataFileName[] = "icudtl.dat"; const char kIcuDataFileName[] = "icudtl.dat";
const char kIcuExtraDataFileName[] = "icudtl_extra.dat";
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat"; const char kAssetsPathPrefix[] = "assets/";
#endif #endif // defined(OS_ANDROID)
// File handle intentionally never closed. Not using File here because its // File handle intentionally never closed. Not using File here because its
// Windows implementation guards against two instances owning the same // Windows implementation guards against two instances owning the same
...@@ -79,25 +81,31 @@ const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat"; ...@@ -79,25 +81,31 @@ const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
PlatformFile g_icudtl_pf = kInvalidPlatformFile; PlatformFile g_icudtl_pf = kInvalidPlatformFile;
MemoryMappedFile* g_icudtl_mapped_file = nullptr; MemoryMappedFile* g_icudtl_mapped_file = nullptr;
MemoryMappedFile::Region g_icudtl_region; MemoryMappedFile::Region g_icudtl_region;
PlatformFile g_icudtl_extra_pf = kInvalidPlatformFile;
void LazyInitIcuDataFile() { MemoryMappedFile* g_icudtl_extra_mapped_file = nullptr;
if (g_icudtl_pf != kInvalidPlatformFile) { MemoryMappedFile::Region g_icudtl_extra_region;
return;
} struct PfRegion {
public:
PlatformFile pf;
MemoryMappedFile::Region region;
};
std::unique_ptr<PfRegion> OpenIcuDataFile(const std::string& filename) {
auto result = std::make_unique<PfRegion>();
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
int fd = result->pf =
android::OpenApkAsset(kAndroidAssetsIcuDataFileName, &g_icudtl_region); android::OpenApkAsset(kAssetsPathPrefix + filename, &result->region);
g_icudtl_pf = fd; if (result->pf != -1) {
if (fd != -1) { return result;
return;
} }
// For unit tests, data file is located on disk, so try there as a fallback.
#endif // defined(OS_ANDROID) #endif // defined(OS_ANDROID)
// For unit tests, data file is located on disk, so try there as a fallback.
#if !defined(OS_MACOSX) #if !defined(OS_MACOSX)
FilePath data_path; FilePath data_path;
if (!PathService::Get(DIR_ASSETS, &data_path)) { if (!PathService::Get(DIR_ASSETS, &data_path)) {
LOG(ERROR) << "Can't find " << kIcuDataFileName; LOG(ERROR) << "Can't find " << filename;
return; return nullptr;
} }
#if defined(OS_WIN) #if defined(OS_WIN)
// TODO(brucedawson): http://crbug.com/445616 // TODO(brucedawson): http://crbug.com/445616
...@@ -105,7 +113,7 @@ void LazyInitIcuDataFile() { ...@@ -105,7 +113,7 @@ void LazyInitIcuDataFile() {
wcscpy_s(tmp_buffer, as_wcstr(data_path.value())); wcscpy_s(tmp_buffer, as_wcstr(data_path.value()));
debug::Alias(tmp_buffer); debug::Alias(tmp_buffer);
#endif #endif
data_path = data_path.AppendASCII(kIcuDataFileName); data_path = data_path.AppendASCII(filename);
#if defined(OS_WIN) #if defined(OS_WIN)
// TODO(brucedawson): http://crbug.com/445616 // TODO(brucedawson): http://crbug.com/445616
...@@ -116,8 +124,7 @@ void LazyInitIcuDataFile() { ...@@ -116,8 +124,7 @@ void LazyInitIcuDataFile() {
#else // !defined(OS_MACOSX) #else // !defined(OS_MACOSX)
// Assume it is in the framework bundle's Resources directory. // Assume it is in the framework bundle's Resources directory.
ScopedCFTypeRef<CFStringRef> data_file_name( ScopedCFTypeRef<CFStringRef> data_file_name(SysUTF8ToCFStringRef(filename));
SysUTF8ToCFStringRef(kIcuDataFileName));
FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name); FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
#if defined(OS_IOS) #if defined(OS_IOS)
FilePath override_data_path = ios::FilePathOfEmbeddedICU(); FilePath override_data_path = ios::FilePathOfEmbeddedICU();
...@@ -126,8 +133,8 @@ void LazyInitIcuDataFile() { ...@@ -126,8 +133,8 @@ void LazyInitIcuDataFile() {
} }
#endif // !defined(OS_IOS) #endif // !defined(OS_IOS)
if (data_path.empty()) { if (data_path.empty()) {
LOG(ERROR) << kIcuDataFileName << " not found in bundle"; LOG(ERROR) << filename << " not found in bundle";
return; return nullptr;
} }
#endif // !defined(OS_MACOSX) #endif // !defined(OS_MACOSX)
File file(data_path, File::FLAG_OPEN | File::FLAG_READ); File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
...@@ -139,8 +146,8 @@ void LazyInitIcuDataFile() { ...@@ -139,8 +146,8 @@ void LazyInitIcuDataFile() {
g_debug_icu_pf_filename[0] = 0; g_debug_icu_pf_filename[0] = 0;
#endif // OS_WIN #endif // OS_WIN
g_icudtl_pf = file.TakePlatformFile(); result->pf = file.TakePlatformFile();
g_icudtl_region = MemoryMappedFile::Region::kWholeFile; result->region = MemoryMappedFile::Region::kWholeFile;
} }
#if defined(OS_WIN) #if defined(OS_WIN)
else { else {
...@@ -150,6 +157,47 @@ void LazyInitIcuDataFile() { ...@@ -150,6 +157,47 @@ void LazyInitIcuDataFile() {
wcscpy_s(g_debug_icu_pf_filename, as_wcstr(data_path.value())); wcscpy_s(g_debug_icu_pf_filename, as_wcstr(data_path.value()));
} }
#endif // OS_WIN #endif // OS_WIN
return result;
}
void LazyOpenIcuDataFile() {
if (g_icudtl_pf != kInvalidPlatformFile) {
return;
}
auto pf_region = OpenIcuDataFile(kIcuDataFileName);
if (!pf_region) {
return;
}
g_icudtl_pf = pf_region->pf;
g_icudtl_region = pf_region->region;
}
int LoadIcuData(PlatformFile data_fd,
const MemoryMappedFile::Region& data_region,
std::unique_ptr<MemoryMappedFile>* out_mapped_data_file,
UErrorCode* out_error_code) {
if (data_fd == kInvalidPlatformFile) {
LOG(ERROR) << "Invalid file descriptor to ICU data received.";
return 1; // To debug http://crbug.com/445616.
}
out_mapped_data_file->reset(new MemoryMappedFile());
if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) {
LOG(ERROR) << "Couldn't mmap icu data file";
return 2; // To debug http://crbug.com/445616.
}
(*out_error_code) = U_ZERO_ERROR;
udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()),
out_error_code);
if (U_FAILURE(*out_error_code)) {
LOG(ERROR) << "Failed to initialize ICU with data file: "
<< u_errorName(*out_error_code);
return 3; // To debug http://crbug.com/445616.
}
return 0;
} }
bool InitializeICUWithFileDescriptorInternal( bool InitializeICUWithFileDescriptorInternal(
...@@ -160,28 +208,20 @@ bool InitializeICUWithFileDescriptorInternal( ...@@ -160,28 +208,20 @@ bool InitializeICUWithFileDescriptorInternal(
g_debug_icu_load = 0; // To debug http://crbug.com/445616. g_debug_icu_load = 0; // To debug http://crbug.com/445616.
return true; return true;
} }
if (data_fd == kInvalidPlatformFile) {
g_debug_icu_load = 1; // To debug http://crbug.com/445616.
LOG(ERROR) << "Invalid file descriptor to ICU data received.";
return false;
}
std::unique_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile()); std::unique_ptr<MemoryMappedFile> mapped_file;
if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) { UErrorCode err;
g_debug_icu_load = 2; // To debug http://crbug.com/445616. g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err);
LOG(ERROR) << "Couldn't mmap icu data file"; if (g_debug_icu_load == 1 || g_debug_icu_load == 2) {
return false; return false;
} }
g_icudtl_mapped_file = icudtl_mapped_file.release(); g_icudtl_mapped_file = mapped_file.release();
UErrorCode err = U_ZERO_ERROR; if (g_debug_icu_load == 3) {
udata_setCommonData(const_cast<uint8_t*>(g_icudtl_mapped_file->data()), &err);
if (err != U_ZERO_ERROR) {
g_debug_icu_load = 3; // To debug http://crbug.com/445616.
g_debug_icu_last_error = err; g_debug_icu_last_error = err;
} }
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
else { else if (g_debug_icu_load == 0) {
// On Android, we can't leave it up to ICU to set the default timezone // On Android, we can't leave it up to ICU to set the default timezone
// because ICU's timezone detection does not work in many timezones (e.g. // because ICU's timezone detection does not work in many timezones (e.g.
// Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
...@@ -195,7 +235,7 @@ bool InitializeICUWithFileDescriptorInternal( ...@@ -195,7 +235,7 @@ bool InitializeICUWithFileDescriptorInternal(
#endif #endif
// Never try to load ICU data from files. // Never try to load ICU data from files.
udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
return err == U_ZERO_ERROR; return U_SUCCESS(err);
} }
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#endif // !defined(OS_NACL) #endif // !defined(OS_NACL)
...@@ -204,7 +244,23 @@ bool InitializeICUWithFileDescriptorInternal( ...@@ -204,7 +244,23 @@ bool InitializeICUWithFileDescriptorInternal(
#if !defined(OS_NACL) #if !defined(OS_NACL)
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#if defined(OS_ANDROID) bool InitializeExtraICUWithFileDescriptor(
PlatformFile data_fd,
const MemoryMappedFile::Region& data_region) {
if (g_icudtl_pf != kInvalidPlatformFile) {
// Must call InitializeExtraICUWithFileDescriptor() before
// InitializeICUWithFileDescriptor().
return false;
}
std::unique_ptr<MemoryMappedFile> mapped_file;
UErrorCode err;
if (LoadIcuData(data_fd, data_region, &mapped_file, &err) != 0) {
return false;
}
g_icudtl_extra_mapped_file = mapped_file.release();
return true;
}
bool InitializeICUWithFileDescriptor( bool InitializeICUWithFileDescriptor(
PlatformFile data_fd, PlatformFile data_fd,
const MemoryMappedFile::Region& data_region) { const MemoryMappedFile::Region& data_region) {
...@@ -220,7 +276,14 @@ PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) { ...@@ -220,7 +276,14 @@ PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
*out_region = g_icudtl_region; *out_region = g_icudtl_region;
return g_icudtl_pf; return g_icudtl_pf;
} }
#endif
PlatformFile GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region) {
if (g_icudtl_extra_pf == kInvalidPlatformFile) {
return kInvalidPlatformFile;
}
*out_region = g_icudtl_extra_region;
return g_icudtl_extra_pf;
}
const uint8_t* GetRawIcuMemory() { const uint8_t* GetRawIcuMemory() {
CHECK(g_icudtl_mapped_file); CHECK(g_icudtl_mapped_file);
...@@ -244,7 +307,28 @@ bool InitializeICUFromRawMemory(const uint8_t* raw_memory) { ...@@ -244,7 +307,28 @@ bool InitializeICUFromRawMemory(const uint8_t* raw_memory) {
#endif #endif
} }
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE bool InitializeExtraICU() {
if (g_icudtl_pf != kInvalidPlatformFile) {
// Must call InitializeExtraICU() before InitializeICU().
return false;
}
auto pf_region = OpenIcuDataFile(kIcuExtraDataFileName);
if (!pf_region) {
return false;
}
g_icudtl_extra_pf = pf_region->pf;
g_icudtl_extra_region = pf_region->region;
std::unique_ptr<MemoryMappedFile> mapped_file;
UErrorCode err;
if (LoadIcuData(g_icudtl_extra_pf, g_icudtl_extra_region, &mapped_file,
&err) != 0) {
return false;
}
g_icudtl_extra_mapped_file = mapped_file.release();
return true;
}
#endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
bool InitializeICU() { bool InitializeICU() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
...@@ -261,7 +345,7 @@ bool InitializeICU() { ...@@ -261,7 +345,7 @@ bool InitializeICU() {
// it is needed. This can fail if the process is sandboxed at that time. // it is needed. This can fail if the process is sandboxed at that time.
// Instead, we map the file in and hand off the data so the sandbox won't // Instead, we map the file in and hand off the data so the sandbox won't
// cause any problems. // cause any problems.
LazyInitIcuDataFile(); LazyOpenIcuDataFile();
result = result =
InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region); InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
#if defined(OS_WIN) #if defined(OS_WIN)
...@@ -299,5 +383,16 @@ void AllowMultipleInitializeCallsForTesting() { ...@@ -299,5 +383,16 @@ void AllowMultipleInitializeCallsForTesting() {
#endif #endif
} }
#if !defined(OS_NACL)
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
void ResetGlobalsForTesting() {
g_icudtl_pf = kInvalidPlatformFile;
g_icudtl_mapped_file = nullptr;
g_icudtl_extra_pf = kInvalidPlatformFile;
g_icudtl_extra_mapped_file = nullptr;
}
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#endif // !defined(OS_NACL)
} // namespace i18n } // namespace i18n
} // namespace base } // namespace base
...@@ -23,18 +23,28 @@ namespace i18n { ...@@ -23,18 +23,28 @@ namespace i18n {
BASE_I18N_EXPORT bool InitializeICU(); BASE_I18N_EXPORT bool InitializeICU();
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#if defined(OS_ANDROID) // Loads ICU's extra data tables from disk for the current process. If used must
// Returns the PlatformFile and Region that was initialized by InitializeICU(). // be called before InitializeICU().
// Use with InitializeICUWithFileDescriptor(). BASE_I18N_EXPORT bool InitializeExtraICU();
// Returns the PlatformFile and Region that was initialized by InitializeICU()
// or InitializeExtraICU(). Use with InitializeICUWithFileDescriptor() or
// InitializeExtraICUWithFileDescriptor().
BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle( BASE_I18N_EXPORT PlatformFile GetIcuDataFileHandle(
MemoryMappedFile::Region* out_region); MemoryMappedFile::Region* out_region);
BASE_I18N_EXPORT PlatformFile
GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region);
// Android uses a file descriptor passed by browser process to initialize ICU // Loads ICU extra data file from file descriptor passed by browser process to
// in render processes. // initialize ICU in render processes. If used must be called before
// InitializeICUWithFileDescriptor().
BASE_I18N_EXPORT bool InitializeExtraICUWithFileDescriptor(
PlatformFile data_fd,
const MemoryMappedFile::Region& data_region);
// Loads ICU data file from file descriptor passed by browser process to
// initialize ICU in render processes.
BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor( BASE_I18N_EXPORT bool InitializeICUWithFileDescriptor(
PlatformFile data_fd, PlatformFile data_fd,
const MemoryMappedFile::Region& data_region); const MemoryMappedFile::Region& data_region);
#endif
// Returns a void pointer to the memory mapped ICU data file. // Returns a void pointer to the memory mapped ICU data file.
// //
...@@ -60,6 +70,12 @@ BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory); ...@@ -60,6 +70,12 @@ BASE_I18N_EXPORT bool InitializeICUFromRawMemory(const uint8_t* raw_memory);
// In a test binary, the call above might occur twice. // In a test binary, the call above might occur twice.
BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting(); BASE_I18N_EXPORT void AllowMultipleInitializeCallsForTesting();
#if !defined(OS_NACL)
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
BASE_I18N_EXPORT void ResetGlobalsForTesting();
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#endif // !defined(OS_NACL)
} // namespace i18n } // namespace i18n
} // namespace base } // namespace base
......
// Copyright (c) 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/i18n/icu_util.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
#if !defined(OS_NACL)
#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
namespace base {
namespace i18n {
class IcuUtilTest : public testing::Test {
protected:
void SetUp() override { ResetGlobalsForTesting(); }
};
#if defined(OS_ANDROID)
TEST_F(IcuUtilTest, InitializeIcuSucceeds) {
bool success = InitializeICU();
ASSERT_TRUE(success);
}
TEST_F(IcuUtilTest, ExtraFileNotInitializedAtStart) {
MemoryMappedFile::Region region;
PlatformFile file = GetIcuExtraDataFileHandle(&region);
ASSERT_EQ(file, kInvalidPlatformFile);
}
TEST_F(IcuUtilTest, InitializeExtraIcuSucceeds) {
bool success = InitializeExtraICU();
ASSERT_TRUE(success);
}
TEST_F(IcuUtilTest, CannotInitializeExtraIcuAfterIcu) {
InitializeICU();
bool success = InitializeExtraICU();
ASSERT_FALSE(success);
}
TEST_F(IcuUtilTest, ExtraFileInitializedAfterInit) {
InitializeExtraICU();
MemoryMappedFile::Region region;
PlatformFile file = GetIcuExtraDataFileHandle(&region);
ASSERT_NE(file, kInvalidPlatformFile);
}
TEST_F(IcuUtilTest, InitializeExtraIcuFromFdSucceeds) {
InitializeExtraICU();
MemoryMappedFile::Region region;
PlatformFile pf = GetIcuExtraDataFileHandle(&region);
bool success = InitializeExtraICUWithFileDescriptor(pf, region);
ASSERT_TRUE(success);
}
TEST_F(IcuUtilTest, CannotInitializeExtraIcuFromFdAfterIcu) {
InitializeExtraICU();
InitializeICU();
MemoryMappedFile::Region region;
PlatformFile pf = GetIcuExtraDataFileHandle(&region);
bool success = InitializeExtraICUWithFileDescriptor(pf, region);
ASSERT_FALSE(success);
}
#endif // defined(OS_ANDROID)
} // namespace i18n
} // namespace base
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
#endif // !defined(OS_NACL)
...@@ -7,7 +7,7 @@ extra_icu_module_desc = { ...@@ -7,7 +7,7 @@ extra_icu_module_desc = {
android_manifest = android_manifest =
"//chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml" "//chrome/android/modules/extra_icu/internal/java/AndroidManifest.xml"
java_deps = [ java_deps = [
"//chrome/android/modules/extra_icu/internal:icudtl_extra_assets", "//third_party/icu:icu_extra_assets",
"//chrome/android/modules/extra_icu/internal:java", "//chrome/android/modules/extra_icu/internal:java",
] ]
} }
...@@ -12,10 +12,3 @@ android_library("java") { ...@@ -12,10 +12,3 @@ android_library("java") {
"//chrome/android/modules/extra_icu/public:java", "//chrome/android/modules/extra_icu/public:java",
] ]
} }
android_assets("icudtl_extra_assets") {
sources = [
"//third_party/icu/android_small/icudtl_extra.dat",
]
disable_compression = true
}
...@@ -405,6 +405,7 @@ ...@@ -405,6 +405,7 @@
#elif defined(OS_ANDROID) #elif defined(OS_ANDROID)
#include "base/android/application_status_listener.h" #include "base/android/application_status_listener.h"
#include "chrome/android/features/dev_ui/buildflags.h" #include "chrome/android/features/dev_ui/buildflags.h"
#include "chrome/android/modules/extra_icu/provider/module_provider.h"
#include "chrome/browser/android/app_hooks.h" #include "chrome/browser/android/app_hooks.h"
#include "chrome/browser/android/chrome_context_util.h" #include "chrome/browser/android/chrome_context_util.h"
#include "chrome/browser/android/devtools_manager_delegate_android.h" #include "chrome/browser/android/devtools_manager_delegate_android.h"
...@@ -5668,3 +5669,10 @@ void ChromeContentBrowserClient::BlockBluetoothScanning( ...@@ -5668,3 +5669,10 @@ void ChromeContentBrowserClient::BlockBluetoothScanning(
CONTENT_SETTINGS_TYPE_BLUETOOTH_SCANNING, std::string(), CONTENT_SETTINGS_TYPE_BLUETOOTH_SCANNING, std::string(),
CONTENT_SETTING_BLOCK); CONTENT_SETTING_BLOCK);
} }
bool ChromeContentBrowserClient::ShouldLoadExtraIcuDataFile() {
#if defined(OS_ANDROID)
return extra_icu::ModuleProvider::IsModuleInstalled();
#endif
return false;
}
...@@ -614,6 +614,8 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { ...@@ -614,6 +614,8 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient {
const url::Origin& requesting_origin, const url::Origin& requesting_origin,
const url::Origin& embedding_origin) override; const url::Origin& embedding_origin) override;
bool ShouldLoadExtraIcuDataFile() override;
content::PreviewsState DetermineAllowedPreviewsWithoutHoldback( content::PreviewsState DetermineAllowedPreviewsWithoutHoldback(
content::PreviewsState initial_state, content::PreviewsState initial_state,
content::NavigationHandle* navigation_handle, content::NavigationHandle* navigation_handle,
......
...@@ -764,15 +764,41 @@ int ContentMainRunnerImpl::Initialize(const ContentMainParams& params) { ...@@ -764,15 +764,41 @@ int ContentMainRunnerImpl::Initialize(const ContentMainParams& params) {
RegisterContentSchemes(delegate_->ShouldLockSchemeRegistry()); RegisterContentSchemes(delegate_->ShouldLockSchemeRegistry());
#if defined(OS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) #if defined(OS_ANDROID) && (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
int icudata_fd = g_fds->MaybeGet(kAndroidICUDataDescriptor); // On Android, we have two ICU data files. A main one with most languages
if (icudata_fd != -1) { // that is expected to always be available and an extra one that is
auto icudata_region = g_fds->GetRegion(kAndroidICUDataDescriptor); // installed separately via a dynamic feature module. If the extra ICU data
if (!base::i18n::InitializeICUWithFileDescriptor(icudata_fd, // file is available we have to apply it _before_ the main ICU data file.
icudata_region)) // Otherwise, the languages of the extra ICU file will be overridden.
if (process_type.empty()) {
// In browser process load ICU data files from disk.
if (GetContentClient()->browser()->ShouldLoadExtraIcuDataFile()) {
if (!base::i18n::InitializeExtraICU()) {
return TerminateForFatalInitializationError();
}
}
if (!base::i18n::InitializeICU()) {
return TerminateForFatalInitializationError(); return TerminateForFatalInitializationError();
}
} else { } else {
if (!base::i18n::InitializeICU()) // In child process map ICU data files loaded by browser process.
int icu_extra_data_fd = g_fds->MaybeGet(kAndroidICUExtraDataDescriptor);
if (icu_extra_data_fd != -1) {
auto icu_extra_data_region =
g_fds->GetRegion(kAndroidICUExtraDataDescriptor);
if (!base::i18n::InitializeExtraICUWithFileDescriptor(
icu_extra_data_fd, icu_extra_data_region)) {
return TerminateForFatalInitializationError();
}
}
int icu_data_fd = g_fds->MaybeGet(kAndroidICUDataDescriptor);
if (icu_data_fd == -1) {
return TerminateForFatalInitializationError(); return TerminateForFatalInitializationError();
}
auto icu_data_region = g_fds->GetRegion(kAndroidICUDataDescriptor);
if (!base::i18n::InitializeICUWithFileDescriptor(icu_data_fd,
icu_data_region)) {
return TerminateForFatalInitializationError();
}
} }
#else #else
if (!base::i18n::InitializeICU()) if (!base::i18n::InitializeICU())
......
...@@ -83,6 +83,12 @@ ChildProcessLauncherHelper::GetFilesToMap() { ...@@ -83,6 +83,12 @@ ChildProcessLauncherHelper::GetFilesToMap() {
base::MemoryMappedFile::Region icu_region; base::MemoryMappedFile::Region icu_region;
int fd = base::i18n::GetIcuDataFileHandle(&icu_region); int fd = base::i18n::GetIcuDataFileHandle(&icu_region);
files_to_register->ShareWithRegion(kAndroidICUDataDescriptor, fd, icu_region); files_to_register->ShareWithRegion(kAndroidICUDataDescriptor, fd, icu_region);
base::MemoryMappedFile::Region icu_extra_region;
int extra_fd = base::i18n::GetIcuExtraDataFileHandle(&icu_extra_region);
if (extra_fd != -1) {
files_to_register->ShareWithRegion(kAndroidICUExtraDataDescriptor, extra_fd,
icu_extra_region);
}
#endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
return files_to_register; return files_to_register;
......
...@@ -1007,4 +1007,8 @@ void ContentBrowserClient::BlockBluetoothScanning( ...@@ -1007,4 +1007,8 @@ void ContentBrowserClient::BlockBluetoothScanning(
const url::Origin& requesting_origin, const url::Origin& requesting_origin,
const url::Origin& embedding_origin) {} const url::Origin& embedding_origin) {}
bool ContentBrowserClient::ShouldLoadExtraIcuDataFile() {
return false;
}
} // namespace content } // namespace content
...@@ -1682,6 +1682,10 @@ class CONTENT_EXPORT ContentBrowserClient { ...@@ -1682,6 +1682,10 @@ class CONTENT_EXPORT ContentBrowserClient {
virtual void BlockBluetoothScanning(content::BrowserContext* browser_context, virtual void BlockBluetoothScanning(content::BrowserContext* browser_context,
const url::Origin& requesting_origin, const url::Origin& requesting_origin,
const url::Origin& embedding_origin); const url::Origin& embedding_origin);
// Returns true if the extra ICU data file is available and should be used to
// initialize ICU.
virtual bool ShouldLoadExtraIcuDataFile();
}; };
} // namespace content } // namespace content
......
...@@ -15,6 +15,7 @@ enum { ...@@ -15,6 +15,7 @@ enum {
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
kAndroidPropertyDescriptor = service_manager::kFirstEmbedderDescriptor, kAndroidPropertyDescriptor = service_manager::kFirstEmbedderDescriptor,
kAndroidICUDataDescriptor, kAndroidICUDataDescriptor,
kAndroidICUExtraDataDescriptor,
#endif #endif
// Reserves 100 to 199 for dynamically generated IDs. // Reserves 100 to 199 for dynamically generated IDs.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment