Upload upstream chromium 67.0.3396
[platform/framework/web/chromium-efl.git] / base / i18n / icu_util.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/i18n/icu_util.h"
6
7 #if defined(OS_WIN)
8 #include <windows.h>
9 #endif
10
11 #include <string>
12
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"
25 #endif
26
27 #if defined(OS_ANDROID)
28 #include "base/android/apk_assets.h"
29 #include "base/android/timezone_utils.h"
30 #endif
31
32 #if defined(OS_IOS)
33 #include "base/ios/ios_util.h"
34 #endif
35
36 #if defined(OS_MACOSX)
37 #include "base/mac/foundation_util.h"
38 #endif
39
40 #if defined(OS_FUCHSIA)
41 #include "base/base_paths_fuchsia.h"
42 #endif
43
44 namespace base {
45 namespace i18n {
46
47 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
48 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
49 #if defined(OS_WIN)
50 #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
51 #endif
52 #endif
53
54 namespace {
55 #if !defined(OS_NACL)
56 #if DCHECK_IS_ON()
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()
63
64 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
65
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";
73 #endif
74
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;
81
82 void LazyInitIcuDataFile() {
83   if (g_icudtl_pf != kInvalidPlatformFile) {
84     return;
85   }
86 #if defined(OS_ANDROID)
87   int fd = base::android::OpenApkAsset(kAndroidAssetsIcuDataFileName,
88                                        &g_icudtl_region);
89   g_icudtl_pf = fd;
90   if (fd != -1) {
91     return;
92   }
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)
96   FilePath data_path;
97   if (!PathService::Get(DIR_ASSETS, &data_path)) {
98     LOG(ERROR) << "Can't find " << kIcuDataFileName;
99     return;
100   }
101   data_path = data_path.AppendASCII(kIcuDataFileName);
102 #else
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);
107 #if defined(OS_IOS)
108   FilePath override_data_path = base::ios::FilePathOfEmbeddedICU();
109   if (!override_data_path.empty()) {
110     data_path = override_data_path;
111   }
112 #endif  // !defined(OS_IOS)
113   if (data_path.empty()) {
114     LOG(ERROR) << kIcuDataFileName << " not found in bundle";
115     return;
116   }
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;
122   }
123 }
124
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) {
130     return true;
131   }
132   if (data_fd == kInvalidPlatformFile) {
133     LOG(ERROR) << "Invalid file descriptor to ICU data received.";
134     return false;
135   }
136
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";
140     return false;
141   }
142   g_icudtl_mapped_file = icudtl_mapped_file.release();
143
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())));
157   }
158 #endif
159   // Never try to load ICU data from files.
160   udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
161   return err == U_ZERO_ERROR;
162 }
163 #endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
164 #endif  // !defined(OS_NACL)
165
166 }  // namespace
167
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) {
174 #if DCHECK_IS_ON()
175   DCHECK(!g_check_called_once || !g_called_once);
176   g_called_once = true;
177 #endif
178   return InitializeICUWithFileDescriptorInternal(data_fd, data_region);
179 }
180
181 PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
182   CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
183   *out_region = g_icudtl_region;
184   return g_icudtl_pf;
185 }
186 #endif
187
188 const uint8_t* GetRawIcuMemory() {
189   CHECK(g_icudtl_mapped_file);
190   return g_icudtl_mapped_file->data();
191 }
192
193 bool InitializeICUFromRawMemory(const uint8_t* raw_memory) {
194 #if !defined(COMPONENT_BUILD)
195 #if DCHECK_IS_ON()
196   DCHECK(!g_check_called_once || !g_called_once);
197   g_called_once = true;
198 #endif
199
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;
205 #else
206   return true;
207 #endif
208 }
209
210 #endif  // ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE
211
212 bool InitializeICU() {
213 #if DCHECK_IS_ON()
214   DCHECK(!g_check_called_once || !g_called_once);
215   g_called_once = true;
216 #endif
217
218   bool result;
219 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
220   FilePath data_path;
221   PathService::Get(DIR_ASSETS, &data_path);
222   data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME);
223
224   HMODULE module = LoadLibrary(data_path.value().c_str());
225   if (!module) {
226     LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME;
227     return false;
228   }
229
230   FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL);
231   if (!addr) {
232     LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in "
233                << ICU_UTIL_DATA_SHARED_MODULE_NAME;
234     return false;
235   }
236
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.
244   result = true;
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();
251   result =
252       InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
253 #endif
254
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
259 // when requested.
260 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
261   if (result)
262     std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
263 #endif
264   return result;
265 }
266 #endif  // !defined(OS_NACL)
267
268 void AllowMultipleInitializeCallsForTesting() {
269 #if DCHECK_IS_ON() && !defined(OS_NACL)
270   g_check_called_once = false;
271 #endif
272 }
273
274 }  // namespace i18n
275 }  // namespace base