OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/extensions/extension_l10n_util.h" | 5 #include "extensions/common/extension_l10n_util.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <set> | 8 #include <set> |
9 #include <string> | 9 #include <string> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/file_util.h" | 12 #include "base/file_util.h" |
13 #include "base/files/file_enumerator.h" | 13 #include "base/files/file_enumerator.h" |
14 #include "base/json/json_file_value_serializer.h" | 14 #include "base/json/json_file_value_serializer.h" |
15 #include "base/logging.h" | 15 #include "base/logging.h" |
16 #include "base/memory/linked_ptr.h" | 16 #include "base/memory/linked_ptr.h" |
17 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
18 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
19 #include "base/values.h" | 19 #include "base/values.h" |
20 #include "chrome/common/extensions/extension_file_util.h" | |
21 #include "chrome/common/extensions/message_bundle.h" | |
22 #include "chrome/common/url_constants.h" | |
23 #include "extensions/common/constants.h" | 20 #include "extensions/common/constants.h" |
24 #include "extensions/common/error_utils.h" | 21 #include "extensions/common/error_utils.h" |
| 22 #include "extensions/common/file_util.h" |
25 #include "extensions/common/manifest_constants.h" | 23 #include "extensions/common/manifest_constants.h" |
| 24 #include "extensions/common/message_bundle.h" |
26 #include "third_party/icu/source/common/unicode/uloc.h" | 25 #include "third_party/icu/source/common/unicode/uloc.h" |
27 #include "ui/base/l10n/l10n_util.h" | 26 #include "ui/base/l10n/l10n_util.h" |
28 | 27 |
29 namespace errors = extensions::manifest_errors; | 28 namespace errors = extensions::manifest_errors; |
30 namespace keys = extensions::manifest_keys; | 29 namespace keys = extensions::manifest_keys; |
31 | 30 |
32 namespace { | 31 namespace { |
33 | 32 |
34 // Loads contents of the messages file for given locale. If file is not found, | 33 // Loads contents of the messages file for given locale. If file is not found, |
35 // or there was parsing error we return NULL and set |error|. | 34 // or there was parsing error we return NULL and set |error|. |
36 // Caller owns the returned object. | 35 // Caller owns the returned object. |
37 base::DictionaryValue* LoadMessageFile(const base::FilePath& locale_path, | 36 base::DictionaryValue* LoadMessageFile(const base::FilePath& locale_path, |
38 const std::string& locale, | 37 const std::string& locale, |
39 std::string* error) { | 38 std::string* error) { |
40 base::FilePath file = locale_path.AppendASCII(locale) | 39 base::FilePath file = |
41 .Append(extensions::kMessagesFilename); | 40 locale_path.AppendASCII(locale).Append(extensions::kMessagesFilename); |
42 JSONFileValueSerializer messages_serializer(file); | 41 JSONFileValueSerializer messages_serializer(file); |
43 base::Value* dictionary = messages_serializer.Deserialize(NULL, error); | 42 base::Value* dictionary = messages_serializer.Deserialize(NULL, error); |
44 if (!dictionary) { | 43 if (!dictionary) { |
45 if (error->empty()) { | 44 if (error->empty()) { |
46 // JSONFileValueSerializer just returns NULL if file cannot be found. It | 45 // JSONFileValueSerializer just returns NULL if file cannot be found. It |
47 // doesn't set the error, so we have to do it. | 46 // doesn't set the error, so we have to do it. |
48 *error = base::StringPrintf("Catalog file is missing for locale %s.", | 47 *error = base::StringPrintf("Catalog file is missing for locale %s.", |
49 locale.c_str()); | 48 locale.c_str()); |
50 } else { | 49 } else { |
51 *error = extensions::ErrorUtils::FormatErrorMessage( | 50 *error = extensions::ErrorUtils::FormatErrorMessage( |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
172 | 171 |
173 base::ListValue* file_handlers = NULL; | 172 base::ListValue* file_handlers = NULL; |
174 if (manifest->GetList(keys::kFileBrowserHandlers, &file_handlers)) { | 173 if (manifest->GetList(keys::kFileBrowserHandlers, &file_handlers)) { |
175 key.assign(keys::kFileBrowserHandlers); | 174 key.assign(keys::kFileBrowserHandlers); |
176 for (size_t i = 0; i < file_handlers->GetSize(); i++) { | 175 for (size_t i = 0; i < file_handlers->GetSize(); i++) { |
177 base::DictionaryValue* handler = NULL; | 176 base::DictionaryValue* handler = NULL; |
178 if (!file_handlers->GetDictionary(i, &handler)) { | 177 if (!file_handlers->GetDictionary(i, &handler)) { |
179 *error = errors::kInvalidFileBrowserHandler; | 178 *error = errors::kInvalidFileBrowserHandler; |
180 return false; | 179 return false; |
181 } | 180 } |
182 if (!LocalizeManifestValue(keys::kPageActionDefaultTitle, messages, | 181 if (!LocalizeManifestValue( |
183 handler, error)) | 182 keys::kPageActionDefaultTitle, messages, handler, error)) |
184 return false; | 183 return false; |
185 } | 184 } |
186 } | 185 } |
187 | 186 |
188 base::ListValue* media_galleries_handlers = NULL; | 187 base::ListValue* media_galleries_handlers = NULL; |
189 if (manifest->GetList(keys::kMediaGalleriesHandlers, | 188 if (manifest->GetList(keys::kMediaGalleriesHandlers, |
190 &media_galleries_handlers)) { | 189 &media_galleries_handlers)) { |
191 key.assign(keys::kMediaGalleriesHandlers); | 190 key.assign(keys::kMediaGalleriesHandlers); |
192 for (size_t i = 0; i < media_galleries_handlers->GetSize(); i++) { | 191 for (size_t i = 0; i < media_galleries_handlers->GetSize(); i++) { |
193 base::DictionaryValue* handler = NULL; | 192 base::DictionaryValue* handler = NULL; |
194 if (!media_galleries_handlers->GetDictionary(i, &handler)) { | 193 if (!media_galleries_handlers->GetDictionary(i, &handler)) { |
195 *error = errors::kInvalidMediaGalleriesHandler; | 194 *error = errors::kInvalidMediaGalleriesHandler; |
196 return false; | 195 return false; |
197 } | 196 } |
198 if (!LocalizeManifestValue(keys::kPageActionDefaultTitle, messages, | 197 if (!LocalizeManifestValue( |
199 handler, error)) | 198 keys::kPageActionDefaultTitle, messages, handler, error)) |
200 return false; | 199 return false; |
201 } | 200 } |
202 } | 201 } |
203 | 202 |
204 // Initialize all input_components | 203 // Initialize all input_components |
205 base::ListValue* input_components = NULL; | 204 base::ListValue* input_components = NULL; |
206 if (manifest->GetList(keys::kInputComponents, &input_components)) { | 205 if (manifest->GetList(keys::kInputComponents, &input_components)) { |
207 for (size_t i = 0; i < input_components->GetSize(); ++i) { | 206 for (size_t i = 0; i < input_components->GetSize(); ++i) { |
208 base::DictionaryValue* module = NULL; | 207 base::DictionaryValue* module = NULL; |
209 if (!input_components->GetDictionary(i, &module)) { | 208 if (!input_components->GetDictionary(i, &module)) { |
(...skipping 14 matching lines...) Expand all Loading... |
224 // Initialize app.launch.web_url. | 223 // Initialize app.launch.web_url. |
225 if (!LocalizeManifestValue(keys::kLaunchWebURL, messages, manifest, error)) | 224 if (!LocalizeManifestValue(keys::kLaunchWebURL, messages, manifest, error)) |
226 return false; | 225 return false; |
227 | 226 |
228 // Initialize description of commmands. | 227 // Initialize description of commmands. |
229 base::DictionaryValue* commands_handler = NULL; | 228 base::DictionaryValue* commands_handler = NULL; |
230 if (manifest->GetDictionary(keys::kCommands, &commands_handler)) { | 229 if (manifest->GetDictionary(keys::kCommands, &commands_handler)) { |
231 for (base::DictionaryValue::Iterator iter(*commands_handler); | 230 for (base::DictionaryValue::Iterator iter(*commands_handler); |
232 !iter.IsAtEnd(); | 231 !iter.IsAtEnd(); |
233 iter.Advance()) { | 232 iter.Advance()) { |
234 key.assign(base::StringPrintf("commands.%s.description", | 233 key.assign( |
235 iter.key().c_str())); | 234 base::StringPrintf("commands.%s.description", iter.key().c_str())); |
236 if (!LocalizeManifestValue(key, messages, manifest, error)) | 235 if (!LocalizeManifestValue(key, messages, manifest, error)) |
237 return false; | 236 return false; |
238 } | 237 } |
239 } | 238 } |
240 | 239 |
241 // Initialize search_provider fields. | 240 // Initialize search_provider fields. |
242 base::DictionaryValue* search_provider = NULL; | 241 base::DictionaryValue* search_provider = NULL; |
243 if (manifest->GetDictionary(keys::kOverrideSearchProvider, | 242 if (manifest->GetDictionary(keys::kOverrideSearchProvider, |
244 &search_provider)) { | 243 &search_provider)) { |
245 for (base::DictionaryValue::Iterator iter(*search_provider); | 244 for (base::DictionaryValue::Iterator iter(*search_provider); |
246 !iter.IsAtEnd(); | 245 !iter.IsAtEnd(); |
247 iter.Advance()) { | 246 iter.Advance()) { |
248 key.assign(base::StringPrintf( | 247 key.assign(base::StringPrintf( |
249 "%s.%s", keys::kOverrideSearchProvider, iter.key().c_str())); | 248 "%s.%s", keys::kOverrideSearchProvider, iter.key().c_str())); |
250 bool success = (key == keys::kSettingsOverrideAlternateUrls) ? | 249 bool success = |
251 LocalizeManifestListValue(key, messages, manifest, error) : | 250 (key == keys::kSettingsOverrideAlternateUrls) |
252 LocalizeManifestValue(key, messages, manifest, error); | 251 ? LocalizeManifestListValue(key, messages, manifest, error) |
| 252 : LocalizeManifestValue(key, messages, manifest, error); |
253 if (!success) | 253 if (!success) |
254 return false; | 254 return false; |
255 } | 255 } |
256 } | 256 } |
257 | 257 |
258 // Initialize chrome_settings_overrides.homepage. | 258 // Initialize chrome_settings_overrides.homepage. |
259 if (!LocalizeManifestValue( | 259 if (!LocalizeManifestValue( |
260 keys::kOverrideHomepage, messages, manifest, error)) | 260 keys::kOverrideHomepage, messages, manifest, error)) |
261 return false; | 261 return false; |
262 | 262 |
263 // Initialize chrome_settings_overrides.startup_pages. | 263 // Initialize chrome_settings_overrides.startup_pages. |
264 if (!LocalizeManifestListValue( | 264 if (!LocalizeManifestListValue( |
265 keys::kOverrideStartupPage, messages, manifest, error)) | 265 keys::kOverrideStartupPage, messages, manifest, error)) |
266 return false; | 266 return false; |
267 | 267 |
268 // Add current locale key to the manifest, so we can overwrite prefs | 268 // Add current locale key to the manifest, so we can overwrite prefs |
269 // with new manifest when chrome locale changes. | 269 // with new manifest when chrome locale changes. |
270 manifest->SetString(keys::kCurrentLocale, CurrentLocaleOrDefault()); | 270 manifest->SetString(keys::kCurrentLocale, CurrentLocaleOrDefault()); |
271 return true; | 271 return true; |
272 } | 272 } |
273 | 273 |
274 bool LocalizeExtension(const base::FilePath& extension_path, | 274 bool LocalizeExtension(const base::FilePath& extension_path, |
275 base::DictionaryValue* manifest, | 275 base::DictionaryValue* manifest, |
276 std::string* error) { | 276 std::string* error) { |
277 DCHECK(manifest); | 277 DCHECK(manifest); |
278 | 278 |
279 std::string default_locale = GetDefaultLocaleFromManifest(*manifest, error); | 279 std::string default_locale = GetDefaultLocaleFromManifest(*manifest, error); |
280 | 280 |
281 scoped_ptr<extensions::MessageBundle> message_bundle( | 281 scoped_ptr<extensions::MessageBundle> message_bundle( |
282 extension_file_util::LoadMessageBundle( | 282 extensions::file_util::LoadMessageBundle( |
283 extension_path, default_locale, error)); | 283 extension_path, default_locale, error)); |
284 | 284 |
285 if (!message_bundle.get() && !error->empty()) | 285 if (!message_bundle.get() && !error->empty()) |
286 return false; | 286 return false; |
287 | 287 |
288 if (message_bundle.get() && | 288 if (message_bundle.get() && |
289 !LocalizeManifest(*message_bundle, manifest, error)) | 289 !LocalizeManifest(*message_bundle, manifest, error)) |
290 return false; | 290 return false; |
291 | 291 |
292 return true; | 292 return true; |
293 } | 293 } |
294 | 294 |
295 bool AddLocale(const std::set<std::string>& chrome_locales, | 295 bool AddLocale(const std::set<std::string>& chrome_locales, |
296 const base::FilePath& locale_folder, | 296 const base::FilePath& locale_folder, |
297 const std::string& locale_name, | 297 const std::string& locale_name, |
298 std::set<std::string>* valid_locales, | 298 std::set<std::string>* valid_locales, |
299 std::string* error) { | 299 std::string* error) { |
300 // Accept name that starts with a . but don't add it to the list of supported | 300 // Accept name that starts with a . but don't add it to the list of supported |
301 // locales. | 301 // locales. |
302 if (locale_name.find(".") == 0) | 302 if (locale_name.find(".") == 0) |
303 return true; | 303 return true; |
304 if (chrome_locales.find(locale_name) == chrome_locales.end()) { | 304 if (chrome_locales.find(locale_name) == chrome_locales.end()) { |
305 // Warn if there is an extension locale that's not in the Chrome list, | 305 // Warn if there is an extension locale that's not in the Chrome list, |
306 // but don't fail. | 306 // but don't fail. |
307 DLOG(WARNING) << base::StringPrintf("Supplied locale %s is not supported.", | 307 DLOG(WARNING) << base::StringPrintf("Supplied locale %s is not supported.", |
308 locale_name.c_str()); | 308 locale_name.c_str()); |
309 return true; | 309 return true; |
310 } | 310 } |
311 // Check if messages file is actually present (but don't check content). | 311 // Check if messages file is actually present (but don't check content). |
312 if (base::PathExists( | 312 if (base::PathExists(locale_folder.Append(extensions::kMessagesFilename))) { |
313 locale_folder.Append(extensions::kMessagesFilename))) { | |
314 valid_locales->insert(locale_name); | 313 valid_locales->insert(locale_name); |
315 } else { | 314 } else { |
316 *error = base::StringPrintf("Catalog file is missing for locale %s.", | 315 *error = base::StringPrintf("Catalog file is missing for locale %s.", |
317 locale_name.c_str()); | 316 locale_name.c_str()); |
318 return false; | 317 return false; |
319 } | 318 } |
320 | 319 |
321 return true; | 320 return true; |
322 } | 321 } |
323 | 322 |
(...skipping 26 matching lines...) Expand all Loading... |
350 all_fallback_locales->push_back(default_locale); | 349 all_fallback_locales->push_back(default_locale); |
351 } | 350 } |
352 | 351 |
353 bool GetValidLocales(const base::FilePath& locale_path, | 352 bool GetValidLocales(const base::FilePath& locale_path, |
354 std::set<std::string>* valid_locales, | 353 std::set<std::string>* valid_locales, |
355 std::string* error) { | 354 std::string* error) { |
356 std::set<std::string> chrome_locales; | 355 std::set<std::string> chrome_locales; |
357 GetAllLocales(&chrome_locales); | 356 GetAllLocales(&chrome_locales); |
358 | 357 |
359 // Enumerate all supplied locales in the extension. | 358 // Enumerate all supplied locales in the extension. |
360 base::FileEnumerator locales(locale_path, | 359 base::FileEnumerator locales( |
361 false, | 360 locale_path, false, base::FileEnumerator::DIRECTORIES); |
362 base::FileEnumerator::DIRECTORIES); | |
363 base::FilePath locale_folder; | 361 base::FilePath locale_folder; |
364 while (!(locale_folder = locales.Next()).empty()) { | 362 while (!(locale_folder = locales.Next()).empty()) { |
365 std::string locale_name = locale_folder.BaseName().MaybeAsASCII(); | 363 std::string locale_name = locale_folder.BaseName().MaybeAsASCII(); |
366 if (locale_name.empty()) { | 364 if (locale_name.empty()) { |
367 NOTREACHED(); | 365 NOTREACHED(); |
368 continue; // Not ASCII. | 366 continue; // Not ASCII. |
369 } | 367 } |
370 if (!AddLocale(chrome_locales, | 368 if (!AddLocale( |
371 locale_folder, | 369 chrome_locales, locale_folder, locale_name, valid_locales, error)) { |
372 locale_name, | |
373 valid_locales, | |
374 error)) { | |
375 return false; | 370 return false; |
376 } | 371 } |
377 } | 372 } |
378 | 373 |
379 if (valid_locales->empty()) { | 374 if (valid_locales->empty()) { |
380 *error = errors::kLocalesNoValidLocaleNamesListed; | 375 *error = errors::kLocalesNoValidLocaleNamesListed; |
381 return false; | 376 return false; |
382 } | 377 } |
383 | 378 |
384 return true; | 379 return true; |
385 } | 380 } |
386 | 381 |
387 extensions::MessageBundle* LoadMessageCatalogs( | 382 extensions::MessageBundle* LoadMessageCatalogs( |
388 const base::FilePath& locale_path, | 383 const base::FilePath& locale_path, |
389 const std::string& default_locale, | 384 const std::string& default_locale, |
390 const std::string& application_locale, | 385 const std::string& application_locale, |
391 const std::set<std::string>& valid_locales, | 386 const std::set<std::string>& valid_locales, |
392 std::string* error) { | 387 std::string* error) { |
393 std::vector<std::string> all_fallback_locales; | 388 std::vector<std::string> all_fallback_locales; |
394 GetAllFallbackLocales(application_locale, default_locale, | 389 GetAllFallbackLocales( |
395 &all_fallback_locales); | 390 application_locale, default_locale, &all_fallback_locales); |
396 | 391 |
397 std::vector<linked_ptr<base::DictionaryValue> > catalogs; | 392 std::vector<linked_ptr<base::DictionaryValue> > catalogs; |
398 for (size_t i = 0; i < all_fallback_locales.size(); ++i) { | 393 for (size_t i = 0; i < all_fallback_locales.size(); ++i) { |
399 // Skip all parent locales that are not supplied. | 394 // Skip all parent locales that are not supplied. |
400 if (valid_locales.find(all_fallback_locales[i]) == valid_locales.end()) | 395 if (valid_locales.find(all_fallback_locales[i]) == valid_locales.end()) |
401 continue; | 396 continue; |
402 linked_ptr<base::DictionaryValue> catalog( | 397 linked_ptr<base::DictionaryValue> catalog( |
403 LoadMessageFile(locale_path, all_fallback_locales[i], error)); | 398 LoadMessageFile(locale_path, all_fallback_locales[i], error)); |
404 if (!catalog.get()) { | 399 if (!catalog.get()) { |
405 // If locale is valid, but messages.json is corrupted or missing, return | 400 // If locale is valid, but messages.json is corrupted or missing, return |
406 // an error. | 401 // an error. |
407 return NULL; | 402 return NULL; |
408 } else { | 403 } else { |
409 catalogs.push_back(catalog); | 404 catalogs.push_back(catalog); |
410 } | 405 } |
411 } | 406 } |
412 | 407 |
413 return extensions::MessageBundle::Create(catalogs, error); | 408 return extensions::MessageBundle::Create(catalogs, error); |
414 } | 409 } |
415 | 410 |
416 bool ValidateExtensionLocales(const base::FilePath& extension_path, | 411 bool ValidateExtensionLocales(const base::FilePath& extension_path, |
417 const base::DictionaryValue* manifest, | 412 const base::DictionaryValue* manifest, |
418 std::string* error) { | 413 std::string* error) { |
419 std::string default_locale = GetDefaultLocaleFromManifest(*manifest, error); | 414 std::string default_locale = GetDefaultLocaleFromManifest(*manifest, error); |
420 | 415 |
421 if (default_locale.empty()) | 416 if (default_locale.empty()) |
422 return true; | 417 return true; |
423 | 418 |
424 base::FilePath locale_path = | 419 base::FilePath locale_path = extension_path.Append(extensions::kLocaleFolder); |
425 extension_path.Append(extensions::kLocaleFolder); | |
426 | 420 |
427 std::set<std::string> valid_locales; | 421 std::set<std::string> valid_locales; |
428 if (!GetValidLocales(locale_path, &valid_locales, error)) | 422 if (!GetValidLocales(locale_path, &valid_locales, error)) |
429 return false; | 423 return false; |
430 | 424 |
431 for (std::set<std::string>::const_iterator locale = valid_locales.begin(); | 425 for (std::set<std::string>::const_iterator locale = valid_locales.begin(); |
432 locale != valid_locales.end(); ++locale) { | 426 locale != valid_locales.end(); |
| 427 ++locale) { |
433 std::string locale_error; | 428 std::string locale_error; |
434 scoped_ptr<base::DictionaryValue> catalog( | 429 scoped_ptr<base::DictionaryValue> catalog( |
435 LoadMessageFile(locale_path, *locale, &locale_error)); | 430 LoadMessageFile(locale_path, *locale, &locale_error)); |
436 | 431 |
437 if (!locale_error.empty()) { | 432 if (!locale_error.empty()) { |
438 if (!error->empty()) | 433 if (!error->empty()) |
439 error->append(" "); | 434 error->append(" "); |
440 error->append(locale_error); | 435 error->append(locale_error); |
441 } | 436 } |
442 } | 437 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 ScopedLocaleForTest::ScopedLocaleForTest(const std::string& locale) | 469 ScopedLocaleForTest::ScopedLocaleForTest(const std::string& locale) |
475 : locale_(extension_l10n_util::CurrentLocaleOrDefault()) { | 470 : locale_(extension_l10n_util::CurrentLocaleOrDefault()) { |
476 extension_l10n_util::SetProcessLocale(locale); | 471 extension_l10n_util::SetProcessLocale(locale); |
477 } | 472 } |
478 | 473 |
479 ScopedLocaleForTest::~ScopedLocaleForTest() { | 474 ScopedLocaleForTest::~ScopedLocaleForTest() { |
480 extension_l10n_util::SetProcessLocale(locale_); | 475 extension_l10n_util::SetProcessLocale(locale_); |
481 } | 476 } |
482 | 477 |
483 } // namespace extension_l10n_util | 478 } // namespace extension_l10n_util |
OLD | NEW |