blob: 13d42f4223dd4e218fda1fdc3b6cca7e6ecf00e0 [file] [log] [blame]
[email protected]ec015c82012-04-12 23:41:531// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]36a26ed2010-03-24 03:09:052// 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]36a26ed2010-03-24 03:09:058
[email protected]337ca072010-11-16 15:28:459#include <algorithm>
10
[email protected]36a26ed2010-03-24 03:09:0511#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]3b63f8f42011-03-28 01:54:1516#include "base/memory/singleton.h"
[email protected]36a26ed2010-03-24 03:09:0517#include "base/string_util.h"
[email protected]be1ce6a72010-08-03 14:35:2218#include "base/utf_string_conversions.h"
[email protected]337ca072010-11-16 15:28:4519#include "base/win/i18n.h"
[email protected]935aa542010-10-15 01:59:1520#include "base/win/windows_version.h"
[email protected]6ae3d492010-10-20 14:01:2121#include "chrome_frame/policy_settings.h"
[email protected]3a78fbd2011-08-26 22:26:1322#include "ui/base/resource/data_pack.h"
[email protected]6ae3d492010-10-20 14:01:2123
24namespace {
25
[email protected]36a26ed2010-03-24 03:09:0526const wchar_t kLocalesDirName[] = L"Locales";
27
[email protected]6ae3d492010-10-20 14:01:2128bool IsInvalidTagCharacter(wchar_t tag_character) {
29 return !(L'-' == tag_character ||
30 IsAsciiDigit(tag_character) ||
31 IsAsciiAlpha(tag_character));
32}
[email protected]36a26ed2010-03-24 03:09:0533
[email protected]6ae3d492010-10-20 14:01:2134// A helper function object that performs a lower-case ASCII comparison between
35// two strings.
36class 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.
52bool 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]337ca072010-11-16 15:28:4564// Returns true if the collection is modified.
65bool 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]6ae3d492010-10-20 14:01:2183} // namespace
84
85SimpleResourceLoader::SimpleResourceLoader()
[email protected]3a78fbd2011-08-26 22:26:1386 : data_pack_(NULL),
87 locale_dll_handle_(NULL) {
[email protected]36a26ed2010-03-24 03:09:0588 // Find and load the resource DLL.
[email protected]6ae3d492010-10-20 14:01:2189 std::vector<std::wstring> language_tags;
90
91 // First, try the locale dictated by policy and its fallback.
[email protected]687b9602010-12-08 10:43:0892 PushBackWithFallbackIfAbsent(
93 PolicySettings::GetInstance()->ApplicationLocale(),
94 &language_tags);
[email protected]6ae3d492010-10-20 14:01:2195
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]36a26ed2010-03-24 03:09:05104
[email protected]6ae3d492010-10-20 14:01:21105 DetermineLocalesDirectory(&locales_path);
[email protected]3a78fbd2011-08-26 22:26:13106 if (!LoadLocalePack(language_tags, locales_path, &locale_dll_handle_,
107 &data_pack_, &language_)) {
[email protected]6ae3d492010-10-20 14:01:21108 NOTREACHED() << "Failed loading any resource dll (even \"en-US\").";
[email protected]36a26ed2010-03-24 03:09:05109 }
110}
111
[email protected]6ae3d492010-10-20 14:01:21112SimpleResourceLoader::~SimpleResourceLoader() {
[email protected]3a78fbd2011-08-26 22:26:13113 delete data_pack_;
[email protected]6ae3d492010-10-20 14:01:21114}
[email protected]36a26ed2010-03-24 03:09:05115
[email protected]6ae3d492010-10-20 14:01:21116// static
[email protected]8e8bb6d2010-12-13 08:18:55117SimpleResourceLoader* SimpleResourceLoader::GetInstance() {
[email protected]687b9602010-12-08 10:43:08118 return Singleton<SimpleResourceLoader>::get();
119}
120
121// static
[email protected]6ae3d492010-10-20 14:01:21122void SimpleResourceLoader::GetPreferredLanguages(
123 std::vector<std::wstring>* language_tags) {
[email protected]337ca072010-11-16 15:28:45124 DCHECK(language_tags);
[email protected]6ae3d492010-10-20 14:01:21125 // The full set of preferred languages and their fallbacks are given priority.
[email protected]337ca072010-11-16 15:28:45126 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]6ae3d492010-10-20 14:01:21131 }
132 }
[email protected]337ca072010-11-16 15:28:45133 // 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]36a26ed2010-03-24 03:09:05137}
138
[email protected]6ae3d492010-10-20 14:01:21139// static
140void SimpleResourceLoader::DetermineLocalesDirectory(FilePath* locales_path) {
141 DCHECK(locales_path);
[email protected]36a26ed2010-03-24 03:09:05142
143 FilePath module_path;
144 PathService::Get(base::DIR_MODULE, &module_path);
[email protected]6ae3d492010-10-20 14:01:21145 *locales_path = module_path.Append(kLocalesDirName);
[email protected]36a26ed2010-03-24 03:09:05146
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]6ae3d492010-10-20 14:01:21150 if (!file_util::DirectoryExists(*locales_path)) {
151 *locales_path = module_path.DirName();
152 *locales_path = locales_path->Append(kLocalesDirName);
[email protected]36a26ed2010-03-24 03:09:05153 }
154
[email protected]6ae3d492010-10-20 14:01:21155 // 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]36a26ed2010-03-24 03:09:05157}
158
[email protected]6ae3d492010-10-20 14:01:21159// static
160bool 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]3a78fbd2011-08-26 22:26:13172bool SimpleResourceLoader::LoadLocalePack(
[email protected]6ae3d492010-10-20 14:01:21173 const std::vector<std::wstring>& language_tags,
174 const FilePath& locales_path,
175 HMODULE* dll_handle,
[email protected]3a78fbd2011-08-26 22:26:13176 ui::DataPack** data_pack,
177 std::wstring* language) {
178 DCHECK(language);
[email protected]36a26ed2010-03-24 03:09:05179
180 // The dll should only have resources, not executable code.
[email protected]6ae3d492010-10-20 14:01:21181 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]3a78fbd2011-08-26 22:26:13185
[email protected]6ae3d492010-10-20 14:01:21186 const std::wstring dll_suffix(L".dll");
[email protected]3a78fbd2011-08-26 22:26:13187 const std::wstring pack_suffix(L".pak");
188
189 bool found_pack = false;
[email protected]36a26ed2010-03-24 03:09:05190
[email protected]6ae3d492010-10-20 14:01:21191 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]3a78fbd2011-08-26 22:26:13200
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]63942f22012-05-01 06:11:52208 scoped_ptr<ui::DataPack> cur_data_pack(
[email protected]c49201a2012-05-24 11:04:57209 new ui::DataPack(ui::SCALE_FACTOR_100P));
[email protected]6c26d9c2012-07-10 19:18:04210 if (!cur_data_pack->LoadFromPath(resource_pack_path))
[email protected]3a78fbd2011-08-26 22:26:13211 continue;
[email protected]ec015c82012-04-12 23:41:53212
[email protected]3a78fbd2011-08-26 22:26:13213 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]ec015c82012-04-12 23:41:53218 *data_pack = cur_data_pack.release();
[email protected]3a78fbd2011-08-26 22:26:13219 found_pack = true;
220 break;
221 } else {
222 *data_pack = NULL;
223 }
[email protected]6ae3d492010-10-20 14:01:21224 }
[email protected]6ae3d492010-10-20 14:01:21225 }
[email protected]3a78fbd2011-08-26 22:26:13226 DCHECK(found_pack || file_util::DirectoryExists(locales_path))
[email protected]6ae3d492010-10-20 14:01:21227 << "Could not locate locales DLL directory.";
[email protected]3a78fbd2011-08-26 22:26:13228 return found_pack;
[email protected]36a26ed2010-03-24 03:09:05229}
230
231std::wstring SimpleResourceLoader::GetLocalizedResource(int message_id) {
[email protected]3a78fbd2011-08-26 22:26:13232 if (!data_pack_) {
[email protected]e14fff7f2010-09-24 14:29:44233 DLOG(ERROR) << "locale resources are not loaded";
[email protected]36a26ed2010-03-24 03:09:05234 return std::wstring();
235 }
236
237 DCHECK(IS_INTRESOURCE(message_id));
238
[email protected]3a78fbd2011-08-26 22:26:13239 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]36a26ed2010-03-24 03:09:05243 }
[email protected]3a78fbd2011-08-26 22:26:13244
[email protected]775870c2011-09-19 22:26:51245 // 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]3a78fbd2011-08-26 22:26:13253 return msg;
[email protected]36a26ed2010-03-24 03:09:05254}
255
256// static
[email protected]6ae3d492010-10-20 14:01:21257std::wstring SimpleResourceLoader::GetLanguage() {
[email protected]8e8bb6d2010-12-13 08:18:55258 return SimpleResourceLoader::GetInstance()->language_;
[email protected]6ae3d492010-10-20 14:01:21259}
260
261// static
[email protected]36a26ed2010-03-24 03:09:05262std::wstring SimpleResourceLoader::Get(int message_id) {
[email protected]8e8bb6d2010-12-13 08:18:55263 SimpleResourceLoader* loader = SimpleResourceLoader::GetInstance();
[email protected]36a26ed2010-03-24 03:09:05264 return loader->GetLocalizedResource(message_id);
265}
[email protected]3c6f8e12010-03-24 21:58:21266
[email protected]6ae3d492010-10-20 14:01:21267HMODULE SimpleResourceLoader::GetResourceModuleHandle() {
[email protected]3c6f8e12010-03-24 21:58:21268 return locale_dll_handle_;
269}