1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors
2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file.
4*6777b538SAndroid Build Coastguard Worker
5*6777b538SAndroid Build Coastguard Worker #include "base/i18n/icu_util.h"
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Worker #include "build/build_config.h"
8*6777b538SAndroid Build Coastguard Worker
9*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
10*6777b538SAndroid Build Coastguard Worker #include <windows.h>
11*6777b538SAndroid Build Coastguard Worker #endif
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Worker #include <string.h>
14*6777b538SAndroid Build Coastguard Worker
15*6777b538SAndroid Build Coastguard Worker #include <memory>
16*6777b538SAndroid Build Coastguard Worker #include <string>
17*6777b538SAndroid Build Coastguard Worker
18*6777b538SAndroid Build Coastguard Worker #include "base/debug/alias.h"
19*6777b538SAndroid Build Coastguard Worker #include "base/environment.h"
20*6777b538SAndroid Build Coastguard Worker #include "base/files/file_path.h"
21*6777b538SAndroid Build Coastguard Worker #include "base/files/file_util.h"
22*6777b538SAndroid Build Coastguard Worker #include "base/files/memory_mapped_file.h"
23*6777b538SAndroid Build Coastguard Worker #include "base/logging.h"
24*6777b538SAndroid Build Coastguard Worker #include "base/metrics/histogram_functions.h"
25*6777b538SAndroid Build Coastguard Worker #include "base/metrics/metrics_hashes.h"
26*6777b538SAndroid Build Coastguard Worker #include "base/path_service.h"
27*6777b538SAndroid Build Coastguard Worker #include "base/strings/string_util.h"
28*6777b538SAndroid Build Coastguard Worker #include "build/chromecast_buildflags.h"
29*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/putil.h"
30*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/uclean.h"
31*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/udata.h"
32*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/utrace.h"
33*6777b538SAndroid Build Coastguard Worker
34*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
35*6777b538SAndroid Build Coastguard Worker #include "base/android/apk_assets.h"
36*6777b538SAndroid Build Coastguard Worker #include "base/android/timezone_utils.h"
37*6777b538SAndroid Build Coastguard Worker #endif
38*6777b538SAndroid Build Coastguard Worker
39*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_IOS)
40*6777b538SAndroid Build Coastguard Worker #include "base/ios/ios_util.h"
41*6777b538SAndroid Build Coastguard Worker #endif
42*6777b538SAndroid Build Coastguard Worker
43*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_APPLE)
44*6777b538SAndroid Build Coastguard Worker #include "base/apple/foundation_util.h"
45*6777b538SAndroid Build Coastguard Worker #endif
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
48*6777b538SAndroid Build Coastguard Worker #include "base/fuchsia/intl_profile_watcher.h"
49*6777b538SAndroid Build Coastguard Worker #endif
50*6777b538SAndroid Build Coastguard Worker
51*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA)
52*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/common/unicode/unistr.h"
53*6777b538SAndroid Build Coastguard Worker #endif
54*6777b538SAndroid Build Coastguard Worker
55*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) || \
56*6777b538SAndroid Build Coastguard Worker BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
57*6777b538SAndroid Build Coastguard Worker #include "third_party/icu/source/i18n/unicode/timezone.h"
58*6777b538SAndroid Build Coastguard Worker #endif
59*6777b538SAndroid Build Coastguard Worker
60*6777b538SAndroid Build Coastguard Worker namespace base::i18n {
61*6777b538SAndroid Build Coastguard Worker
62*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_NACL)
63*6777b538SAndroid Build Coastguard Worker namespace {
64*6777b538SAndroid Build Coastguard Worker
65*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
66*6777b538SAndroid Build Coastguard Worker // Assert that we are not called more than once. Even though calling this
67*6777b538SAndroid Build Coastguard Worker // function isn't harmful (ICU can handle it), being called twice probably
68*6777b538SAndroid Build Coastguard Worker // indicates a programming error.
69*6777b538SAndroid Build Coastguard Worker bool g_check_called_once = true;
70*6777b538SAndroid Build Coastguard Worker bool g_called_once = false;
71*6777b538SAndroid Build Coastguard Worker #endif // DCHECK_IS_ON()
72*6777b538SAndroid Build Coastguard Worker
73*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
74*6777b538SAndroid Build Coastguard Worker
75*6777b538SAndroid Build Coastguard Worker // To debug http://crbug.com/445616.
76*6777b538SAndroid Build Coastguard Worker int g_debug_icu_last_error;
77*6777b538SAndroid Build Coastguard Worker int g_debug_icu_load;
78*6777b538SAndroid Build Coastguard Worker int g_debug_icu_pf_error_details;
79*6777b538SAndroid Build Coastguard Worker int g_debug_icu_pf_last_error;
80*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
81*6777b538SAndroid Build Coastguard Worker wchar_t g_debug_icu_pf_filename[_MAX_PATH];
82*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_WIN)
83*6777b538SAndroid Build Coastguard Worker // Use an unversioned file name to simplify a icu version update down the road.
84*6777b538SAndroid Build Coastguard Worker // No need to change the filename in multiple places (gyp files, windows
85*6777b538SAndroid Build Coastguard Worker // build pkg configurations, etc). 'l' stands for Little Endian.
86*6777b538SAndroid Build Coastguard Worker // This variable is exported through the header file.
87*6777b538SAndroid Build Coastguard Worker const char kIcuDataFileName[] = "icudtl.dat";
88*6777b538SAndroid Build Coastguard Worker
89*6777b538SAndroid Build Coastguard Worker // Time zone data loading.
90*6777b538SAndroid Build Coastguard Worker // For now, only Fuchsia has a meaningful use case for this feature, so it is
91*6777b538SAndroid Build Coastguard Worker // only implemented for OS_FUCHSIA.
92*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
93*6777b538SAndroid Build Coastguard Worker // The environment variable used to point the ICU data loader to the directory
94*6777b538SAndroid Build Coastguard Worker // containing time zone data. This is available from ICU version 54. The env
95*6777b538SAndroid Build Coastguard Worker // variable approach is antiquated by today's standards (2019), but is the
96*6777b538SAndroid Build Coastguard Worker // recommended way to configure ICU.
97*6777b538SAndroid Build Coastguard Worker //
98*6777b538SAndroid Build Coastguard Worker // See for details: http://userguide.icu-project.org/datetime/timezone
99*6777b538SAndroid Build Coastguard Worker const char kIcuTimeZoneEnvVariable[] = "ICU_TIMEZONE_FILES_DIR";
100*6777b538SAndroid Build Coastguard Worker
101*6777b538SAndroid Build Coastguard Worker // Up-to-date time zone data MUST be provided by the system as a
102*6777b538SAndroid Build Coastguard Worker // directory offered to Chromium components at /config/tzdata. Chromium
103*6777b538SAndroid Build Coastguard Worker // components "use" the `tzdata` directory capability, specifying the
104*6777b538SAndroid Build Coastguard Worker // "/config/tzdata" path. Chromium components will crash if this capability
105*6777b538SAndroid Build Coastguard Worker // is not available.
106*6777b538SAndroid Build Coastguard Worker //
107*6777b538SAndroid Build Coastguard Worker // TimeZoneDataTest.* tests verify that external timezone data is correctly
108*6777b538SAndroid Build Coastguard Worker // loaded from the system, to alert developers if the platform and Chromium
109*6777b538SAndroid Build Coastguard Worker // versions are no longer compatible versions.
110*6777b538SAndroid Build Coastguard Worker // LINT.IfChange(icu_time_zone_data_path)
111*6777b538SAndroid Build Coastguard Worker const char kIcuTimeZoneDataDir[] = "/config/tzdata/icu/44/le";
112*6777b538SAndroid Build Coastguard Worker // LINT.ThenChange(//sandbox/policy.fuchsia/sandbox_policy_fuchsia.cc:icu_time_zone_data_path)
113*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_FUCHSIA)
114*6777b538SAndroid Build Coastguard Worker
115*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
116*6777b538SAndroid Build Coastguard Worker const char kAndroidAssetsIcuDataFileName[] = "assets/icudtl.dat";
117*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_ANDROID)
118*6777b538SAndroid Build Coastguard Worker
119*6777b538SAndroid Build Coastguard Worker // File handle intentionally never closed. Not using File here because its
120*6777b538SAndroid Build Coastguard Worker // Windows implementation guards against two instances owning the same
121*6777b538SAndroid Build Coastguard Worker // PlatformFile (which we allow since we know it is never freed).
122*6777b538SAndroid Build Coastguard Worker PlatformFile g_icudtl_pf = kInvalidPlatformFile;
123*6777b538SAndroid Build Coastguard Worker IcuDataFile* g_icudtl_mapped_file = nullptr;
124*6777b538SAndroid Build Coastguard Worker MemoryMappedFile::Region g_icudtl_region;
125*6777b538SAndroid Build Coastguard Worker
126*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
127*6777b538SAndroid Build Coastguard Worker // The directory from which the ICU data loader will be configured to load time
128*6777b538SAndroid Build Coastguard Worker // zone data. It is only changed by SetIcuTimeZoneDataDirForTesting().
129*6777b538SAndroid Build Coastguard Worker const char* g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
130*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_FUCHSIA)
131*6777b538SAndroid Build Coastguard Worker
LazyInitIcuDataFile()132*6777b538SAndroid Build Coastguard Worker void LazyInitIcuDataFile() {
133*6777b538SAndroid Build Coastguard Worker if (g_icudtl_pf != kInvalidPlatformFile) {
134*6777b538SAndroid Build Coastguard Worker return;
135*6777b538SAndroid Build Coastguard Worker }
136*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
137*6777b538SAndroid Build Coastguard Worker int fd =
138*6777b538SAndroid Build Coastguard Worker android::OpenApkAsset(kAndroidAssetsIcuDataFileName, &g_icudtl_region);
139*6777b538SAndroid Build Coastguard Worker g_icudtl_pf = fd;
140*6777b538SAndroid Build Coastguard Worker if (fd != -1) {
141*6777b538SAndroid Build Coastguard Worker return;
142*6777b538SAndroid Build Coastguard Worker }
143*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_ANDROID)
144*6777b538SAndroid Build Coastguard Worker // For unit tests, data file is located on disk, so try there as a fallback.
145*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_APPLE)
146*6777b538SAndroid Build Coastguard Worker FilePath data_path;
147*6777b538SAndroid Build Coastguard Worker if (!PathService::Get(DIR_ASSETS, &data_path)) {
148*6777b538SAndroid Build Coastguard Worker LOG(ERROR) << "Can't find " << kIcuDataFileName;
149*6777b538SAndroid Build Coastguard Worker return;
150*6777b538SAndroid Build Coastguard Worker }
151*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
152*6777b538SAndroid Build Coastguard Worker // TODO(brucedawson): http://crbug.com/445616
153*6777b538SAndroid Build Coastguard Worker wchar_t tmp_buffer[_MAX_PATH] = {0};
154*6777b538SAndroid Build Coastguard Worker wcscpy_s(tmp_buffer, data_path.value().c_str());
155*6777b538SAndroid Build Coastguard Worker debug::Alias(tmp_buffer);
156*6777b538SAndroid Build Coastguard Worker #endif
157*6777b538SAndroid Build Coastguard Worker data_path = data_path.AppendASCII(kIcuDataFileName);
158*6777b538SAndroid Build Coastguard Worker
159*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
160*6777b538SAndroid Build Coastguard Worker // TODO(brucedawson): http://crbug.com/445616
161*6777b538SAndroid Build Coastguard Worker wchar_t tmp_buffer2[_MAX_PATH] = {0};
162*6777b538SAndroid Build Coastguard Worker wcscpy_s(tmp_buffer2, data_path.value().c_str());
163*6777b538SAndroid Build Coastguard Worker debug::Alias(tmp_buffer2);
164*6777b538SAndroid Build Coastguard Worker #endif
165*6777b538SAndroid Build Coastguard Worker
166*6777b538SAndroid Build Coastguard Worker #else // !BUILDFLAG(IS_APPLE)
167*6777b538SAndroid Build Coastguard Worker // Assume it is in the framework bundle's Resources directory.
168*6777b538SAndroid Build Coastguard Worker FilePath data_path = apple::PathForFrameworkBundleResource(kIcuDataFileName);
169*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_IOS)
170*6777b538SAndroid Build Coastguard Worker FilePath override_data_path = ios::FilePathOfEmbeddedICU();
171*6777b538SAndroid Build Coastguard Worker if (!override_data_path.empty()) {
172*6777b538SAndroid Build Coastguard Worker data_path = override_data_path;
173*6777b538SAndroid Build Coastguard Worker }
174*6777b538SAndroid Build Coastguard Worker #endif // !BUILDFLAG(IS_IOS)
175*6777b538SAndroid Build Coastguard Worker if (data_path.empty()) {
176*6777b538SAndroid Build Coastguard Worker LOG(ERROR) << kIcuDataFileName << " not found in bundle";
177*6777b538SAndroid Build Coastguard Worker return;
178*6777b538SAndroid Build Coastguard Worker }
179*6777b538SAndroid Build Coastguard Worker #endif // !BUILDFLAG(IS_APPLE)
180*6777b538SAndroid Build Coastguard Worker File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
181*6777b538SAndroid Build Coastguard Worker if (file.IsValid()) {
182*6777b538SAndroid Build Coastguard Worker // TODO(brucedawson): http://crbug.com/445616.
183*6777b538SAndroid Build Coastguard Worker g_debug_icu_pf_last_error = 0;
184*6777b538SAndroid Build Coastguard Worker g_debug_icu_pf_error_details = 0;
185*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
186*6777b538SAndroid Build Coastguard Worker g_debug_icu_pf_filename[0] = 0;
187*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_WIN)
188*6777b538SAndroid Build Coastguard Worker
189*6777b538SAndroid Build Coastguard Worker g_icudtl_pf = file.TakePlatformFile();
190*6777b538SAndroid Build Coastguard Worker g_icudtl_region = MemoryMappedFile::Region::kWholeFile;
191*6777b538SAndroid Build Coastguard Worker }
192*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
193*6777b538SAndroid Build Coastguard Worker else {
194*6777b538SAndroid Build Coastguard Worker // TODO(brucedawson): http://crbug.com/445616.
195*6777b538SAndroid Build Coastguard Worker g_debug_icu_pf_last_error = ::GetLastError();
196*6777b538SAndroid Build Coastguard Worker g_debug_icu_pf_error_details = file.error_details();
197*6777b538SAndroid Build Coastguard Worker wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str());
198*6777b538SAndroid Build Coastguard Worker }
199*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_WIN)
200*6777b538SAndroid Build Coastguard Worker }
201*6777b538SAndroid Build Coastguard Worker
202*6777b538SAndroid Build Coastguard Worker // Configures ICU to load external time zone data, if appropriate.
InitializeExternalTimeZoneData()203*6777b538SAndroid Build Coastguard Worker void InitializeExternalTimeZoneData() {
204*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
205*6777b538SAndroid Build Coastguard Worker // Set the environment variable to override the location used by ICU.
206*6777b538SAndroid Build Coastguard Worker // Loading can still fail if the directory is empty or its data is invalid.
207*6777b538SAndroid Build Coastguard Worker std::unique_ptr<base::Environment> env = base::Environment::Create();
208*6777b538SAndroid Build Coastguard Worker if (!base::DirectoryExists(base::FilePath(g_icu_time_zone_data_dir))) {
209*6777b538SAndroid Build Coastguard Worker PLOG(FATAL) << "Could not open directory: '" << g_icu_time_zone_data_dir
210*6777b538SAndroid Build Coastguard Worker << "'";
211*6777b538SAndroid Build Coastguard Worker }
212*6777b538SAndroid Build Coastguard Worker env->SetVar(kIcuTimeZoneEnvVariable, g_icu_time_zone_data_dir);
213*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_FUCHSIA)
214*6777b538SAndroid Build Coastguard Worker }
215*6777b538SAndroid Build Coastguard Worker
LoadIcuData(PlatformFile data_fd,const MemoryMappedFile::Region & data_region,std::unique_ptr<IcuDataFile> * out_mapped_data_file,UErrorCode * out_error_code)216*6777b538SAndroid Build Coastguard Worker int LoadIcuData(PlatformFile data_fd,
217*6777b538SAndroid Build Coastguard Worker const MemoryMappedFile::Region& data_region,
218*6777b538SAndroid Build Coastguard Worker std::unique_ptr<IcuDataFile>* out_mapped_data_file,
219*6777b538SAndroid Build Coastguard Worker UErrorCode* out_error_code) {
220*6777b538SAndroid Build Coastguard Worker InitializeExternalTimeZoneData();
221*6777b538SAndroid Build Coastguard Worker
222*6777b538SAndroid Build Coastguard Worker if (data_fd == kInvalidPlatformFile) {
223*6777b538SAndroid Build Coastguard Worker LOG(ERROR) << "Invalid file descriptor to ICU data received.";
224*6777b538SAndroid Build Coastguard Worker return 1; // To debug http://crbug.com/445616.
225*6777b538SAndroid Build Coastguard Worker }
226*6777b538SAndroid Build Coastguard Worker
227*6777b538SAndroid Build Coastguard Worker *out_mapped_data_file = std::make_unique<IcuDataFile>();
228*6777b538SAndroid Build Coastguard Worker if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) {
229*6777b538SAndroid Build Coastguard Worker LOG(ERROR) << "Couldn't mmap icu data file";
230*6777b538SAndroid Build Coastguard Worker return 2; // To debug http://crbug.com/445616.
231*6777b538SAndroid Build Coastguard Worker }
232*6777b538SAndroid Build Coastguard Worker
233*6777b538SAndroid Build Coastguard Worker (*out_error_code) = U_ZERO_ERROR;
234*6777b538SAndroid Build Coastguard Worker udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()),
235*6777b538SAndroid Build Coastguard Worker out_error_code);
236*6777b538SAndroid Build Coastguard Worker if (U_FAILURE(*out_error_code)) {
237*6777b538SAndroid Build Coastguard Worker LOG(ERROR) << "Failed to initialize ICU with data file: "
238*6777b538SAndroid Build Coastguard Worker << u_errorName(*out_error_code);
239*6777b538SAndroid Build Coastguard Worker return 3; // To debug http://crbug.com/445616.
240*6777b538SAndroid Build Coastguard Worker }
241*6777b538SAndroid Build Coastguard Worker
242*6777b538SAndroid Build Coastguard Worker return 0;
243*6777b538SAndroid Build Coastguard Worker }
244*6777b538SAndroid Build Coastguard Worker
InitializeICUWithFileDescriptorInternal(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)245*6777b538SAndroid Build Coastguard Worker bool InitializeICUWithFileDescriptorInternal(
246*6777b538SAndroid Build Coastguard Worker PlatformFile data_fd,
247*6777b538SAndroid Build Coastguard Worker const MemoryMappedFile::Region& data_region) {
248*6777b538SAndroid Build Coastguard Worker // This can be called multiple times in tests.
249*6777b538SAndroid Build Coastguard Worker if (g_icudtl_mapped_file) {
250*6777b538SAndroid Build Coastguard Worker g_debug_icu_load = 0; // To debug http://crbug.com/445616.
251*6777b538SAndroid Build Coastguard Worker return true;
252*6777b538SAndroid Build Coastguard Worker }
253*6777b538SAndroid Build Coastguard Worker
254*6777b538SAndroid Build Coastguard Worker std::unique_ptr<IcuDataFile> mapped_file;
255*6777b538SAndroid Build Coastguard Worker UErrorCode err;
256*6777b538SAndroid Build Coastguard Worker g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err);
257*6777b538SAndroid Build Coastguard Worker if (g_debug_icu_load == 1 || g_debug_icu_load == 2) {
258*6777b538SAndroid Build Coastguard Worker return false;
259*6777b538SAndroid Build Coastguard Worker }
260*6777b538SAndroid Build Coastguard Worker g_icudtl_mapped_file = mapped_file.release();
261*6777b538SAndroid Build Coastguard Worker
262*6777b538SAndroid Build Coastguard Worker if (g_debug_icu_load == 3) {
263*6777b538SAndroid Build Coastguard Worker g_debug_icu_last_error = err;
264*6777b538SAndroid Build Coastguard Worker }
265*6777b538SAndroid Build Coastguard Worker
266*6777b538SAndroid Build Coastguard Worker // Never try to load ICU data from files.
267*6777b538SAndroid Build Coastguard Worker udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
268*6777b538SAndroid Build Coastguard Worker return U_SUCCESS(err);
269*6777b538SAndroid Build Coastguard Worker }
270*6777b538SAndroid Build Coastguard Worker
InitializeICUFromDataFile()271*6777b538SAndroid Build Coastguard Worker bool InitializeICUFromDataFile() {
272*6777b538SAndroid Build Coastguard Worker // If the ICU data directory is set, ICU won't actually load the data until
273*6777b538SAndroid Build Coastguard Worker // it is needed. This can fail if the process is sandboxed at that time.
274*6777b538SAndroid Build Coastguard Worker // Instead, we map the file in and hand off the data so the sandbox won't
275*6777b538SAndroid Build Coastguard Worker // cause any problems.
276*6777b538SAndroid Build Coastguard Worker LazyInitIcuDataFile();
277*6777b538SAndroid Build Coastguard Worker bool result =
278*6777b538SAndroid Build Coastguard Worker InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
279*6777b538SAndroid Build Coastguard Worker
280*6777b538SAndroid Build Coastguard Worker int debug_icu_load = g_debug_icu_load;
281*6777b538SAndroid Build Coastguard Worker debug::Alias(&debug_icu_load);
282*6777b538SAndroid Build Coastguard Worker int debug_icu_last_error = g_debug_icu_last_error;
283*6777b538SAndroid Build Coastguard Worker debug::Alias(&debug_icu_last_error);
284*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_WIN)
285*6777b538SAndroid Build Coastguard Worker int debug_icu_pf_last_error = g_debug_icu_pf_last_error;
286*6777b538SAndroid Build Coastguard Worker debug::Alias(&debug_icu_pf_last_error);
287*6777b538SAndroid Build Coastguard Worker int debug_icu_pf_error_details = g_debug_icu_pf_error_details;
288*6777b538SAndroid Build Coastguard Worker debug::Alias(&debug_icu_pf_error_details);
289*6777b538SAndroid Build Coastguard Worker wchar_t debug_icu_pf_filename[_MAX_PATH] = {0};
290*6777b538SAndroid Build Coastguard Worker wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename);
291*6777b538SAndroid Build Coastguard Worker debug::Alias(&debug_icu_pf_filename);
292*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_WIN)
293*6777b538SAndroid Build Coastguard Worker // Excluding Chrome OS from this CHECK due to b/289684640.
294*6777b538SAndroid Build Coastguard Worker #if !BUILDFLAG(IS_CHROMEOS)
295*6777b538SAndroid Build Coastguard Worker // https://crbug.com/445616
296*6777b538SAndroid Build Coastguard Worker // https://crbug.com/1449816
297*6777b538SAndroid Build Coastguard Worker CHECK(result);
298*6777b538SAndroid Build Coastguard Worker #endif
299*6777b538SAndroid Build Coastguard Worker
300*6777b538SAndroid Build Coastguard Worker return result;
301*6777b538SAndroid Build Coastguard Worker }
302*6777b538SAndroid Build Coastguard Worker #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
303*6777b538SAndroid Build Coastguard Worker
304*6777b538SAndroid Build Coastguard Worker // Explicitly initialize ICU's time zone if necessary.
305*6777b538SAndroid Build Coastguard Worker // On some platforms, the time zone must be explicitly initialized zone rather
306*6777b538SAndroid Build Coastguard Worker // than relying on ICU's internal initialization.
InitializeIcuTimeZone()307*6777b538SAndroid Build Coastguard Worker void InitializeIcuTimeZone() {
308*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_ANDROID)
309*6777b538SAndroid Build Coastguard Worker // On Android, we can't leave it up to ICU to set the default time zone
310*6777b538SAndroid Build Coastguard Worker // because ICU's time zone detection does not work in many time zones (e.g.
311*6777b538SAndroid Build Coastguard Worker // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
312*6777b538SAndroid Build Coastguard Worker // time zone and set the ICU default time zone accordingly in advance of
313*6777b538SAndroid Build Coastguard Worker // actual use. See crbug.com/722821 and
314*6777b538SAndroid Build Coastguard Worker // https://ssl.icu-project.org/trac/ticket/13208 .
315*6777b538SAndroid Build Coastguard Worker std::u16string zone_id = android::GetDefaultTimeZoneId();
316*6777b538SAndroid Build Coastguard Worker icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
317*6777b538SAndroid Build Coastguard Worker icu::UnicodeString(false, zone_id.data(), zone_id.length())));
318*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_FUCHSIA)
319*6777b538SAndroid Build Coastguard Worker // The platform-specific mechanisms used by ICU's detectHostTimeZone() to
320*6777b538SAndroid Build Coastguard Worker // determine the default time zone will not work on Fuchsia. Therefore,
321*6777b538SAndroid Build Coastguard Worker // proactively set the default system.
322*6777b538SAndroid Build Coastguard Worker // This is also required by TimeZoneMonitorFuchsia::ProfileMayHaveChanged(),
323*6777b538SAndroid Build Coastguard Worker // which uses the current default to detect whether the time zone changed in
324*6777b538SAndroid Build Coastguard Worker // the new profile.
325*6777b538SAndroid Build Coastguard Worker // If the system time zone cannot be obtained or is not understood by ICU,
326*6777b538SAndroid Build Coastguard Worker // the "unknown" time zone will be returned by createTimeZone() and used.
327*6777b538SAndroid Build Coastguard Worker std::string zone_id =
328*6777b538SAndroid Build Coastguard Worker FuchsiaIntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization();
329*6777b538SAndroid Build Coastguard Worker icu::TimeZone::adoptDefault(
330*6777b538SAndroid Build Coastguard Worker icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(zone_id)));
331*6777b538SAndroid Build Coastguard Worker #elif BUILDFLAG(IS_CHROMEOS) || (BUILDFLAG(IS_LINUX) && !BUILDFLAG(IS_CASTOS))
332*6777b538SAndroid Build Coastguard Worker // To respond to the time zone change properly, the default time zone
333*6777b538SAndroid Build Coastguard Worker // cache in ICU has to be populated on starting up.
334*6777b538SAndroid Build Coastguard Worker // See TimeZoneMonitorLinux::NotifyClientsFromImpl().
335*6777b538SAndroid Build Coastguard Worker std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
336*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_ANDROID)
337*6777b538SAndroid Build Coastguard Worker }
338*6777b538SAndroid Build Coastguard Worker
339*6777b538SAndroid Build Coastguard Worker enum class ICUCreateInstance {
340*6777b538SAndroid Build Coastguard Worker kCharacterBreakIterator = 0,
341*6777b538SAndroid Build Coastguard Worker kWordBreakIterator = 1,
342*6777b538SAndroid Build Coastguard Worker kLineBreakIterator = 2,
343*6777b538SAndroid Build Coastguard Worker kLineBreakIteratorTypeLoose = 3,
344*6777b538SAndroid Build Coastguard Worker kLineBreakIteratorTypeNormal = 4,
345*6777b538SAndroid Build Coastguard Worker kLineBreakIteratorTypeStrict = 5,
346*6777b538SAndroid Build Coastguard Worker kSentenceBreakIterator = 6,
347*6777b538SAndroid Build Coastguard Worker kTitleBreakIterator = 7,
348*6777b538SAndroid Build Coastguard Worker kThaiBreakEngine = 8,
349*6777b538SAndroid Build Coastguard Worker kLaoBreakEngine = 9,
350*6777b538SAndroid Build Coastguard Worker kBurmeseBreakEngine = 10,
351*6777b538SAndroid Build Coastguard Worker kKhmerBreakEngine = 11,
352*6777b538SAndroid Build Coastguard Worker kChineseJapaneseBreakEngine = 12,
353*6777b538SAndroid Build Coastguard Worker
354*6777b538SAndroid Build Coastguard Worker kMaxValue = kChineseJapaneseBreakEngine
355*6777b538SAndroid Build Coastguard Worker };
356*6777b538SAndroid Build Coastguard Worker
357*6777b538SAndroid Build Coastguard Worker // Common initialization to run regardless of how ICU is initialized.
358*6777b538SAndroid Build Coastguard Worker // There are multiple exposed InitializeIcu* functions. This should be called
359*6777b538SAndroid Build Coastguard Worker // as at the end of (the last functions in the sequence of) these functions.
DoCommonInitialization()360*6777b538SAndroid Build Coastguard Worker bool DoCommonInitialization() {
361*6777b538SAndroid Build Coastguard Worker // TODO(jungshik): Some callers do not care about tz at all. If necessary,
362*6777b538SAndroid Build Coastguard Worker // add a boolean argument to this function to init the default tz only
363*6777b538SAndroid Build Coastguard Worker // when requested.
364*6777b538SAndroid Build Coastguard Worker InitializeIcuTimeZone();
365*6777b538SAndroid Build Coastguard Worker
366*6777b538SAndroid Build Coastguard Worker utrace_setLevel(UTRACE_VERBOSE);
367*6777b538SAndroid Build Coastguard Worker return true;
368*6777b538SAndroid Build Coastguard Worker }
369*6777b538SAndroid Build Coastguard Worker
370*6777b538SAndroid Build Coastguard Worker } // namespace
371*6777b538SAndroid Build Coastguard Worker
372*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
InitializeICUWithFileDescriptor(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)373*6777b538SAndroid Build Coastguard Worker bool InitializeICUWithFileDescriptor(
374*6777b538SAndroid Build Coastguard Worker PlatformFile data_fd,
375*6777b538SAndroid Build Coastguard Worker const MemoryMappedFile::Region& data_region) {
376*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
377*6777b538SAndroid Build Coastguard Worker DCHECK(!g_check_called_once || !g_called_once);
378*6777b538SAndroid Build Coastguard Worker g_called_once = true;
379*6777b538SAndroid Build Coastguard Worker #endif
380*6777b538SAndroid Build Coastguard Worker if (!InitializeICUWithFileDescriptorInternal(data_fd, data_region))
381*6777b538SAndroid Build Coastguard Worker return false;
382*6777b538SAndroid Build Coastguard Worker
383*6777b538SAndroid Build Coastguard Worker return DoCommonInitialization();
384*6777b538SAndroid Build Coastguard Worker }
385*6777b538SAndroid Build Coastguard Worker
GetIcuDataFileHandle(MemoryMappedFile::Region * out_region)386*6777b538SAndroid Build Coastguard Worker PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
387*6777b538SAndroid Build Coastguard Worker CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
388*6777b538SAndroid Build Coastguard Worker *out_region = g_icudtl_region;
389*6777b538SAndroid Build Coastguard Worker return g_icudtl_pf;
390*6777b538SAndroid Build Coastguard Worker }
391*6777b538SAndroid Build Coastguard Worker
ResetGlobalsForTesting()392*6777b538SAndroid Build Coastguard Worker void ResetGlobalsForTesting() {
393*6777b538SAndroid Build Coastguard Worker // Reset ICU library internal state before tearing-down the mapped data
394*6777b538SAndroid Build Coastguard Worker // file, or handle.
395*6777b538SAndroid Build Coastguard Worker u_cleanup();
396*6777b538SAndroid Build Coastguard Worker
397*6777b538SAndroid Build Coastguard Worker // `g_icudtl_pf` does not actually own the FD once ICU is initialized, so
398*6777b538SAndroid Build Coastguard Worker // don't try to close it here.
399*6777b538SAndroid Build Coastguard Worker g_icudtl_pf = kInvalidPlatformFile;
400*6777b538SAndroid Build Coastguard Worker delete std::exchange(g_icudtl_mapped_file, nullptr);
401*6777b538SAndroid Build Coastguard Worker
402*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
403*6777b538SAndroid Build Coastguard Worker g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
404*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_FUCHSIA)
405*6777b538SAndroid Build Coastguard Worker }
406*6777b538SAndroid Build Coastguard Worker
407*6777b538SAndroid Build Coastguard Worker #if BUILDFLAG(IS_FUCHSIA)
408*6777b538SAndroid Build Coastguard Worker // |dir| must remain valid until ResetGlobalsForTesting() is called.
SetIcuTimeZoneDataDirForTesting(const char * dir)409*6777b538SAndroid Build Coastguard Worker void SetIcuTimeZoneDataDirForTesting(const char* dir) {
410*6777b538SAndroid Build Coastguard Worker g_icu_time_zone_data_dir = dir;
411*6777b538SAndroid Build Coastguard Worker }
412*6777b538SAndroid Build Coastguard Worker #endif // BUILDFLAG(IS_FUCHSIA)
413*6777b538SAndroid Build Coastguard Worker #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
414*6777b538SAndroid Build Coastguard Worker
InitializeICU()415*6777b538SAndroid Build Coastguard Worker bool InitializeICU() {
416*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
417*6777b538SAndroid Build Coastguard Worker DCHECK(!g_check_called_once || !g_called_once);
418*6777b538SAndroid Build Coastguard Worker g_called_once = true;
419*6777b538SAndroid Build Coastguard Worker #endif
420*6777b538SAndroid Build Coastguard Worker
421*6777b538SAndroid Build Coastguard Worker #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
422*6777b538SAndroid Build Coastguard Worker // The ICU data is statically linked.
423*6777b538SAndroid Build Coastguard Worker #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
424*6777b538SAndroid Build Coastguard Worker if (!InitializeICUFromDataFile())
425*6777b538SAndroid Build Coastguard Worker return false;
426*6777b538SAndroid Build Coastguard Worker #else
427*6777b538SAndroid Build Coastguard Worker #error Unsupported ICU_UTIL_DATA_IMPL value
428*6777b538SAndroid Build Coastguard Worker #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
429*6777b538SAndroid Build Coastguard Worker
430*6777b538SAndroid Build Coastguard Worker return DoCommonInitialization();
431*6777b538SAndroid Build Coastguard Worker }
432*6777b538SAndroid Build Coastguard Worker
AllowMultipleInitializeCallsForTesting()433*6777b538SAndroid Build Coastguard Worker void AllowMultipleInitializeCallsForTesting() {
434*6777b538SAndroid Build Coastguard Worker #if DCHECK_IS_ON()
435*6777b538SAndroid Build Coastguard Worker g_check_called_once = false;
436*6777b538SAndroid Build Coastguard Worker #endif
437*6777b538SAndroid Build Coastguard Worker }
438*6777b538SAndroid Build Coastguard Worker
439*6777b538SAndroid Build Coastguard Worker #endif // !BUILDFLAG(IS_NACL)
440*6777b538SAndroid Build Coastguard Worker
441*6777b538SAndroid Build Coastguard Worker } // namespace base::i18n
442