OLD | NEW |
| (Empty) |
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(base::ASCIIToWide( | |
136 base::i18n::GetConfiguredLocale()), | |
137 language_tags); | |
138 } | |
139 | |
140 // static | |
141 void SimpleResourceLoader::DetermineLocalesDirectory( | |
142 base::FilePath* locales_path) { | |
143 DCHECK(locales_path); | |
144 | |
145 base::FilePath module_path; | |
146 PathService::Get(base::DIR_MODULE, &module_path); | |
147 *locales_path = module_path.Append(kLocalesDirName); | |
148 | |
149 // We may be residing in the "locales" directory's parent, or we might be | |
150 // in a sibling directory. Move up one and look for Locales again in the | |
151 // latter case. | |
152 if (!base::DirectoryExists(*locales_path)) { | |
153 *locales_path = module_path.DirName(); | |
154 *locales_path = locales_path->Append(kLocalesDirName); | |
155 } | |
156 | |
157 // Don't make a second check to see if the dir is in the parent. We'll notice | |
158 // and log that in LoadLocaleDll when we actually try loading DLLs. | |
159 } | |
160 | |
161 // static | |
162 bool SimpleResourceLoader::IsValidLanguageTag( | |
163 const std::wstring& language_tag) { | |
164 // "[a-zA-Z]+(-[a-zA-Z0-9]+)*" is a simplification, but better than nothing. | |
165 // Rather than pick up the weight of a regex processor, just search for a | |
166 // character that isn't in the above set. This will at least weed out | |
167 // attempts at "../../EvilBinary". | |
168 return language_tag.end() == std::find_if(language_tag.begin(), | |
169 language_tag.end(), | |
170 &IsInvalidTagCharacter); | |
171 } | |
172 | |
173 // static | |
174 bool SimpleResourceLoader::LoadLocalePack( | |
175 const std::vector<std::wstring>& language_tags, | |
176 const base::FilePath& locales_path, | |
177 HMODULE* dll_handle, | |
178 ui::DataPack** data_pack, | |
179 std::wstring* language) { | |
180 DCHECK(language); | |
181 | |
182 // The dll should only have resources, not executable code. | |
183 const DWORD load_flags = | |
184 (base::win::GetVersion() >= base::win::VERSION_VISTA ? | |
185 LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE : | |
186 DONT_RESOLVE_DLL_REFERENCES); | |
187 | |
188 const std::wstring dll_suffix(L".dll"); | |
189 const std::wstring pack_suffix(L".pak"); | |
190 | |
191 bool found_pack = false; | |
192 | |
193 for (std::vector<std::wstring>::const_iterator scan = language_tags.begin(), | |
194 end = language_tags.end(); | |
195 scan != end; | |
196 ++scan) { | |
197 if (!IsValidLanguageTag(*scan)) { | |
198 LOG(WARNING) << "Invalid language tag supplied while locating resources:" | |
199 " \"" << *scan << "\""; | |
200 continue; | |
201 } | |
202 | |
203 // Attempt to load both the resource pack and the dll. We return success | |
204 // only we load both. | |
205 base::FilePath resource_pack_path = | |
206 locales_path.Append(*scan + pack_suffix); | |
207 base::FilePath dll_path = locales_path.Append(*scan + dll_suffix); | |
208 | |
209 if (base::PathExists(resource_pack_path) && | |
210 base::PathExists(dll_path)) { | |
211 scoped_ptr<ui::DataPack> cur_data_pack( | |
212 new ui::DataPack(ui::SCALE_FACTOR_100P)); | |
213 if (!cur_data_pack->LoadFromPath(resource_pack_path)) | |
214 continue; | |
215 | |
216 HMODULE locale_dll_handle = LoadLibraryEx(dll_path.value().c_str(), NULL, | |
217 load_flags); | |
218 if (locale_dll_handle) { | |
219 *dll_handle = locale_dll_handle; | |
220 *language = dll_path.BaseName().RemoveExtension().value(); | |
221 *data_pack = cur_data_pack.release(); | |
222 found_pack = true; | |
223 break; | |
224 } else { | |
225 *data_pack = NULL; | |
226 } | |
227 } | |
228 } | |
229 DCHECK(found_pack || base::DirectoryExists(locales_path)) | |
230 << "Could not locate locales DLL directory."; | |
231 return found_pack; | |
232 } | |
233 | |
234 std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) { | |
235 if (!data_pack_) { | |
236 DLOG(ERROR) << "locale resources are not loaded"; | |
237 return std::wstring(); | |
238 } | |
239 | |
240 DCHECK(IS_INTRESOURCE(message_id)); | |
241 | |
242 base::StringPiece data; | |
243 if (!data_pack_->GetStringPiece(message_id, &data)) { | |
244 DLOG(ERROR) << "Unable to find string for resource id:" << message_id; | |
245 return std::wstring(); | |
246 } | |
247 | |
248 // Data pack encodes strings as either UTF8 or UTF16. | |
249 base::string16 msg; | |
250 if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF16) { | |
251 msg = base::string16(reinterpret_cast<const base::char16*>(data.data()), | |
252 data.length() / 2); | |
253 } else if (data_pack_->GetTextEncodingType() == ui::DataPack::UTF8) { | |
254 msg = base::UTF8ToUTF16(data); | |
255 } | |
256 return msg; | |
257 } | |
258 | |
259 // static | |
260 std::wstring SimpleResourceLoader::GetLanguage() { | |
261 return SimpleResourceLoader::GetInstance()->language_; | |
262 } | |
263 | |
264 // static | |
265 std::wstring SimpleResourceLoader::Get(int message_id) { | |
266 SimpleResourceLoader* loader = SimpleResourceLoader::GetInstance(); | |
267 return loader->GetLocalizedResource(message_id); | |
268 } | |
269 | |
270 HMODULE SimpleResourceLoader::GetResourceModuleHandle() { | |
271 return locale_dll_handle_; | |
272 } | |
OLD | NEW |