summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authorcira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-16 21:11:06 +0000
committercira@chromium.org <cira@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-12-16 21:11:06 +0000
commitc6d474f8e04b08306349ea514d529b8f25ffd4b2 (patch)
tree756833e85392a8b487cf3ee3cefd0d81ab8c51fa /chrome/common
parent538a1ac2bc5ad7f171e15642edb4e8c1dd34348d (diff)
downloadchromium_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.cc14
-rw-r--r--chrome/common/extensions/extension.h23
-rw-r--r--chrome/common/extensions/extension_constants.cc1
-rw-r--r--chrome/common/extensions/extension_constants.h1
-rw-r--r--chrome/common/extensions/extension_l10n_util.cc102
-rw-r--r--chrome/common/extensions/extension_l10n_util.h20
-rw-r--r--chrome/common/extensions/extension_l10n_util_unittest.cc185
-rw-r--r--chrome/common/extensions/extension_message_bundle.cc4
-rw-r--r--chrome/common/extensions/extension_message_bundle.h6
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