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/files/file_path.h" 14 #include "base/files/memory_mapped_file.h" 15 #include "base/logging.h" 16 #include "base/path_service.h" 17 #include "base/strings/string_util.h" 18 #include "base/strings/sys_string_conversions.h" 19 #include "third_party/icu/source/common/unicode/putil.h" 20 #include "third_party/icu/source/common/unicode/udata.h" 21 22 #if defined(OS_MACOSX) 23 #include "base/mac/foundation_util.h" 24 #endif 25 26 #define ICU_UTIL_DATA_FILE 0 27 #define ICU_UTIL_DATA_SHARED 1 28 #define ICU_UTIL_DATA_STATIC 2 29 30 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE 31 // Use an unversioned file name to simplify a icu version update down the road. 32 // No need to change the filename in multiple places (gyp files, windows 33 // build pkg configurations, etc). 'l' stands for Little Endian. 34 #define ICU_UTIL_DATA_FILE_NAME "icudtl.dat" 35 #elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED 36 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" 37 #if defined(OS_WIN) 38 #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" 39 #endif 40 #endif 41 42 namespace base { 43 namespace i18n { 44 45 namespace { 46 47 #if !defined(NDEBUG) 48 // Assert that we are not called more than once. Even though calling this 49 // function isn't harmful (ICU can handle it), being called twice probably 50 // indicates a programming error. 51 bool g_called_once = false; 52 bool g_check_called_once = true; 53 #endif 54 } 55 56 57 #if defined(OS_ANDROID) 58 bool InitializeICUWithFileDescriptor(int data_fd) { 59 #if !defined(NDEBUG) 60 DCHECK(!g_check_called_once || !g_called_once); 61 g_called_once = true; 62 #endif 63 64 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 65 // The ICU data is statically linked. 66 return true; 67 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 68 CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 69 if (!mapped_file.IsValid()) { 70 if (!mapped_file.Initialize(base::File(data_fd))) { 71 LOG(ERROR) << "Couldn't mmap icu data file"; 72 return false; 73 } 74 } 75 UErrorCode err = U_ZERO_ERROR; 76 udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 77 return err == U_ZERO_ERROR; 78 #endif // ICU_UTIL_DATA_FILE 79 } 80 #endif 81 82 83 bool InitializeICU() { 84 #if !defined(NDEBUG) 85 DCHECK(!g_check_called_once || !g_called_once); 86 g_called_once = true; 87 #endif 88 89 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) 90 // We expect to find the ICU data module alongside the current module. 91 FilePath data_path; 92 PathService::Get(base::DIR_MODULE, &data_path); 93 data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); 94 95 HMODULE module = LoadLibrary(data_path.value().c_str()); 96 if (!module) { 97 LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; 98 return false; 99 } 100 101 FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); 102 if (!addr) { 103 LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " 104 << ICU_UTIL_DATA_SHARED_MODULE_NAME; 105 return false; 106 } 107 108 UErrorCode err = U_ZERO_ERROR; 109 udata_setCommonData(reinterpret_cast<void*>(addr), &err); 110 return err == U_ZERO_ERROR; 111 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 112 // The ICU data is statically linked. 113 return true; 114 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 115 // If the ICU data directory is set, ICU won't actually load the data until 116 // it is needed. This can fail if the process is sandboxed at that time. 117 // Instead, we map the file in and hand off the data so the sandbox won't 118 // cause any problems. 119 120 // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever 121 // be released. 122 CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 123 if (!mapped_file.IsValid()) { 124 #if !defined(OS_MACOSX) 125 FilePath data_path; 126 #if defined(OS_WIN) 127 // The data file will be in the same directory as the current module. 128 bool path_ok = PathService::Get(base::DIR_MODULE, &data_path); 129 #elif defined(OS_ANDROID) 130 bool path_ok = PathService::Get(base::DIR_ANDROID_APP_DATA, &data_path); 131 #else 132 // For now, expect the data file to be alongside the executable. 133 // This is sufficient while we work on unit tests, but will eventually 134 // likely live in a data directory. 135 bool path_ok = PathService::Get(base::DIR_EXE, &data_path); 136 #endif 137 DCHECK(path_ok); 138 data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); 139 #else 140 // Assume it is in the framework bundle's Resources directory. 141 FilePath data_path = 142 base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); 143 if (data_path.empty()) { 144 LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; 145 return false; 146 } 147 #endif // OS check 148 if (!mapped_file.Initialize(data_path)) { 149 LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe(); 150 return false; 151 } 152 } 153 UErrorCode err = U_ZERO_ERROR; 154 udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 155 return err == U_ZERO_ERROR; 156 #endif 157 } 158 159 void AllowMultipleInitializeCallsForTesting() { 160 #if !defined(NDEBUG) 161 g_check_called_once = false; 162 #endif 163 } 164 165 } // namespace i18n 166 } // namespace base 167