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.
5 #include "chrome_frame/simple_resource_loader.h"
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"
16 #include "base/memory/singleton.h"
17 #include "base/string_util.h"
18 #include "base/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"
26 const wchar_t kLocalesDirName
[] = L
"Locales";
28 bool IsInvalidTagCharacter(wchar_t tag_character
) {
29 return !(L
'-' == tag_character
||
30 IsAsciiDigit(tag_character
) ||
31 IsAsciiAlpha(tag_character
));
34 // A helper function object that performs a lower-case ASCII comparison between
36 class CompareInsensitiveASCII
37 : public std::unary_function
<const std::wstring
&, bool> {
39 explicit CompareInsensitiveASCII(const std::wstring
& value
)
40 : value_lowered_(WideToASCII(value
)) {
41 StringToLowerASCII(&value_lowered_
);
43 bool operator()(const std::wstring
& comparand
) {
44 return LowerCaseEqualsASCII(comparand
, value_lowered_
.c_str());
48 std::string value_lowered_
;
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
);
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;
70 if (!language
.empty()) {
71 // Try adding the language itself.
72 modified
= PushBackIfAbsent(language
, collection
);
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
);
85 SimpleResourceLoader::SimpleResourceLoader()
87 locale_dll_handle_(NULL
) {
88 // Find and load the resource DLL.
89 std::vector
<std::wstring
> language_tags
;
91 // First, try the locale dictated by policy and its fallback.
92 PushBackWithFallbackIfAbsent(
93 PolicySettings::GetInstance()->ApplicationLocale(),
96 // Next, try the thread, process, user, system languages.
97 GetPreferredLanguages(&language_tags
);
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");
103 FilePath locales_path
;
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\").";
112 SimpleResourceLoader::~SimpleResourceLoader() {
117 SimpleResourceLoader
* SimpleResourceLoader::GetInstance() {
118 return Singleton
<SimpleResourceLoader
>::get();
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
);
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()),
140 void SimpleResourceLoader::DetermineLocalesDirectory(FilePath
* locales_path
) {
141 DCHECK(locales_path
);
143 FilePath module_path
;
144 PathService::Get(base::DIR_MODULE
, &module_path
);
145 *locales_path
= module_path
.Append(kLocalesDirName
);
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
150 if (!file_util::DirectoryExists(*locales_path
)) {
151 *locales_path
= module_path
.DirName();
152 *locales_path
= locales_path
->Append(kLocalesDirName
);
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.
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(),
168 &IsInvalidTagCharacter
);
172 bool SimpleResourceLoader::LoadLocalePack(
173 const std::vector
<std::wstring
>& language_tags
,
174 const FilePath
& locales_path
,
176 ui::DataPack
** data_pack
,
177 std::wstring
* language
) {
180 // The dll should only have resources, not executable code.
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
);
186 const std::wstring
dll_suffix(L
".dll");
187 const std::wstring
pack_suffix(L
".pak");
189 bool found_pack
= false;
191 for (std::vector
<std::wstring
>::const_iterator scan
= language_tags
.begin(),
192 end
= language_tags
.end();
195 if (!IsValidLanguageTag(*scan
)) {
196 LOG(WARNING
) << "Invalid language tag supplied while locating resources:"
197 " \"" << *scan
<< "\"";
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
);
206 if (file_util::PathExists(resource_pack_path
) &&
207 file_util::PathExists(dll_path
)) {
208 scoped_ptr
<ui::DataPack
> cur_data_pack(
209 new ui::DataPack(ui::SCALE_FACTOR_100P
));
210 if (!cur_data_pack
->LoadFromPath(resource_pack_path
))
213 HMODULE locale_dll_handle
= LoadLibraryEx(dll_path
.value().c_str(), NULL
,
215 if (locale_dll_handle
) {
216 *dll_handle
= locale_dll_handle
;
217 *language
= dll_path
.BaseName().RemoveExtension().value();
218 *data_pack
= cur_data_pack
.release();
226 DCHECK(found_pack
|| file_util::DirectoryExists(locales_path
))
227 << "Could not locate locales DLL directory.";
231 std::wstring
SimpleResourceLoader::GetLocalizedResource(int message_id
) {
233 DLOG(ERROR
) << "locale resources are not loaded";
234 return std::wstring();
237 DCHECK(IS_INTRESOURCE(message_id
));
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();
245 // Data pack encodes strings as either UTF8 or UTF16.
247 if (data_pack_
->GetTextEncodingType() == ui::DataPack::UTF16
) {
248 msg
= string16(reinterpret_cast<const char16
*>(data
.data()),
250 } else if (data_pack_
->GetTextEncodingType() == ui::DataPack::UTF8
) {
251 msg
= UTF8ToUTF16(data
);
257 std::wstring
SimpleResourceLoader::GetLanguage() {
258 return SimpleResourceLoader::GetInstance()->language_
;
262 std::wstring
SimpleResourceLoader::Get(int message_id
) {
263 SimpleResourceLoader
* loader
= SimpleResourceLoader::GetInstance();
264 return loader
->GetLocalizedResource(message_id
);
267 HMODULE
SimpleResourceLoader::GetResourceModuleHandle() {
268 return locale_dll_handle_
;