Home | History | Annotate | Download | only in chrome_frame
      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 "chrome_frame/simple_resource_loader.h"
      6 
      7 #include <atlbase.h>
      8 
      9 #include <algorithm>
     10 
     11 #include "base/base_paths.h"
     12 #include "base/file_util.h"
     13 #include "base/files/file_path.h"
     14 #include "base/i18n/rtl.h"
     15 #include "base/memory/singleton.h"
     16 #include "base/path_service.h"
     17 #include "base/strings/string_util.h"
     18 #include "base/strings/utf_string_conversions.h"
     19 #include "base/win/i18n.h"
     20 #include "base/win/windows_version.h"
     21 #include "chrome_frame/policy_settings.h"
     22 #include "ui/base/resource/data_pack.h"
     23 
     24 namespace {
     25 
     26 const wchar_t kLocalesDirName[] = L"Locales";
     27 
     28 bool IsInvalidTagCharacter(wchar_t tag_character) {
     29   return !(L'-' == tag_character ||
     30            IsAsciiDigit(tag_character) ||
     31            IsAsciiAlpha(tag_character));
     32 }
     33 
     34 // A helper function object that performs a lower-case ASCII comparison between
     35 // two strings.
     36 class CompareInsensitiveASCII
     37     : public std::unary_function<const std::wstring&, bool> {
     38  public:
     39   explicit CompareInsensitiveASCII(const std::wstring& value)
     40       : value_lowered_(WideToASCII(value)) {
     41     StringToLowerASCII(&value_lowered_);
     42   }
     43   bool operator()(const std::wstring& comparand) {
     44     return LowerCaseEqualsASCII(comparand, value_lowered_.c_str());
     45   }
     46 
     47  private:
     48   std::string value_lowered_;
     49 };
     50 
     51 // Returns true if the value was added.
     52 bool PushBackIfAbsent(
     53     const std::wstring& value,
     54     std::vector<std::wstring>* collection) {
     55   if (collection->end() ==
     56       std::find_if(collection->begin(), collection->end(),
     57                    CompareInsensitiveASCII(value))) {
     58     collection->push_back(value);
     59     return true;
     60   }
     61   return false;
     62 }
     63 
     64 // Returns true if the collection is modified.
     65 bool PushBackWithFallbackIfAbsent(
     66     const std::wstring& language,
     67     std::vector<std::wstring>* collection) {
     68   bool modified = false;
     69 
     70   if (!language.empty()) {
     71     // Try adding the language itself.
     72     modified = PushBackIfAbsent(language, collection);
     73 
     74     // Now try adding its fallback, if it has one.
     75     std::wstring::size_type dash_pos = language.find(L'-');
     76     if (0 < dash_pos && language.size() - 1 > dash_pos)
     77       modified |= PushBackIfAbsent(language.substr(0, dash_pos), collection);
     78   }
     79 
     80   return modified;
     81 }
     82 
     83 }  // namespace
     84 
     85 SimpleResourceLoader::SimpleResourceLoader()
     86     : data_pack_(NULL),
     87       locale_dll_handle_(NULL) {
     88   // Find and load the resource DLL.
     89   std::vector<std::wstring> language_tags;
     90 
     91   // First, try the locale dictated by policy and its fallback.
     92   PushBackWithFallbackIfAbsent(
     93       PolicySettings::GetInstance()->ApplicationLocale(),
     94       &language_tags);
     95 
     96   // Next, try the thread, process, user, system languages.
     97   GetPreferredLanguages(&language_tags);
     98 
     99   // Finally, fall-back on "en-US" (which may already be present in the vector,
    100   // but that's okay since we'll exit with success when the first is tried).
    101   language_tags.push_back(L"en-US");
    102 
    103   base::FilePath locales_path;
    104 
    105   DetermineLocalesDirectory(&locales_path);
    106   if (!LoadLocalePack(language_tags, locales_path, &locale_dll_handle_,
    107                       &data_pack_, &language_)) {
    108     NOTREACHED() << "Failed loading any resource dll (even \"en-US\").";
    109   }
    110 }
    111 
    112 SimpleResourceLoader::~SimpleResourceLoader() {
    113   delete data_pack_;
    114 }
    115 
    116 // static
    117 SimpleResourceLoader* SimpleResourceLoader::GetInstance() {
    118   return Singleton<SimpleResourceLoader>::get();
    119 }
    120 
    121 // static
    122 void SimpleResourceLoader::GetPreferredLanguages(
    123     std::vector<std::wstring>* language_tags) {
    124   DCHECK(language_tags);
    125   // The full set of preferred languages and their fallbacks are given priority.
    126   std::vector<std::wstring> languages;
    127   if (base::win::i18n::GetThreadPreferredUILanguageList(&languages)) {
    128     for (std::vector<std::wstring>::const_iterator scan = languages.begin(),
    129              end = languages.end(); scan != end; ++scan) {
    130       PushBackIfAbsent(*scan, language_tags);
    131     }
    132   }
    133   // Use the base i18n routines (i.e., ICU) as a last, best hope for something
    134   // meaningful for the user.
    135   PushBackWithFallbackIfAbsent(ASCIIToWide(base::i18n::GetConfiguredLocale()),
    136                                language_tags);
    137 }
    138 
    139 // static
    140 void SimpleResourceLoader::DetermineLocalesDirectory(
    141     base::FilePath* locales_path) {
    142   DCHECK(locales_path);
    143 
    144   base::FilePath module_path;
    145   PathService::Get(base::DIR_MODULE, &module_path);
    146   *locales_path = module_path.Append(kLocalesDirName);
    147 
    148   // We may be residing in the "locales" directory's parent, or we might be
    149   // in a sibling directory. Move up one and look for Locales again in the
    150   // latter case.
    151   if (!base::DirectoryExists(*locales_path)) {
    152     *locales_path = module_path.DirName();
    153     *locales_path = locales_path->Append(kLocalesDirName);
    154   }
    155 
    156   // Don't make a second check to see if the dir is in the parent.  We'll notice
    157   // and log that in LoadLocaleDll when we actually try loading DLLs.
    158 }
    159 
    160 // static
    161 bool SimpleResourceLoader::IsValidLanguageTag(
    162     const std::wstring& language_tag) {
    163   // "[a-zA-Z]+(-[a-zA-Z0-9]+)*" is a simplification, but better than nothing.
    164   // Rather than pick up the weight of a regex processor, just search for a
    165   // character that isn't in the above set.  This will at least weed out
    166   // attempts at "../../EvilBinary".
    167   return language_tag.end() == std::find_if(language_tag.begin(),
    168                                             language_tag.end(),
    169                                             &IsInvalidTagCharacter);
    170 }
    171 
    172 // static
    173 bool SimpleResourceLoader::LoadLocalePack(
    174     const std::vector<std::wstring>& language_tags,
    175     const base::FilePath& locales_path,
    176     HMODULE* dll_handle,
    177     ui::DataPack** data_pack,
    178     std::wstring* language) {
    179   DCHECK(language);
    180 
    181   // The dll should only have resources, not executable code.
    182   const DWORD load_flags =
    183       (base::win::GetVersion() >= base::win::VERSION_VISTA ?
    184           LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE :
    185           DONT_RESOLVE_DLL_REFERENCES);
    186 
    187   const std::wstring dll_suffix(L".dll");
    188   const std::wstring pack_suffix(L".pak");
    189 
    190   bool found_pack = false;
    191 
    192   for (std::vector<std::wstring>::const_iterator scan = language_tags.begin(),
    193          end = language_tags.end();
    194        scan != end;
    195        ++scan) {
    196     if (!IsValidLanguageTag(*scan)) {
    197       LOG(WARNING) << "Invalid language tag supplied while locating resources:"
    198                       " \"" << *scan << "\"";
    199       continue;
    200     }
    201 
    202     // Attempt to load both the resource pack and the dll. We return success
    203     // only we load both.
    204     base::FilePath resource_pack_path =
    205         locales_path.Append(*scan + pack_suffix);
    206     base::FilePath dll_path = locales_path.Append(*scan + dll_suffix);
    207 
    208     if (base::PathExists(resource_pack_path) &&
    209         base::PathExists(dll_path)) {
    210       scoped_ptr<ui::DataPack> cur_data_pack(
    211           new ui::DataPack(ui::SCALE_FACTOR_100P));
    212       if (!cur_data_pack->LoadFromPath(resource_pack_path))
    213         continue;
    214 
    215       HMODULE locale_dll_handle = LoadLibraryEx(dll_path.value().c_str(), NULL,
    216                                                 load_flags);
    217       if (locale_dll_handle) {
    218         *dll_handle = locale_dll_handle;
    219         *language = dll_path.BaseName().RemoveExtension().value();
    220         *data_pack = cur_data_pack.release();
    221         found_pack = true;
    222         break;
    223       } else {
    224         *data_pack = NULL;
    225       }
    226     }
    227   }
    228   DCHECK(found_pack || base::DirectoryExists(locales_path))
    229       << "Could not locate locales DLL directory.";
    230   return found_pack;
    231 }
    232 
    233 std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) {
    234   if (!data_pack_) {
    235     DLOG(ERROR) << "locale resources are not loaded";
    236     return std::wstring();
    237   }
    238 
    239   DCHECK(IS_INTRESOURCE(message_id));
    240 
    241   base::StringPiece data;
    242   if (!data_pack_->GetStringPiece(message_id, &data)) {
    243     DLOG(ERROR) << "Unable to find string for resource id:" << message_id;
    244     return std::wstring();
    245   }
    246 
    247   // Data pack encodes strings as either UTF8 or UTF16.
    248   string16 msg;
    249   if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF16) {
    250     msg = string16(reinterpret_cast<const char16*>(data.data()),
    251                    data.length() / 2);
    252   } else if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF8) {
    253     msg = UTF8ToUTF16(data);
    254   }
    255   return msg;
    256 }
    257 
    258 // static
    259 std::wstring SimpleResourceLoader::GetLanguage() {
    260   return SimpleResourceLoader::GetInstance()->language_;
    261 }
    262 
    263 // static
    264 std::wstring SimpleResourceLoader::Get(int message_id) {
    265   SimpleResourceLoader* loader = SimpleResourceLoader::GetInstance();
    266   return loader->GetLocalizedResource(message_id);
    267 }
    268 
    269 HMODULE SimpleResourceLoader::GetResourceModuleHandle() {
    270   return locale_dll_handle_;
    271 }
    272