diff options
author | cira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-16 21:11:06 +0000 |
---|---|---|
committer | cira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-12-16 21:11:06 +0000 |
commit | c6d474f8e04b08306349ea514d529b8f25ffd4b2 (patch) | |
tree | 756833e85392a8b487cf3ee3cefd0d81ab8c51fa /chrome/common | |
parent | 538a1ac2bc5ad7f171e15642edb4e8c1dd34348d (diff) | |
download | chromium_src-c6d474f8e04b08306349ea514d529b8f25ffd4b2.zip chromium_src-c6d474f8e04b08306349ea514d529b8f25ffd4b2.tar.gz chromium_src-c6d474f8e04b08306349ea514d529b8f25ffd4b2.tar.bz2 |
Localizing manifest for installed or unpacked extensions.
1. Use _MSG_ format for manifest fields instead of hard-coded names.
2. Localize manifest during installation procedure -> save localized version to prefs.
3. Add current_locale field to manifest so we can detect when chrome locale changes and re-localize manifest.
4. Adds prefs method to MigratePrefs&Write to disk.
5. General refactor of extension_file_util wrt l10n code.
6. Don't localize themes (we can revert this by adding default_locale and current_locale keys to allowed keys for theme section).
BUG=27360
Review URL: http://codereview.chromium.org/434015
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@34749 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r-- | chrome/common/extensions/extension.cc | 14 | ||||
-rw-r--r-- | chrome/common/extensions/extension.h | 23 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.cc | 1 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.h | 1 | ||||
-rw-r--r-- | chrome/common/extensions/extension_l10n_util.cc | 102 | ||||
-rw-r--r-- | chrome/common/extensions/extension_l10n_util.h | 20 | ||||
-rw-r--r-- | chrome/common/extensions/extension_l10n_util_unittest.cc | 185 | ||||
-rw-r--r-- | chrome/common/extensions/extension_message_bundle.cc | 4 | ||||
-rw-r--r-- | chrome/common/extensions/extension_message_bundle.h | 6 |
9 files changed, 334 insertions, 22 deletions
diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index 94d6de2..280ad98 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -683,27 +683,17 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_id, return false; } - // Initialize & localize name. + // Initialize name. if (!source.GetString(keys::kName, &name_)) { *error = errors::kInvalidName; return false; - } else if (message_bundle_.get()) { - std::string l10n_name = - message_bundle_->GetL10nMessage(ExtensionMessageBundle::kExtensionName); - if (!l10n_name.empty()) - name_ = l10n_name; } - // Initialize & localize description (if present). + // Initialize description (if present). if (source.HasKey(keys::kDescription)) { if (!source.GetString(keys::kDescription, &description_)) { *error = errors::kInvalidDescription; return false; - } else if (message_bundle_.get()) { - std::string l10n_description = message_bundle_->GetL10nMessage( - ExtensionMessageBundle::kExtensionDescription); - if (!l10n_description.empty()) - description_ = l10n_description; } } diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 331de16..d1b1aad 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -425,4 +425,27 @@ class Extension { typedef std::vector<Extension*> ExtensionList; +// Handy struct to pass core extension info around. +struct ExtensionInfo { + ExtensionInfo(const DictionaryValue* manifest, + const std::string& id, + const FilePath& path, + Extension::Location location) + : extension_id(id), + extension_path(path), + extension_location(location) { + if (manifest) + extension_manifest.reset( + static_cast<DictionaryValue*>(manifest->DeepCopy())); + } + + scoped_ptr<DictionaryValue> extension_manifest; + std::string extension_id; + FilePath extension_path; + Extension::Location extension_location; + + private: + DISALLOW_COPY_AND_ASSIGN(ExtensionInfo); +}; + #endif // CHROME_COMMON_EXTENSIONS_EXTENSION_H_ diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 65d5042..3deaf20 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -13,6 +13,7 @@ const wchar_t* kChromeURLOverrides = L"chrome_url_overrides"; const wchar_t* kContentScripts = L"content_scripts"; const wchar_t* kConvertedFromUserScript = L"converted_from_user_script"; const wchar_t* kCss = L"css"; +const wchar_t* kCurrentLocale = L"current_locale"; const wchar_t* kDefaultLocale = L"default_locale"; const wchar_t* kDescription = L"description"; const wchar_t* kIcons = L"icons"; diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index 720a775..a83c2d8 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -14,6 +14,7 @@ namespace extension_manifest_keys { extern const wchar_t* kContentScripts; extern const wchar_t* kConvertedFromUserScript; extern const wchar_t* kCss; + extern const wchar_t* kCurrentLocale; extern const wchar_t* kDefaultLocale; extern const wchar_t* kDescription; extern const wchar_t* kExcludeGlobs; diff --git a/chrome/common/extensions/extension_l10n_util.cc b/chrome/common/extensions/extension_l10n_util.cc index 4157083..9d0510e 100644 --- a/chrome/common/extensions/extension_l10n_util.cc +++ b/chrome/common/extensions/extension_l10n_util.cc @@ -13,6 +13,8 @@ #include "base/linked_ptr.h" #include "base/string_util.h" #include "base/values.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/extension_file_util.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_constants.h" #include "chrome/common/extensions/extension_message_bundle.h" @@ -43,6 +45,96 @@ std::string GetDefaultLocaleFromManifest(const DictionaryValue& manifest, return default_locale; } +bool ShouldRelocalizeManifest(const ExtensionInfo& info) { + DictionaryValue* manifest = info.extension_manifest.get(); + if (!manifest) + return false; + + if (!manifest->HasKey(keys::kDefaultLocale)) + return false; + + std::string manifest_current_locale; + manifest->GetString(keys::kCurrentLocale, &manifest_current_locale); + return manifest_current_locale != CurrentLocaleOrDefault(); +} + +// Localizes manifest value for a given key. +static bool LocalizeManifestValue(const std::wstring& key, + const ExtensionMessageBundle& messages, + DictionaryValue* manifest, + std::string* error) { + std::string result; + if (!manifest->GetString(key, &result)) + return true; + + if (!messages.ReplaceMessages(&result, error)) + return false; + + manifest->SetString(key, result); + return true; +} + +bool LocalizeManifest(const ExtensionMessageBundle& messages, + DictionaryValue* manifest, + std::string* error) { + // Don't localize themes. + if (manifest->HasKey(keys::kTheme)) + return true; + + // Initialize name. + std::string result; + if (!manifest->GetString(keys::kName, &result)) { + *error = errors::kInvalidName; + return false; + } + if (!LocalizeManifestValue(keys::kName, messages, manifest, error)) { + return false; + } + + // Initialize description. + if (!LocalizeManifestValue(keys::kDescription, messages, manifest, error)) + return false; + + // Initialize browser_action.default_title + std::wstring key(keys::kBrowserAction); + key.append(L"."); + key.append(keys::kPageActionDefaultTitle); + if (!LocalizeManifestValue(key, messages, manifest, error)) + return false; + + // Initialize page_action.default_title + key.assign(keys::kPageAction); + key.append(L"."); + key.append(keys::kPageActionDefaultTitle); + if (!LocalizeManifestValue(key, messages, manifest, error)) + return false; + + // Add current locale key to the manifest, so we can overwrite prefs + // with new manifest when chrome locale changes. + manifest->SetString(keys::kCurrentLocale, CurrentLocaleOrDefault()); + return true; +} + +bool LocalizeExtension(Extension* extension, + DictionaryValue* manifest, + std::string* error) { + ExtensionMessageBundle* message_bundle = + extension_file_util::LoadExtensionMessageBundle(extension->path(), + *manifest, + error); + if (!message_bundle && !error->empty()) + return false; + + // TODO(cira): remove ExtensionMessageBundle object from Extension class + // after we implement IPC that requests message bundles on demand. + extension->set_message_bundle(message_bundle); + + if (message_bundle && !LocalizeManifest(*message_bundle, manifest, error)) + return false; + + return true; +} + bool AddLocale(const std::set<std::string>& chrome_locales, const FilePath& locale_folder, const std::string& locale_name, @@ -78,6 +170,15 @@ std::string NormalizeLocale(const std::string& locale) { return normalized_locale; } +std::string CurrentLocaleOrDefault() { + std::string current_locale = + NormalizeLocale(g_browser_process->GetApplicationLocale()); + if (current_locale.empty()) + current_locale = "en"; + + return current_locale; +} + void GetParentLocales(const std::string& current_locale, std::vector<std::string>* parent_locales) { std::string locale(NormalizeLocale(current_locale)); @@ -145,7 +246,6 @@ bool GetValidLocales(const FilePath& locale_path, static DictionaryValue* LoadMessageFile(const FilePath& locale_path, const std::string& locale, std::string* error) { - std::string extension_locale = locale; FilePath file = locale_path.AppendASCII(extension_locale) .AppendASCII(Extension::kMessagesFilename); diff --git a/chrome/common/extensions/extension_l10n_util.h b/chrome/common/extensions/extension_l10n_util.h index 7be62912..60733d1 100644 --- a/chrome/common/extensions/extension_l10n_util.h +++ b/chrome/common/extensions/extension_l10n_util.h @@ -15,6 +15,7 @@ class DictionaryValue; class Extension; class ExtensionMessageBundle; class FilePath; +struct ExtensionInfo; namespace extension_l10n_util { @@ -28,6 +29,22 @@ void SetProcessLocale(const std::string& locale); std::string GetDefaultLocaleFromManifest(const DictionaryValue& manifest, std::string* error); +// Returns true iff the extension was localized, and the current locale +// doesn't match the locale written into info.extension_manifest. +bool ShouldRelocalizeManifest(const ExtensionInfo& info); + +// Localize extension name, description, browser_action and other fields +// in the manifest. +bool LocalizeManifest(const ExtensionMessageBundle& messages, + DictionaryValue* manifest, + std::string* error); + +// Load message catalogs, localize manifest and attach message bundle to the +// extension. +bool LocalizeExtension(Extension* extension, + DictionaryValue* manifest, + std::string* error); + // Adds locale_name to the extension if it's in chrome_locales, and // if messages file is present (we don't check content of messages file here). // Returns false if locale_name was not found in chrome_locales, and sets @@ -42,6 +59,9 @@ bool AddLocale(const std::set<std::string>& chrome_locales, // Converts all - into _, to be consistent with ICU and file system names. std::string NormalizeLocale(const std::string& locale); +// Returns normalized current locale, or default locale - en_US. +std::string CurrentLocaleOrDefault(); + // Produce a vector of parent locales for given locale. // It includes the current locale in the result. // sr_Cyrl_RS generates sr_Cyrl_RS, sr_Cyrl and sr. diff --git a/chrome/common/extensions/extension_l10n_util_unittest.cc b/chrome/common/extensions/extension_l10n_util_unittest.cc index da46b78..84e8a47 100644 --- a/chrome/common/extensions/extension_l10n_util_unittest.cc +++ b/chrome/common/extensions/extension_l10n_util_unittest.cc @@ -5,6 +5,7 @@ #include "app/l10n_util.h" #include "base/file_path.h" #include "base/file_util.h" +#include "base/linked_ptr.h" #include "base/path_service.h" #include "base/scoped_ptr.h" #include "base/scoped_temp_dir.h" @@ -15,6 +16,9 @@ #include "chrome/common/extensions/extension_l10n_util.h" #include "testing/gtest/include/gtest/gtest.h" +namespace errors = extension_manifest_errors; +namespace keys = extension_manifest_keys; + namespace { TEST(ExtensionL10nUtil, GetValidLocalesEmptyLocaleFolder) { @@ -193,4 +197,185 @@ TEST(ExtensionL10nUtil, GetParentLocales) { EXPECT_EQ("sr", locales[2]); } +// Caller owns the returned object. +ExtensionMessageBundle* CreateManifestBundle() { + linked_ptr<DictionaryValue> catalog(new DictionaryValue); + + DictionaryValue* name_tree = new DictionaryValue(); + name_tree->SetString(L"message", "name"); + catalog->Set(L"name", name_tree); + + DictionaryValue* description_tree = new DictionaryValue(); + description_tree->SetString(L"message", "description"); + catalog->Set(L"description", description_tree); + + DictionaryValue* action_title_tree = new DictionaryValue(); + action_title_tree->SetString(L"message", "action title"); + catalog->Set(L"title", action_title_tree); + + std::vector<linked_ptr<DictionaryValue> > catalogs; + catalogs.push_back(catalog); + + std::string error; + ExtensionMessageBundle* bundle = + ExtensionMessageBundle::Create(catalogs, &error); + EXPECT_TRUE(NULL != bundle); + EXPECT_TRUE(error.empty()); + + return bundle; +} + +TEST(ExtensionL10nUtil, LocalizeEmptyManifest) { + DictionaryValue manifest; + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_FALSE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + EXPECT_EQ(errors::kInvalidName, error); +} + +TEST(ExtensionL10nUtil, LocalizeManifestWithoutNameMsgAndEmptyDescription) { + DictionaryValue manifest; + manifest.SetString(keys::kName, "no __MSG"); + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_TRUE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + + std::string result; + ASSERT_TRUE(manifest.GetString(keys::kName, &result)); + EXPECT_EQ("no __MSG", result); + + EXPECT_FALSE(manifest.HasKey(keys::kDescription)); + + EXPECT_TRUE(error.empty()); +} + +TEST(ExtensionL10nUtil, LocalizeManifestWithNameMsgAndEmptyDescription) { + DictionaryValue manifest; + manifest.SetString(keys::kName, "__MSG_name__"); + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_TRUE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + + std::string result; + ASSERT_TRUE(manifest.GetString(keys::kName, &result)); + EXPECT_EQ("name", result); + + EXPECT_FALSE(manifest.HasKey(keys::kDescription)); + + EXPECT_TRUE(error.empty()); +} + +TEST(ExtensionL10nUtil, LocalizeManifestWithBadNameMsg) { + DictionaryValue manifest; + manifest.SetString(keys::kName, "__MSG_name_is_bad__"); + manifest.SetString(keys::kDescription, "__MSG_description__"); + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_FALSE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + + std::string result; + ASSERT_TRUE(manifest.GetString(keys::kName, &result)); + EXPECT_EQ("__MSG_name_is_bad__", result); + + ASSERT_TRUE(manifest.GetString(keys::kDescription, &result)); + EXPECT_EQ("__MSG_description__", result); + + EXPECT_EQ("Variable __MSG_name_is_bad__ used but not defined.", error); +} + +TEST(ExtensionL10nUtil, LocalizeManifestWithNameDescriptionDefaultTitleMsgs) { + DictionaryValue manifest; + manifest.SetString(keys::kName, "__MSG_name__"); + manifest.SetString(keys::kDescription, "__MSG_description__"); + std::wstring action_title(keys::kBrowserAction); + action_title.append(L"."); + action_title.append(keys::kPageActionDefaultTitle); + manifest.SetString(action_title, "__MSG_title__"); + + std::string error; + scoped_ptr<ExtensionMessageBundle> messages(CreateManifestBundle()); + + EXPECT_TRUE( + extension_l10n_util::LocalizeManifest(*messages, &manifest, &error)); + + std::string result; + ASSERT_TRUE(manifest.GetString(keys::kName, &result)); + EXPECT_EQ("name", result); + + ASSERT_TRUE(manifest.GetString(keys::kDescription, &result)); + EXPECT_EQ("description", result); + + ASSERT_TRUE(manifest.GetString(action_title, &result)); + EXPECT_EQ("action title", result); + + EXPECT_TRUE(error.empty()); +} + +// Try with NULL manifest. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithNullManifest) { + ExtensionInfo info(NULL, "", FilePath(), Extension::LOAD); + + EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + +// Try with default and current locales missing. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestEmptyManifest) { + DictionaryValue manifest; + ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD); + + EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + +// Try with missing current_locale. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithDefaultLocale) { + DictionaryValue manifest; + manifest.SetString(keys::kDefaultLocale, "en_US"); + + ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD); + + EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + +// Try with missing default_locale. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestWithCurrentLocale) { + DictionaryValue manifest; + manifest.SetString(keys::kCurrentLocale, + extension_l10n_util::CurrentLocaleOrDefault()); + + ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD); + + EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + +// Try with all data present, but with same current_locale as system locale. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestSameCurrentLocale) { + DictionaryValue manifest; + manifest.SetString(keys::kDefaultLocale, "en_US"); + manifest.SetString(keys::kCurrentLocale, + extension_l10n_util::CurrentLocaleOrDefault()); + + ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD); + + EXPECT_FALSE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + +// Try with all data present, but with different current_locale. +TEST(ExtensionL10nUtil, ShouldRelocalizeManifestDifferentCurrentLocale) { + DictionaryValue manifest; + manifest.SetString(keys::kDefaultLocale, "en_US"); + manifest.SetString(keys::kCurrentLocale, "sr"); + + ExtensionInfo info(&manifest, "", FilePath(), Extension::LOAD); + + EXPECT_TRUE(extension_l10n_util::ShouldRelocalizeManifest(info)); +} + } // namespace diff --git a/chrome/common/extensions/extension_message_bundle.cc b/chrome/common/extensions/extension_message_bundle.cc index a076d44..02038db 100644 --- a/chrome/common/extensions/extension_message_bundle.cc +++ b/chrome/common/extensions/extension_message_bundle.cc @@ -22,10 +22,6 @@ const char* ExtensionMessageBundle::kPlaceholderEnd = "$"; const char* ExtensionMessageBundle::kMessageBegin = "__MSG_"; const char* ExtensionMessageBundle::kMessageEnd = "__"; -const char* ExtensionMessageBundle::kExtensionName = "chrome_extension_name"; -const char* ExtensionMessageBundle::kExtensionDescription = - "chrome_extension_description"; - // Formats message in case we encounter a bad formed key in the JSON object. // Returns false and sets |error| to actual error message. static bool BadKeyMessage(const std::string& name, std::string* error) { diff --git a/chrome/common/extensions/extension_message_bundle.h b/chrome/common/extensions/extension_message_bundle.h index 3c57df7..b44805c 100644 --- a/chrome/common/extensions/extension_message_bundle.h +++ b/chrome/common/extensions/extension_message_bundle.h @@ -30,10 +30,6 @@ class ExtensionMessageBundle { static const char* kMessageBegin; static const char* kMessageEnd; - // Extension name and description message names - static const char* kExtensionName; - static const char* kExtensionDescription; - // Creates ExtensionMessageBundle or returns NULL if there was an error. // Expects locale_catalogs to be sorted from more specific to less specific, // with default catalog at the end. @@ -83,7 +79,7 @@ class ExtensionMessageBundle { private: // Use Create to create ExtensionMessageBundle instance. - ExtensionMessageBundle(); + ExtensionMessageBundle(); // Initializes the instance from the contents of vector of catalogs. // If the key is not present in more specific catalog we fall back to next one |