15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/i18n/icu_util.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h> 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/file_path.h" 142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/files/memory_mapped_file.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/path_service.h" 17868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/string_util.h" 182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/strings/sys_string_conversions.h" 19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/putil.h" 20ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch#include "third_party/icu/source/common/unicode/udata.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_MACOSX) 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/mac/foundation_util.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ICU_UTIL_DATA_FILE 0 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ICU_UTIL_DATA_SHARED 1 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ICU_UTIL_DATA_STATIC 2 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE 315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// Use an unversioned file name to simplify a icu version update down the road. 325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// No need to change the filename in multiple places (gyp files, windows 335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// build pkg configurations, etc). 'l' stands for Little Endian. 345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#define ICU_UTIL_DATA_FILE_NAME "icudtl.dat" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if defined(OS_WIN) 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt.dll" 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 423551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)namespace base { 433551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)namespace i18n { 442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles) 4523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)namespace { 4623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 4723730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#if !defined(NDEBUG) 4823730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// Assert that we are not called more than once. Even though calling this 4923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// function isn't harmful (ICU can handle it), being called twice probably 5023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)// indicates a programming error. 5123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)bool g_called_once = false; 5223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)bool g_check_called_once = true; 5323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#endif 5423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)} 5523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 56f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 57f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#if defined(OS_ANDROID) 58f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)bool InitializeICUWithFileDescriptor(int data_fd) { 59f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#if !defined(NDEBUG) 60f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) DCHECK(!g_check_called_once || !g_called_once); 61f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) g_called_once = true; 62f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif 63f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 64f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 65f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) // The ICU data is statically linked. 66f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return true; 67f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 68f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 69f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (!mapped_file.IsValid()) { 70f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) if (!mapped_file.Initialize(base::File(data_fd))) { 71f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) LOG(ERROR) << "Couldn't mmap icu data file"; 72f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return false; 73f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 74f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) } 75f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) UErrorCode err = U_ZERO_ERROR; 76f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) return err == U_ZERO_ERROR; 78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif // ICU_UTIL_DATA_FILE 79f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)} 80f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif 81f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 82f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) 833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)bool InitializeICU() { 8423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#if !defined(NDEBUG) 8523730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) DCHECK(!g_check_called_once || !g_called_once); 8623730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) g_called_once = true; 875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) 905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // We expect to find the ICU data module alongside the current module. 915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath data_path; 925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) PathService::Get(base::DIR_MODULE, &data_path); 935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) data_path = data_path.AppendASCII(ICU_UTIL_DATA_SHARED_MODULE_NAME); 945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) HMODULE module = LoadLibrary(data_path.value().c_str()); 965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!module) { 97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOG(ERROR) << "Failed to load " << ICU_UTIL_DATA_SHARED_MODULE_NAME; 985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); 1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!addr) { 103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOG(ERROR) << ICU_UTIL_DATA_SYMBOL << ": not found in " 1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) << ICU_UTIL_DATA_SHARED_MODULE_NAME; 1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UErrorCode err = U_ZERO_ERROR; 1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) udata_setCommonData(reinterpret_cast<void*>(addr), &err); 1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err == U_ZERO_ERROR; 1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) 112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // The ICU data is statically linked. 1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return true; 1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) 1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // If the ICU data directory is set, ICU won't actually load the data until 1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // it is needed. This can fail if the process is sandboxed at that time. 117a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // Instead, we map the file in and hand off the data so the sandbox won't 1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // cause any problems. 1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Chrome doesn't normally shut down ICU, so the mapped data shouldn't ever 1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // be released. 122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles) CR_DEFINE_STATIC_LOCAL(base::MemoryMappedFile, mapped_file, ()); 1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!mapped_file.IsValid()) { 124a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#if !defined(OS_MACOSX) 1255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) FilePath data_path; 1265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#if defined(OS_WIN) 1275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // The data file will be in the same directory as the current module. 1285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) bool path_ok = PathService::Get(base::DIR_MODULE, &data_path); 129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#elif defined(OS_ANDROID) 130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) bool path_ok = PathService::Get(base::DIR_ANDROID_APP_DATA, &data_path); 1315d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#else 132a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // For now, expect the data file to be alongside the executable. 133a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // This is sufficient while we work on unit tests, but will eventually 134a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) // likely live in a data directory. 135a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) bool path_ok = PathService::Get(base::DIR_EXE, &data_path); 1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#endif 137a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) DCHECK(path_ok); 138a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) data_path = data_path.AppendASCII(ICU_UTIL_DATA_FILE_NAME); 139a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#else 1405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) // Assume it is in the framework bundle's Resources directory. 1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) FilePath data_path = 1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) base::mac::PathForFrameworkBundleResource(CFSTR(ICU_UTIL_DATA_FILE_NAME)); 1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (data_path.empty()) { 144cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOG(ERROR) << ICU_UTIL_DATA_FILE_NAME << " not found in bundle"; 1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 147a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#endif // OS check 1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!mapped_file.Initialize(data_path)) { 149cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) LOG(ERROR) << "Couldn't mmap " << data_path.AsUTF8Unsafe(); 1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return false; 1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) UErrorCode err = U_ZERO_ERROR; 1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) udata_setCommonData(const_cast<uint8*>(mapped_file.data()), &err); 1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return err == U_ZERO_ERROR; 1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif 1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 15923730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)void AllowMultipleInitializeCallsForTesting() { 16023730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#if !defined(NDEBUG) 16123730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) g_check_called_once = false; 16223730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)#endif 16323730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles)} 16423730a6e56a168d1879203e4b3819bb36e3d8f1fTorne (Richard Coles) 1653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)} // namespace i18n 1663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)} // namespace base 167