- add sources.
[platform/framework/web/crosswalk.git] / src / chrome_frame / simple_resource_loader.cc
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 }