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