1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/i18n/icu_util.h"
13 #include "base/debug/alias.h"
14 #include "base/files/file_path.h"
15 #include "base/files/memory_mapped_file.h"
16 #include "base/logging.h"
17 #include "base/path_service.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/sys_string_conversions.h"
20 #include "third_party/icu/source/common/unicode/putil.h"
21 #include "third_party/icu/source/common/unicode/udata.h"
22 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
23 #include "third_party/icu/source/i18n/unicode/timezone.h"
26 #if defined(OS_MACOSX)
27 #include "base/mac/foundation_util.h"
30 #define ICU_UTIL_DATA_FILE 0
31 #define ICU_UTIL_DATA_SHARED 1
32 #define ICU_UTIL_DATA_STATIC 2
37 // Use an unversioned file name to simplify a icu version update down the road.
38 // No need to change the filename in multiple places (gyp files, windows
39 // build pkg configurations, etc). 'l' stands for Little Endian.
40 // This variable is exported through the header file.
41 const char kIcuDataFileName
[] = "icudtl.dat";
42 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED
43 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat"
45 #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll"
52 // Assert that we are not called more than once. Even though calling this
53 // function isn't harmful (ICU can handle it), being called twice probably
54 // indicates a programming error.
56 bool g_called_once
= false;
58 bool g_check_called_once
= true;
63 bool InitializeICUWithFileDescriptor(
65 MemoryMappedFile::Region data_region
) {
67 DCHECK(!g_check_called_once
|| !g_called_once
);
71 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
72 // The ICU data is statically linked.
74 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
75 CR_DEFINE_STATIC_LOCAL(MemoryMappedFile
, mapped_file
, ());
76 if (!mapped_file
.IsValid()) {
77 if (!mapped_file
.Initialize(File(data_fd
), data_region
)) {
78 LOG(ERROR
) << "Couldn't mmap icu data file";
82 UErrorCode err
= U_ZERO_ERROR
;
83 udata_setCommonData(const_cast<uint8
*>(mapped_file
.data()), &err
);
84 return err
== U_ZERO_ERROR
;
85 #endif // ICU_UTIL_DATA_FILE
89 bool InitializeICU() {
91 DCHECK(!g_check_called_once
|| !g_called_once
);
96 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED)
97 // We expect to find the ICU data module alongside the current module.
99 PathService::Get(DIR_MODULE
, &data_path
);
100 data_path
= data_path
.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME
);
102 HMODULE module
= LoadLibrary(data_path
.value().c_str());
104 LOG(ERROR
) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME
;
108 FARPROC addr
= GetProcAddress(module
, ICU_UTIL_DATA_SYMBOL
);
110 LOG(ERROR
) << ICU_UTIL_DATA_SYMBOL
<< ": not found in "
111 << ICU_UTIL_DATA_SHARED_MODULE_NAME
;
115 UErrorCode err
= U_ZERO_ERROR
;
116 udata_setCommonData(reinterpret_cast<void*>(addr
), &err
);
117 result
= (err
== U_ZERO_ERROR
);
118 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC)
119 // The ICU data is statically linked.
121 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE)
122 // If the ICU data directory is set, ICU won't actually load the data until
123 // it is needed. This can fail if the process is sandboxed at that time.
124 // Instead, we map the file in and hand off the data so the sandbox won't
125 // cause any problems.
127 // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever
129 CR_DEFINE_STATIC_LOCAL(MemoryMappedFile
, mapped_file
, ());
130 if (!mapped_file
.IsValid()) {
131 #if !defined(OS_MACOSX)
134 // The data file will be in the same directory as the current module.
135 bool path_ok
= PathService::Get(DIR_MODULE
, &data_path
);
136 wchar_t tmp_buffer
[_MAX_PATH
] = {0};
137 wcscpy_s(tmp_buffer
, data_path
.value().c_str());
138 debug::Alias(tmp_buffer
);
139 CHECK(path_ok
); // TODO(scottmg): http://crbug.com/445616
140 #elif defined(OS_ANDROID)
141 bool path_ok
= PathService::Get(DIR_ANDROID_APP_DATA
, &data_path
);
143 // For now, expect the data file to be alongside the executable.
144 // This is sufficient while we work on unit tests, but will eventually
145 // likely live in a data directory.
146 bool path_ok
= PathService::Get(DIR_EXE
, &data_path
);
149 data_path
= data_path
.AppendASCII(kIcuDataFileName
);
152 // TODO(scottmg): http://crbug.com/445616
153 wchar_t tmp_buffer2
[_MAX_PATH
] = {0};
154 wcscpy_s(tmp_buffer2
, data_path
.value().c_str());
155 debug::Alias(tmp_buffer2
);
159 // Assume it is in the framework bundle's Resources directory.
160 ScopedCFTypeRef
<CFStringRef
> data_file_name(
161 SysUTF8ToCFStringRef(kIcuDataFileName
));
163 mac::PathForFrameworkBundleResource(data_file_name
);
164 if (data_path
.empty()) {
165 LOG(ERROR
) << kIcuDataFileName
<< " not found in bundle";
169 if (!mapped_file
.Initialize(data_path
)) {
171 CHECK(false); // TODO(scottmg): http://crbug.com/445616
173 LOG(ERROR
) << "Couldn't mmap " << data_path
.AsUTF8Unsafe();
177 UErrorCode err
= U_ZERO_ERROR
;
178 udata_setCommonData(const_cast<uint8
*>(mapped_file
.data()), &err
);
179 result
= (err
== U_ZERO_ERROR
);
181 CHECK(result
); // TODO(scottmg): http://crbug.com/445616
185 // To respond to the timezone change properly, the default timezone
186 // cache in ICU has to be populated on starting up.
187 // TODO(jungshik): Some callers do not care about tz at all. If necessary,
188 // add a boolean argument to this function to init'd the default tz only
190 #if defined(OS_LINUX) && !defined(OS_CHROMEOS)
192 scoped_ptr
<icu::TimeZone
> zone(icu::TimeZone::createDefault());
198 void AllowMultipleInitializeCallsForTesting() {
200 g_check_called_once
= false;