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 #include "build/build_config.h" 8 9 #if defined(OS_WIN) 10 #include <windows.h> 11 #endif 12 13 #include <string> 14 15 #include "base/files/file_path.h" 16 #include "base/files/memory_mapped_file.h" 17 #include "base/logging.h" 18 #include "base/path_service.h" 19 #include "base/strings/string_util.h" 20 #include "base/strings/sys_string_conversions.h" 21 #include "third_party/icu/source/common/unicode/putil.h" 22 #include "third_party/icu/source/common/unicode/udata.h" 23 24 #if defined(OS_MACOSX) 25 #include "base/mac/foundation_util.h" 26 #endif 27 28 #define ICU_UTIL_DATA_FILE 0 29 #define ICU_UTIL_DATA_SHARED 1 30 #define ICU_UTIL_DATA_STATIC 2 31 32 #if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE 33 #define ICU_UTIL_DATA_FILE_NAME "icudt" U_ICU_VERSION_SHORT "l.dat" 34 #elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED 35 #define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" 36 #if defined(OS_WIN) 37 #define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" 38 #endif 39 #endif 40 41 namespace base { 42 namespace i18n { 43 44 bool InitializeICU() { 45 #ifndef NDEBUG 46 // Assert that we are not called more than once. Even though calling this 47 // function isn't harmful (ICU can handle it), being called twice probably 48 // indicates a programming error. 49 static bool called_once = false; 50 DCHECK(!called_once); 51 called_once = true; 52 #endif 53 54 #if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) 55 // We expect to find the ICU data module alongside the current module. 56 FilePath data_path; 57 PathService::Get(base::DIR_MODULE, &data_path); 58 data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); 59 60 HMODULE module = LoadLibrary(data_path.value().c_str()); 61 if (!module) { 62 DLOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; 63 return false; 64 } 65 66 FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); 67 if (!addr) { 68 DLOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " 69 << ICU_UTIL_DATA_SHARED_MODULE_NAME; 70 return false; 71 } 72 73 UErrorCode err = U_ZERO_ERROR; 74 udata_setCommonData(reinterpret_cast<void*>(addr), &err); 75 return err == U_ZERO_ERROR; 76 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 77 // The ICU data is statically linked. 78 return true; 79 #elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 80 // If the ICU data directory is set, ICU won't actually load the data until 81 // it is needed. This can fail if the process is sandboxed at that time. 82 // Instead, we map the file in and hand off the data so the sandbox won't 83 // cause any problems. 84 85 // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever 86 // be released. 87 CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 88 if (!mapped_file.IsValid()) { 89 // Assume it is in the framework bundle's Resources directory. 90 #if !defined(OS_MACOSX) 91 // For now, expect the data file to be alongside the executable. 92 // This is sufficient while we work on unit tests, but will eventually 93 // likely live in a data directory. 94 FilePath data_path; 95 bool path_ok = PathService::Get(base::DIR_EXE, &data_path); 96 DCHECK(path_ok); 97 data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); 98 #else 99 FilePath data_path = 100 base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); 101 if (data_path.empty()) { 102 DLOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; 103 return false; 104 } 105 #endif // OS check 106 if (!mapped_file.Initialize(data_path)) { 107 DLOG(ERROR) << "Couldn't mmap " << data_path.value(); 108 return false; 109 } 110 } 111 UErrorCode err = U_ZERO_ERROR; 112 udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 113 return err == U_ZERO_ERROR; 114 #endif 115 } 116 117 } // namespace i18n 118 } // namespace base 119