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