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