Home | History | Annotate | Download | only in i18n
      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