[email protected] | ec015c8 | 2012-04-12 23:41:53 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 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> |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 8 | |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 9 | #include <algorithm> |
| 10 | |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 11 | #include "base/base_paths.h" |
| 12 | #include "base/file_path.h" |
| 13 | #include "base/file_util.h" |
| 14 | #include "base/path_service.h" |
| 15 | #include "base/i18n/rtl.h" |
[email protected] | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 16 | #include "base/memory/singleton.h" |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 17 | #include "base/string_util.h" |
[email protected] | be1ce6a7 | 2010-08-03 14:35:22 | [diff] [blame] | 18 | #include "base/utf_string_conversions.h" |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 19 | #include "base/win/i18n.h" |
[email protected] | 935aa54 | 2010-10-15 01:59:15 | [diff] [blame] | 20 | #include "base/win/windows_version.h" |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 21 | #include "chrome_frame/policy_settings.h" |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 22 | #include "ui/base/resource/data_pack.h" |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 23 | |
| 24 | namespace { |
| 25 | |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 26 | const wchar_t kLocalesDirName[] = L"Locales"; |
| 27 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 28 | bool IsInvalidTagCharacter(wchar_t tag_character) { |
| 29 | return !(L'-' == tag_character || |
| 30 | IsAsciiDigit(tag_character) || |
| 31 | IsAsciiAlpha(tag_character)); |
| 32 | } |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 33 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 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 | |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 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 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 83 | } // namespace |
| 84 | |
| 85 | SimpleResourceLoader::SimpleResourceLoader() |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 86 | : data_pack_(NULL), |
| 87 | locale_dll_handle_(NULL) { |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 88 | // Find and load the resource DLL. |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 89 | std::vector<std::wstring> language_tags; |
| 90 | |
| 91 | // First, try the locale dictated by policy and its fallback. |
[email protected] | 687b960 | 2010-12-08 10:43:08 | [diff] [blame] | 92 | PushBackWithFallbackIfAbsent( |
| 93 | PolicySettings::GetInstance()->ApplicationLocale(), |
| 94 | &language_tags); |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 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 | FilePath locales_path; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 104 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 105 | DetermineLocalesDirectory(&locales_path); |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 106 | if (!LoadLocalePack(language_tags, locales_path, &locale_dll_handle_, |
| 107 | &data_pack_, &language_)) { |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 108 | NOTREACHED() << "Failed loading any resource dll (even \"en-US\")."; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 109 | } |
| 110 | } |
| 111 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 112 | SimpleResourceLoader::~SimpleResourceLoader() { |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 113 | delete data_pack_; |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 114 | } |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 115 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 116 | // static |
[email protected] | 8e8bb6d | 2010-12-13 08:18:55 | [diff] [blame] | 117 | SimpleResourceLoader* SimpleResourceLoader::GetInstance() { |
[email protected] | 687b960 | 2010-12-08 10:43:08 | [diff] [blame] | 118 | return Singleton<SimpleResourceLoader>::get(); |
| 119 | } |
| 120 | |
| 121 | // static |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 122 | void SimpleResourceLoader::GetPreferredLanguages( |
| 123 | std::vector<std::wstring>* language_tags) { |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 124 | DCHECK(language_tags); |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 125 | // The full set of preferred languages and their fallbacks are given priority. |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 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); |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 131 | } |
| 132 | } |
[email protected] | 337ca07 | 2010-11-16 15:28:45 | [diff] [blame] | 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); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 137 | } |
| 138 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 139 | // static |
| 140 | void SimpleResourceLoader::DetermineLocalesDirectory(FilePath* locales_path) { |
| 141 | DCHECK(locales_path); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 142 | |
| 143 | FilePath module_path; |
| 144 | PathService::Get(base::DIR_MODULE, &module_path); |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 145 | *locales_path = module_path.Append(kLocalesDirName); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 146 | |
| 147 | // We may be residing in the "locales" directory's parent, or we might be |
| 148 | // in a sibling directory. Move up one and look for Locales again in the |
| 149 | // latter case. |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 150 | if (!file_util::DirectoryExists(*locales_path)) { |
| 151 | *locales_path = module_path.DirName(); |
| 152 | *locales_path = locales_path->Append(kLocalesDirName); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 153 | } |
| 154 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 155 | // Don't make a second check to see if the dir is in the parent. We'll notice |
| 156 | // and log that in LoadLocaleDll when we actually try loading DLLs. |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 157 | } |
| 158 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 159 | // static |
| 160 | bool SimpleResourceLoader::IsValidLanguageTag( |
| 161 | const std::wstring& language_tag) { |
| 162 | // "[a-zA-Z]+(-[a-zA-Z0-9]+)*" is a simplification, but better than nothing. |
| 163 | // Rather than pick up the weight of a regex processor, just search for a |
| 164 | // character that isn't in the above set. This will at least weed out |
| 165 | // attempts at "../../EvilBinary". |
| 166 | return language_tag.end() == std::find_if(language_tag.begin(), |
| 167 | language_tag.end(), |
| 168 | &IsInvalidTagCharacter); |
| 169 | } |
| 170 | |
| 171 | // static |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 172 | bool SimpleResourceLoader::LoadLocalePack( |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 173 | const std::vector<std::wstring>& language_tags, |
| 174 | const FilePath& locales_path, |
| 175 | HMODULE* dll_handle, |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 176 | ui::DataPack** data_pack, |
| 177 | std::wstring* language) { |
| 178 | DCHECK(language); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 179 | |
| 180 | // The dll should only have resources, not executable code. |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 181 | const DWORD load_flags = |
| 182 | (base::win::GetVersion() >= base::win::VERSION_VISTA ? |
| 183 | LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE : |
| 184 | DONT_RESOLVE_DLL_REFERENCES); |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 185 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 186 | const std::wstring dll_suffix(L".dll"); |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 187 | const std::wstring pack_suffix(L".pak"); |
| 188 | |
| 189 | bool found_pack = false; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 190 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 191 | for (std::vector<std::wstring>::const_iterator scan = language_tags.begin(), |
| 192 | end = language_tags.end(); |
| 193 | scan != end; |
| 194 | ++scan) { |
| 195 | if (!IsValidLanguageTag(*scan)) { |
| 196 | LOG(WARNING) << "Invalid language tag supplied while locating resources:" |
| 197 | " \"" << *scan << "\""; |
| 198 | continue; |
| 199 | } |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 200 | |
| 201 | // Attempt to load both the resource pack and the dll. We return success |
| 202 | // only we load both. |
| 203 | FilePath resource_pack_path = locales_path.Append(*scan + pack_suffix); |
| 204 | FilePath dll_path = locales_path.Append(*scan + dll_suffix); |
| 205 | |
| 206 | if (file_util::PathExists(resource_pack_path) && |
| 207 | file_util::PathExists(dll_path)) { |
[email protected] | 63942f2 | 2012-05-01 06:11:52 | [diff] [blame] | 208 | scoped_ptr<ui::DataPack> cur_data_pack( |
[email protected] | c49201a | 2012-05-24 11:04:57 | [diff] [blame] | 209 | new ui::DataPack(ui::SCALE_FACTOR_100P)); |
[email protected] | 6c26d9c | 2012-07-10 19:18:04 | [diff] [blame] | 210 | if (!cur_data_pack->LoadFromPath(resource_pack_path)) |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 211 | continue; |
[email protected] | ec015c8 | 2012-04-12 23:41:53 | [diff] [blame] | 212 | |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 213 | HMODULE locale_dll_handle = LoadLibraryEx(dll_path.value().c_str(), NULL, |
| 214 | load_flags); |
| 215 | if (locale_dll_handle) { |
| 216 | *dll_handle = locale_dll_handle; |
| 217 | *language = dll_path.BaseName().RemoveExtension().value(); |
[email protected] | ec015c8 | 2012-04-12 23:41:53 | [diff] [blame] | 218 | *data_pack = cur_data_pack.release(); |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 219 | found_pack = true; |
| 220 | break; |
| 221 | } else { |
| 222 | *data_pack = NULL; |
| 223 | } |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 224 | } |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 225 | } |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 226 | DCHECK(found_pack || file_util::DirectoryExists(locales_path)) |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 227 | << "Could not locate locales DLL directory."; |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 228 | return found_pack; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 229 | } |
| 230 | |
| 231 | std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) { |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 232 | if (!data_pack_) { |
[email protected] | e14fff7f | 2010-09-24 14:29:44 | [diff] [blame] | 233 | DLOG(ERROR) << "locale resources are not loaded"; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 234 | return std::wstring(); |
| 235 | } |
| 236 | |
| 237 | DCHECK(IS_INTRESOURCE(message_id)); |
| 238 | |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 239 | base::StringPiece data; |
| 240 | if (!data_pack_->GetStringPiece(message_id, &data)) { |
| 241 | DLOG(ERROR) << "Unable to find string for resource id:" << message_id; |
| 242 | return std::wstring(); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 243 | } |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 244 | |
[email protected] | 775870c | 2011-09-19 22:26:51 | [diff] [blame] | 245 | // Data pack encodes strings as either UTF8 or UTF16. |
| 246 | string16 msg; |
| 247 | if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF16) { |
| 248 | msg = string16(reinterpret_cast<const char16*>(data.data()), |
| 249 | data.length() / 2); |
| 250 | } else if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF8) { |
| 251 | msg = UTF8ToUTF16(data); |
| 252 | } |
[email protected] | 3a78fbd | 2011-08-26 22:26:13 | [diff] [blame] | 253 | return msg; |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 254 | } |
| 255 | |
| 256 | // static |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 257 | std::wstring SimpleResourceLoader::GetLanguage() { |
[email protected] | 8e8bb6d | 2010-12-13 08:18:55 | [diff] [blame] | 258 | return SimpleResourceLoader::GetInstance()->language_; |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 259 | } |
| 260 | |
| 261 | // static |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 262 | std::wstring SimpleResourceLoader::Get(int message_id) { |
[email protected] | 8e8bb6d | 2010-12-13 08:18:55 | [diff] [blame] | 263 | SimpleResourceLoader* loader = SimpleResourceLoader::GetInstance(); |
[email protected] | 36a26ed | 2010-03-24 03:09:05 | [diff] [blame] | 264 | return loader->GetLocalizedResource(message_id); |
| 265 | } |
[email protected] | 3c6f8e1 | 2010-03-24 21:58:21 | [diff] [blame] | 266 | |
[email protected] | 6ae3d49 | 2010-10-20 14:01:21 | [diff] [blame] | 267 | HMODULE SimpleResourceLoader::GetResourceModuleHandle() { |
[email protected] | 3c6f8e1 | 2010-03-24 21:58:21 | [diff] [blame] | 268 | return locale_dll_handle_; |
| 269 | } |