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.
5 #include "base/i18n/icu_util.h"
13 #include "base/debug/alias.h"
14 #include "base/files/file_path.h"
15 #include "base/files/memory_mapped_file.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "build/build_config.h"
21 #include "third_party/icu/source/common/unicode/putil.h"
22 #include "third_party/icu/source/common/unicode/udata.h"
23 #if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_ANDROID)
24 #include "third_party/icu/source/i18n/unicode/timezone.h"
27 #if defined(OS_ANDROID)
28 #include "base/android/apk_assets.h"
29 #include "base/android/timezone_utils.h"
33 #include "base/ios/ios_util.h"
36 #if defined(OS_MACOSX)
37 #include "base/mac/foundation_util.h"
40 #if defined(OS_FUCHSIA)
41 #include "base/base_paths_fuchsia.h"
47 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
48 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
50 #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
57 // Assert that we are not called more than once. Even though calling this
58 // function isn't harmful (ICU can handle it), being called twice probably
59 // indicates a programming error.
60 bool g_check_called_once = true;
61 bool g_called_once = false;
62 #endif // DCHECK_IS_ON()
64 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
66 // Use an unversioned file name to simplify a icu version update down the road.
67 // No need to change the filename in multiple places (gyp files, windows
68 // build pkg configurations, etc). 'l' stands for Little Endian.
69 // This variable is exported through the header file.
70 const char kIcuDataFileName[] = "icudtl.dat";
71 #if defined(OS_ANDROID)
72 const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
75 // File handle intentionally never closed. Not using File here because its
76 // Windows implementation guards against two instances owning the same
77 // PlatformFile (which we allow since we know it is never freed).
78 PlatformFile g_icudtl_pf = kInvalidPlatformFile;
79 MemoryMappedFile* g_icudtl_mapped_file = nullptr;
80 MemoryMappedFile::Region g_icudtl_region;
82 void LazyInitIcuDataFile() {
83 if (g_icudtl_pf != kInvalidPlatformFile) {
86 #if defined(OS_ANDROID)
87 int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName,
93 // For unit tests, data file is located on disk, so try there as a fallback.
94 #endif // defined(OS_ANDROID)
95 #if !defined(OS_MACOSX)
97 if (!PathService::Get(DIR_ASSETS, &data_path)) {
98 LOG(ERROR) << "Can't find " << kIcuDataFileName;
101 data_path = data_path.AppendASCII(kIcuDataFileName);
103 // Assume it is in the framework bundle's Resources directory.
104 ScopedCFTypeRef<CFStringRef> data_file_name(
105 SysUTF8ToCFStringRef(kIcuDataFileName));
106 FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
108 FilePath override_data_path = base::ios::FilePathOfEmbeddedICU();
109 if (!override_data_path.empty()) {
110 data_path = override_data_path;
112 #endif // !defined(OS_IOS)
113 if (data_path.empty()) {
114 LOG(ERROR) << kIcuDataFileName << " not found in bundle";
117 #endif // !defined(OS_MACOSX)
118 File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
119 if (file.IsValid()) {
120 g_icudtl_pf = file.TakePlatformFile();
121 g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
125 bool InitializeICUWithFileDescriptorInternal(
126 PlatformFile data_fd,
127 const MemoryMappedFile::Region& data_region) {
128 // This can be called multiple times in tests.
129 if (g_icudtl_mapped_file) {
132 if (data_fd == kInvalidPlatformFile) {
133 LOG(ERROR) << "Invalid file descriptor to ICU data received.";
137 std::unique_ptr<MemoryMappedFile> icudtl_mapped_file(new MemoryMappedFile());
138 if (!icudtl_mapped_file->Initialize(File(data_fd), data_region)) {
139 LOG(ERROR) << "Couldn't mmap icu data file";
142 g_icudtl_mapped_file = icudtl_mapped_file.release();
144 UErrorCode err = U_ZERO_ERROR;
145 udata_setCommonData(const_cast<uint8_t*>(g_icudtl_mapped_file->data()), &err);
146 #if defined(OS_ANDROID)
147 if (err == U_ZERO_ERROR) {
148 // On Android, we can't leave it up to ICU to set the default timezone
149 // because ICU's timezone detection does not work in many timezones (e.g.
150 // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
151 // timezone and set the ICU default timezone accordingly in advance of
152 // actual use. See crbug.com/722821 and
153 // https://ssl.icu-project.org/trac/ticket/13208 .
154 base::string16 timezone_id = base::android::GetDefaultTimeZoneId();
155 icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
156 icu::UnicodeString(FALSE, timezone_id.data(), timezone_id.length())));
159 // Never try to load ICU data from files.
160 udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
161 return err == U_ZERO_ERROR;
163 #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
164 #endif // !defined(OS_NACL)
168 #if !defined(OS_NACL)
169 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
170 #if defined(OS_ANDROID)
171 bool InitializeICUWithFileDescriptor(
172 PlatformFile data_fd,
173 const MemoryMappedFile::Region& data_region) {
175 DCHECK(!g_check_called_once || !g_called_once);
176 g_called_once = true;
178 return InitializeICUWithFileDescriptorInternal(data_fd, data_region);
181 PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
182 CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
183 *out_region = g_icudtl_region;
188 const uint8_t* GetRawIcuMemory() {
189 CHECK(g_icudtl_mapped_file);
190 return g_icudtl_mapped_file->data();
193 bool InitializeICUFromRawMemory(const uint8_t* raw_memory) {
194 #if !defined(COMPONENT_BUILD)
196 DCHECK(!g_check_called_once || !g_called_once);
197 g_called_once = true;
200 UErrorCode err = U_ZERO_ERROR;
201 udata_setCommonData(const_cast<uint8_t*>(raw_memory), &err);
202 // Never try to load ICU data from files.
203 udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
204 return err == U_ZERO_ERROR;
210 #endif // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
212 bool InitializeICU() {
214 DCHECK(!g_check_called_once || !g_called_once);
215 g_called_once = true;
219 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
221 PathService::Get(DIR_ASSETS, &data_path);
222 data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME);
224 HMODULE module = LoadLibrary(data_path.value().c_str());
226 LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
230 FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
232 LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
233 << ICU_UTIL_DATA_SHARED_MODULE_NAME;
237 UErrorCode err = U_ZERO_ERROR;
238 udata_setCommonData(reinterpret_cast<void*>(addr), &err);
239 // Never try to load ICU data from files.
240 udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
241 result = (err == U_ZERO_ERROR);
242 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
243 // The ICU data is statically linked.
245 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
246 // If the ICU data directory is set, ICU won't actually load the data until
247 // it is needed. This can fail if the process is sandboxed at that time.
248 // Instead, we map the file in and hand off the data so the sandbox won't
249 // cause any problems.
250 LazyInitIcuDataFile();
252 InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
255 // To respond to the timezone change properly, the default timezone
256 // cache in ICU has to be populated on starting up.
257 // TODO(jungshik): Some callers do not care about tz at all. If necessary,
258 // add a boolean argument to this function to init'd the default tz only
260 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
262 std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
266 #endif // !defined(OS_NACL)
268 void AllowMultipleInitializeCallsForTesting() {
269 #if DCHECK_IS_ON() && !defined(OS_NACL)
270 g_check_called_once = false;