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/environment.h"
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/files/memory_mapped_file.h"
18 #include "base/logging.h"
19 #include "base/metrics/histogram_functions.h"
20 #include "base/metrics/metrics_hashes.h"
21 #include "base/path_service.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/sys_string_conversions.h"
24 #include "build/build_config.h"
25 #include "build/chromecast_buildflags.h"
26 #include "third_party/icu/source/common/unicode/putil.h"
27 #include "third_party/icu/source/common/unicode/udata.h"
28 #include "third_party/icu/source/common/unicode/utrace.h"
29
30 #if defined(OS_ANDROID)
31 #include "base/android/apk_assets.h"
32 #include "base/android/timezone_utils.h"
33 #endif
34
35 #if defined(OS_IOS)
36 #include "base/ios/ios_util.h"
37 #endif
38
39 #if defined(OS_MACOSX)
40 #include "base/mac/foundation_util.h"
41 #endif
42
43 #if defined(OS_FUCHSIA)
44 #include "base/fuchsia/intl_profile_watcher.h"
45 #endif
46
47 #if defined(OS_ANDROID) || defined(OS_FUCHSIA)
48 #include "third_party/icu/source/common/unicode/unistr.h"
49 #endif
50
51 #if defined(OS_ANDROID) || defined(OS_FUCHSIA) || \
52 (defined(OS_LINUX) && !BUILDFLAG(IS_CHROMECAST)) || defined(OS_BSD)
53 #include "third_party/icu/source/i18n/unicode/timezone.h"
54 #endif
55
56 namespace base {
57 namespace i18n {
58
59 #if !defined(OS_NACL)
60 namespace {
61
62 #if DCHECK_IS_ON()
63 // Assert that we are not called more than once. Even though calling this
64 // function isn't harmful (ICU can handle it), being called twice probably
65 // indicates a programming error.
66 bool g_check_called_once = true;
67 bool g_called_once = false;
68 #endif // DCHECK_IS_ON()
69
70 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
71
72 // To debug http://crbug.com/445616.
73 int g_debug_icu_last_error;
74 int g_debug_icu_load;
75 int g_debug_icu_pf_error_details;
76 int g_debug_icu_pf_last_error;
77 #if defined(OS_WIN)
78 wchar_t g_debug_icu_pf_filename[_MAX_PATH];
79 #endif // OS_WIN
80 // Use an unversioned file name to simplify a icu version update down the road.
81 // No need to change the filename in multiple places (gyp files, windows
82 // build pkg configurations, etc). 'l' stands for Little Endian.
83 // This variable is exported through the header file.
84 const char kIcuDataFileName[] = "icudtl.dat";
85 const char kIcuExtraDataFileName[] = "icudtl_extra.dat";
86
87 // Time zone data loading.
88 // For now, only Fuchsia has a meaningful use case for this feature, so it is
89 // only implemented for OS_FUCHSIA.
90 #if defined(OS_FUCHSIA)
91 // The environment variable used to point the ICU data loader to the directory
92 // containing time zone data. This is available from ICU version 54. The env
93 // variable approach is antiquated by today's standards (2019), but is the
94 // recommended way to configure ICU.
95 //
96 // See for details: http://userguide.icu-project.org/datetime/timezone
97 const char kIcuTimeZoneEnvVariable[] = "ICU_TIMEZONE_FILES_DIR";
98
99 // We assume that Fuchsia will provide time zone data at this path for Chromium
100 // to load, and that the path will be timely updated when Fuchsia needs to
101 // uprev the ICU version it is using. There are unit tests that will fail at
102 // Fuchsia roll time in case either Chromium or Fuchsia get upgraded to
103 // mutually incompatible ICU versions. That should be enough to alert the
104 // developers of the need to keep ICU library versions in ICU and Fuchsia in
105 // reasonable sync.
106 const char kIcuTimeZoneDataDir[] = "/config/data/tzdata/icu/44/le";
107 #endif // defined(OS_FUCHSIA)
108
109 #if defined(OS_ANDROID)
110 const char kAssetsPathPrefix[] = "assets/";
111 #endif // defined(OS_ANDROID)
112
113 // File handle intentionally never closed. Not using File here because its
114 // Windows implementation guards against two instances owning the same
115 // PlatformFile (which we allow since we know it is never freed).
116 PlatformFile g_icudtl_pf = kInvalidPlatformFile;
117 MemoryMappedFile* g_icudtl_mapped_file = nullptr;
118 MemoryMappedFile::Region g_icudtl_region;
119 PlatformFile g_icudtl_extra_pf = kInvalidPlatformFile;
120 MemoryMappedFile* g_icudtl_extra_mapped_file = nullptr;
121 MemoryMappedFile::Region g_icudtl_extra_region;
122
123 #if defined(OS_FUCHSIA)
124 // The directory from which the ICU data loader will be configured to load time
125 // zone data. It is only changed by SetIcuTimeZoneDataDirForTesting().
126 const char* g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
127 #endif // defined(OS_FUCHSIA)
128
129 struct PfRegion {
130 public:
131 PlatformFile pf;
132 MemoryMappedFile::Region region;
133 };
134
OpenIcuDataFile(const std::string & filename)135 std::unique_ptr<PfRegion> OpenIcuDataFile(const std::string& filename) {
136 auto result = std::make_unique<PfRegion>();
137 #if defined(OS_ANDROID)
138 result->pf =
139 android::OpenApkAsset(kAssetsPathPrefix + filename, &result->region);
140 if (result->pf != -1) {
141 return result;
142 }
143 #endif // defined(OS_ANDROID)
144 #if defined(TOOLKIT_QT)
145 FilePath data_path;
146 PathService::Get(base::DIR_QT_LIBRARY_DATA, &data_path);
147 data_path = data_path.AppendASCII(kIcuDataFileName);
148 #elif !defined(OS_MACOSX)
149 // For unit tests, data file is located on disk, so try there as a fallback.
150 FilePath data_path;
151 if (!PathService::Get(DIR_ASSETS, &data_path)) {
152 LOG(ERROR) << "Can't find " << filename;
153 return nullptr;
154 }
155 #if defined(OS_WIN)
156 // TODO(brucedawson): http://crbug.com/445616
157 wchar_t tmp_buffer[_MAX_PATH] = {0};
158 wcscpy_s(tmp_buffer, data_path.value().c_str());
159 debug::Alias(tmp_buffer);
160 #endif
161 data_path = data_path.AppendASCII(filename);
162
163 #if defined(OS_WIN)
164 // TODO(brucedawson): http://crbug.com/445616
165 wchar_t tmp_buffer2[_MAX_PATH] = {0};
166 wcscpy_s(tmp_buffer2, data_path.value().c_str());
167 debug::Alias(tmp_buffer2);
168 #endif
169
170 #else // !defined(OS_MACOSX)
171 // Assume it is in the framework bundle's Resources directory.
172 ScopedCFTypeRef<CFStringRef> data_file_name(SysUTF8ToCFStringRef(filename));
173 FilePath data_path = mac::PathForFrameworkBundleResource(data_file_name);
174 #if defined(OS_IOS)
175 FilePath override_data_path = ios::FilePathOfEmbeddedICU();
176 if (!override_data_path.empty()) {
177 data_path = override_data_path;
178 }
179 #endif // !defined(OS_IOS)
180 if (data_path.empty()) {
181 LOG(ERROR) << filename << " not found in bundle";
182 return nullptr;
183 }
184 #endif // !defined(OS_MACOSX)
185 File file(data_path, File::FLAG_OPEN | File::FLAG_READ);
186 if (file.IsValid()) {
187 // TODO(brucedawson): http://crbug.com/445616.
188 g_debug_icu_pf_last_error = 0;
189 g_debug_icu_pf_error_details = 0;
190 #if defined(OS_WIN)
191 g_debug_icu_pf_filename[0] = 0;
192 #endif // OS_WIN
193
194 result->pf = file.TakePlatformFile();
195 result->region = MemoryMappedFile::Region::kWholeFile;
196 }
197 #if defined(OS_WIN)
198 else {
199 // TODO(brucedawson): http://crbug.com/445616.
200 g_debug_icu_pf_last_error = ::GetLastError();
201 g_debug_icu_pf_error_details = file.error_details();
202 wcscpy_s(g_debug_icu_pf_filename, data_path.value().c_str());
203 }
204 #endif // OS_WIN
205
206 return result;
207 }
208
LazyOpenIcuDataFile()209 void LazyOpenIcuDataFile() {
210 if (g_icudtl_pf != kInvalidPlatformFile) {
211 return;
212 }
213 auto pf_region = OpenIcuDataFile(kIcuDataFileName);
214 if (!pf_region) {
215 return;
216 }
217 g_icudtl_pf = pf_region->pf;
218 g_icudtl_region = pf_region->region;
219 }
220
221 // Configures ICU to load external time zone data, if appropriate.
InitializeExternalTimeZoneData()222 void InitializeExternalTimeZoneData() {
223 #if defined(OS_FUCHSIA)
224 if (!base::DirectoryExists(base::FilePath(g_icu_time_zone_data_dir))) {
225 // TODO(https://crbug.com/1061262): Make this FATAL unless expected.
226 PLOG(WARNING) << "Could not open: '" << g_icu_time_zone_data_dir
227 << "'. Using built-in timezone database";
228 return;
229 }
230
231 // Set the environment variable to override the location used by ICU.
232 // Loading can still fail if the directory is empty or its data is invalid.
233 std::unique_ptr<base::Environment> env = base::Environment::Create();
234 env->SetVar(kIcuTimeZoneEnvVariable, g_icu_time_zone_data_dir);
235 #endif // defined(OS_FUCHSIA)
236 }
237
LoadIcuData(PlatformFile data_fd,const MemoryMappedFile::Region & data_region,std::unique_ptr<MemoryMappedFile> * out_mapped_data_file,UErrorCode * out_error_code)238 int LoadIcuData(PlatformFile data_fd,
239 const MemoryMappedFile::Region& data_region,
240 std::unique_ptr<MemoryMappedFile>* out_mapped_data_file,
241 UErrorCode* out_error_code) {
242 InitializeExternalTimeZoneData();
243
244 if (data_fd == kInvalidPlatformFile) {
245 LOG(ERROR) << "Invalid file descriptor to ICU data received.";
246 return 1; // To debug http://crbug.com/445616.
247 }
248
249 out_mapped_data_file->reset(new MemoryMappedFile());
250 if (!(*out_mapped_data_file)->Initialize(File(data_fd), data_region)) {
251 LOG(ERROR) << "Couldn't mmap icu data file";
252 return 2; // To debug http://crbug.com/445616.
253 }
254
255 (*out_error_code) = U_ZERO_ERROR;
256 udata_setCommonData(const_cast<uint8_t*>((*out_mapped_data_file)->data()),
257 out_error_code);
258 if (U_FAILURE(*out_error_code)) {
259 LOG(ERROR) << "Failed to initialize ICU with data file: "
260 << u_errorName(*out_error_code);
261 return 3; // To debug http://crbug.com/445616.
262 }
263
264 return 0;
265 }
266
InitializeICUWithFileDescriptorInternal(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)267 bool InitializeICUWithFileDescriptorInternal(
268 PlatformFile data_fd,
269 const MemoryMappedFile::Region& data_region) {
270 // This can be called multiple times in tests.
271 if (g_icudtl_mapped_file) {
272 g_debug_icu_load = 0; // To debug http://crbug.com/445616.
273 return true;
274 }
275
276 std::unique_ptr<MemoryMappedFile> mapped_file;
277 UErrorCode err;
278 g_debug_icu_load = LoadIcuData(data_fd, data_region, &mapped_file, &err);
279 if (g_debug_icu_load == 1 || g_debug_icu_load == 2) {
280 return false;
281 }
282 g_icudtl_mapped_file = mapped_file.release();
283
284 if (g_debug_icu_load == 3) {
285 g_debug_icu_last_error = err;
286 }
287
288 // Never try to load ICU data from files.
289 udata_setFileAccess(UDATA_ONLY_PACKAGES, &err);
290 return U_SUCCESS(err);
291 }
292
InitializeICUFromDataFile()293 bool InitializeICUFromDataFile() {
294 // If the ICU data directory is set, ICU won't actually load the data until
295 // it is needed. This can fail if the process is sandboxed at that time.
296 // Instead, we map the file in and hand off the data so the sandbox won't
297 // cause any problems.
298 LazyOpenIcuDataFile();
299 bool result =
300 InitializeICUWithFileDescriptorInternal(g_icudtl_pf, g_icudtl_region);
301
302 #if defined(OS_WIN)
303 int debug_icu_load = g_debug_icu_load;
304 debug::Alias(&debug_icu_load);
305 int debug_icu_last_error = g_debug_icu_last_error;
306 debug::Alias(&debug_icu_last_error);
307 int debug_icu_pf_last_error = g_debug_icu_pf_last_error;
308 debug::Alias(&debug_icu_pf_last_error);
309 int debug_icu_pf_error_details = g_debug_icu_pf_error_details;
310 debug::Alias(&debug_icu_pf_error_details);
311 wchar_t debug_icu_pf_filename[_MAX_PATH] = {0};
312 wcscpy_s(debug_icu_pf_filename, g_debug_icu_pf_filename);
313 debug::Alias(&debug_icu_pf_filename);
314 CHECK(result); // TODO(brucedawson): http://crbug.com/445616
315 #endif // defined(OS_WIN)
316
317 return result;
318 }
319 #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
320
321 // Explicitly initialize ICU's time zone if necessary.
322 // On some platforms, the time zone must be explicitly initialized zone rather
323 // than relying on ICU's internal initialization.
InitializeIcuTimeZone()324 void InitializeIcuTimeZone() {
325 #if defined(OS_ANDROID)
326 // On Android, we can't leave it up to ICU to set the default time zone
327 // because ICU's time zone detection does not work in many time zones (e.g.
328 // Australia/Sydney, Asia/Seoul, Europe/Paris ). Use JNI to detect the host
329 // time zone and set the ICU default time zone accordingly in advance of
330 // actual use. See crbug.com/722821 and
331 // https://ssl.icu-project.org/trac/ticket/13208 .
332 string16 zone_id = android::GetDefaultTimeZoneId();
333 icu::TimeZone::adoptDefault(icu::TimeZone::createTimeZone(
334 icu::UnicodeString(FALSE, zone_id.data(), zone_id.length())));
335 #elif defined(OS_FUCHSIA)
336 // The platform-specific mechanisms used by ICU's detectHostTimeZone() to
337 // determine the default time zone will not work on Fuchsia. Therefore,
338 // proactively set the default system.
339 // This is also required by TimeZoneMonitorFuchsia::ProfileMayHaveChanged(),
340 // which uses the current default to detect whether the time zone changed in
341 // the new profile.
342 // If the system time zone cannot be obtained or is not understood by ICU,
343 // the "unknown" time zone will be returned by createTimeZone() and used.
344 std::string zone_id =
345 fuchsia::IntlProfileWatcher::GetPrimaryTimeZoneIdForIcuInitialization();
346 icu::TimeZone::adoptDefault(
347 icu::TimeZone::createTimeZone(icu::UnicodeString::fromUTF8(zone_id)));
348 #elif (defined(OS_LINUX) || defined(OS_BSD)) && !BUILDFLAG(IS_CHROMECAST)
349 // To respond to the time zone change properly, the default time zone
350 // cache in ICU has to be populated on starting up.
351 // See TimeZoneMonitorLinux::NotifyClientsFromImpl().
352 std::unique_ptr<icu::TimeZone> zone(icu::TimeZone::createDefault());
353 #endif // defined(OS_ANDROID)
354 }
355
356 const char kICUDataFile[] = "ICU.DataFile";
357 const char kICUCreateInstance[] = "ICU.CreateInstance";
358
359 enum class ICUCreateInstance {
360 kCharacterBreakIterator = 0,
361 kWordBreakIterator = 1,
362 kLineBreakIterator = 2,
363 kLineBreakIteratorTypeLoose = 3,
364 kLineBreakIteratorTypeNormal = 4,
365 kLineBreakIteratorTypeStrict = 5,
366 kSentenceBreakIterator = 6,
367 kTitleBreakIterator = 7,
368 kThaiBreakEngine = 8,
369 kLaoBreakEngine = 9,
370 kBurmeseBreakEngine = 10,
371 kKhmerBreakEngine = 11,
372 kChineseJapaneseBreakEngine = 12,
373
374 kMaxValue = kChineseJapaneseBreakEngine
375 };
376
377 // Callback functions to report the opening of ICU Data File, and creation of
378 // key objects to UMA. This help us to understand what built-in ICU data files
379 // are rarely used in the user's machines and the distribution of ICU usage.
TraceICUEntry(const void *,int32_t fn_number)380 static void U_CALLCONV TraceICUEntry(const void*, int32_t fn_number) {
381 #if !defined(USING_SYSTEM_ICU) || U_ICU_VERSION_MAJOR_NUM >= 67
382 switch (fn_number) {
383 case UTRACE_UBRK_CREATE_CHARACTER:
384 base::UmaHistogramEnumeration(kICUCreateInstance,
385 ICUCreateInstance::kCharacterBreakIterator);
386 break;
387 case UTRACE_UBRK_CREATE_SENTENCE:
388 base::UmaHistogramEnumeration(kICUCreateInstance,
389 ICUCreateInstance::kSentenceBreakIterator);
390 break;
391 case UTRACE_UBRK_CREATE_TITLE:
392 base::UmaHistogramEnumeration(kICUCreateInstance,
393 ICUCreateInstance::kTitleBreakIterator);
394 break;
395 case UTRACE_UBRK_CREATE_WORD:
396 base::UmaHistogramEnumeration(kICUCreateInstance,
397 ICUCreateInstance::kWordBreakIterator);
398 break;
399 default:
400 return;
401 }
402 #endif
403 }
404
TraceICUData(const void * context,int32_t fn_number,int32_t level,const char * fmt,va_list args)405 static void U_CALLCONV TraceICUData(const void* context,
406 int32_t fn_number,
407 int32_t level,
408 const char* fmt,
409 va_list args) {
410 switch (fn_number) {
411 case UTRACE_UDATA_DATA_FILE: {
412 std::string icu_data_file_name(va_arg(args, const char*));
413 va_end(args);
414 // Skip icu version specified prefix if exist.
415 // path is prefixed with icu version prefix such as "icudt65l-".
416 // Histogram only the part after the -.
417 if (icu_data_file_name.find("icudt") == 0) {
418 size_t dash = icu_data_file_name.find("-");
419 if (dash != std::string::npos) {
420 icu_data_file_name = icu_data_file_name.substr(dash + 1);
421 }
422 }
423 // UmaHistogramSparse should track less than 100 values.
424 // We currently have about total 55 built-in data files inside ICU
425 // so it fit the UmaHistogramSparse usage.
426 int hash = base::HashMetricName(icu_data_file_name);
427 base::UmaHistogramSparse(kICUDataFile, hash);
428 return;
429 }
430 #if !defined(USING_SYSTEM_ICU) || U_ICU_VERSION_MAJOR_NUM >= 67
431 case UTRACE_UBRK_CREATE_LINE: {
432 const char* lb_type = va_arg(args, const char*);
433 va_end(args);
434 ICUCreateInstance value;
435 switch (lb_type[0]) {
436 case '\0':
437 value = ICUCreateInstance::kLineBreakIterator;
438 break;
439 case 'l':
440 DCHECK(strcmp(lb_type, "loose") == 0);
441 value = ICUCreateInstance::kLineBreakIteratorTypeLoose;
442 break;
443 case 'n':
444 DCHECK(strcmp(lb_type, "normal") == 0);
445 value = ICUCreateInstance::kLineBreakIteratorTypeNormal;
446 break;
447 case 's':
448 DCHECK(strcmp(lb_type, "strict") == 0);
449 value = ICUCreateInstance::kLineBreakIteratorTypeStrict;
450 break;
451 default:
452 return;
453 }
454 base::UmaHistogramEnumeration(kICUCreateInstance, value);
455 return;
456 }
457 case UTRACE_UBRK_CREATE_BREAK_ENGINE: {
458 const char* script = va_arg(args, const char*);
459 va_end(args);
460 ICUCreateInstance value;
461 switch (script[0]) {
462 case 'H':
463 DCHECK(strcmp(script, "Hani") == 0);
464 value = ICUCreateInstance::kChineseJapaneseBreakEngine;
465 break;
466 case 'K':
467 DCHECK(strcmp(script, "Khmr") == 0);
468 value = ICUCreateInstance::kKhmerBreakEngine;
469 break;
470 case 'L':
471 DCHECK(strcmp(script, "Laoo") == 0);
472 value = ICUCreateInstance::kLaoBreakEngine;
473 break;
474 case 'M':
475 DCHECK(strcmp(script, "Mymr") == 0);
476 value = ICUCreateInstance::kBurmeseBreakEngine;
477 break;
478 case 'T':
479 DCHECK(strcmp(script, "Thai") == 0);
480 value = ICUCreateInstance::kThaiBreakEngine;
481 break;
482 default:
483 return;
484 }
485 base::UmaHistogramEnumeration(kICUCreateInstance, value);
486 return;
487 }
488 #endif
489 }
490 }
491
492 // Common initialization to run regardless of how ICU is initialized.
493 // There are multiple exposed InitializeIcu* functions. This should be called
494 // as at the end of (the last functions in the sequence of) these functions.
DoCommonInitialization()495 bool DoCommonInitialization() {
496 // TODO(jungshik): Some callers do not care about tz at all. If necessary,
497 // add a boolean argument to this function to init the default tz only
498 // when requested.
499 InitializeIcuTimeZone();
500
501 const void* context = nullptr;
502 utrace_setFunctions(context, TraceICUEntry, nullptr, TraceICUData);
503 utrace_setLevel(UTRACE_VERBOSE);
504 return true;
505 }
506
507 } // namespace
508
509 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
InitializeExtraICUWithFileDescriptor(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)510 bool InitializeExtraICUWithFileDescriptor(
511 PlatformFile data_fd,
512 const MemoryMappedFile::Region& data_region) {
513 if (g_icudtl_pf != kInvalidPlatformFile) {
514 // Must call InitializeExtraICUWithFileDescriptor() before
515 // InitializeICUWithFileDescriptor().
516 return false;
517 }
518 std::unique_ptr<MemoryMappedFile> mapped_file;
519 UErrorCode err;
520 if (LoadIcuData(data_fd, data_region, &mapped_file, &err) != 0) {
521 return false;
522 }
523 g_icudtl_extra_mapped_file = mapped_file.release();
524 return true;
525 }
526
InitializeICUWithFileDescriptor(PlatformFile data_fd,const MemoryMappedFile::Region & data_region)527 bool InitializeICUWithFileDescriptor(
528 PlatformFile data_fd,
529 const MemoryMappedFile::Region& data_region) {
530 #if DCHECK_IS_ON()
531 DCHECK(!g_check_called_once || !g_called_once);
532 g_called_once = true;
533 #endif
534 if (!InitializeICUWithFileDescriptorInternal(data_fd, data_region))
535 return false;
536
537 return DoCommonInitialization();
538 }
539
GetIcuDataFileHandle(MemoryMappedFile::Region * out_region)540 PlatformFile GetIcuDataFileHandle(MemoryMappedFile::Region* out_region) {
541 CHECK_NE(g_icudtl_pf, kInvalidPlatformFile);
542 *out_region = g_icudtl_region;
543 return g_icudtl_pf;
544 }
545
GetIcuExtraDataFileHandle(MemoryMappedFile::Region * out_region)546 PlatformFile GetIcuExtraDataFileHandle(MemoryMappedFile::Region* out_region) {
547 if (g_icudtl_extra_pf == kInvalidPlatformFile) {
548 return kInvalidPlatformFile;
549 }
550 *out_region = g_icudtl_extra_region;
551 return g_icudtl_extra_pf;
552 }
553
InitializeExtraICU()554 bool InitializeExtraICU() {
555 if (g_icudtl_pf != kInvalidPlatformFile) {
556 // Must call InitializeExtraICU() before InitializeICU().
557 return false;
558 }
559 auto pf_region = OpenIcuDataFile(kIcuExtraDataFileName);
560 if (!pf_region) {
561 return false;
562 }
563 g_icudtl_extra_pf = pf_region->pf;
564 g_icudtl_extra_region = pf_region->region;
565 std::unique_ptr<MemoryMappedFile> mapped_file;
566 UErrorCode err;
567 if (LoadIcuData(g_icudtl_extra_pf, g_icudtl_extra_region, &mapped_file,
568 &err) != 0) {
569 return false;
570 }
571 g_icudtl_extra_mapped_file = mapped_file.release();
572 return true;
573 }
574
ResetGlobalsForTesting()575 void ResetGlobalsForTesting() {
576 g_icudtl_pf = kInvalidPlatformFile;
577 g_icudtl_mapped_file = nullptr;
578 g_icudtl_extra_pf = kInvalidPlatformFile;
579 g_icudtl_extra_mapped_file = nullptr;
580 #if defined(OS_FUCHSIA)
581 g_icu_time_zone_data_dir = kIcuTimeZoneDataDir;
582 #endif // defined(OS_FUCHSIA)
583 }
584
585 #if defined(OS_FUCHSIA)
586 // |dir| must remain valid until ResetGlobalsForTesting() is called.
SetIcuTimeZoneDataDirForTesting(const char * dir)587 void SetIcuTimeZoneDataDirForTesting(const char* dir) {
588 g_icu_time_zone_data_dir = dir;
589 }
590 #endif // defined(OS_FUCHSIA)
591 #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
592
InitializeICU()593 bool InitializeICU() {
594 #if DCHECK_IS_ON()
595 DCHECK(!g_check_called_once || !g_called_once);
596 g_called_once = true;
597 #endif
598
599 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
600 // The ICU data is statically linked.
601 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
602 if (!InitializeICUFromDataFile())
603 return false;
604 #else
605 #error Unsupported ICU_UTIL_DATA_IMPL value
606 #endif // (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
607
608 return DoCommonInitialization();
609 }
610
AllowMultipleInitializeCallsForTesting()611 void AllowMultipleInitializeCallsForTesting() {
612 #if DCHECK_IS_ON()
613 g_check_called_once = false;
614 #endif
615 }
616
617 #endif // !defined(OS_NACL)
618
619 } // namespace i18n
620 } // namespace base
621