summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradriansc@google.com <adriansc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-18 21:46:11 +0000
committeradriansc@google.com <adriansc@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-18 21:46:11 +0000
commitf8cc62e64f6c43deb5973cadecf74e63d7162410 (patch)
tree022aeec91c32e1fc42ccf2a40336b1735ef88129
parentd8214f5e1cde6f9ded0ae6a749208c33dc3bf2be (diff)
downloadchromium_src-f8cc62e64f6c43deb5973cadecf74e63d7162410.zip
chromium_src-f8cc62e64f6c43deb5973cadecf74e63d7162410.tar.gz
chromium_src-f8cc62e64f6c43deb5973cadecf74e63d7162410.tar.bz2
Added code for localizing scripts CSS before injection takes place.
BUG=39899 TEST=browser_tests:ExtensionApiTest.ContentScriptCSSLocalization Review URL: http://codereview.chromium.org/7552028 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97365 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/content_script_apitest.cc5
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.cc53
-rw-r--r--chrome/browser/extensions/execute_code_in_tab_function.h11
-rw-r--r--chrome/browser/extensions/user_script_master.cc43
-rw-r--r--chrome/browser/extensions/user_script_master.h16
-rw-r--r--chrome/browser/extensions/user_script_master_unittest.cc12
-rw-r--r--chrome/browser/renderer_host/chrome_render_message_filter.cc23
-rw-r--r--chrome/common/extensions/extension_file_util.cc25
-rw-r--r--chrome/common/extensions/extension_file_util.h9
-rw-r--r--chrome/common/extensions/extension_set.cc10
-rw-r--r--chrome/common/extensions/extension_set.h7
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/_locales/en/messages.json6
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/background.html74
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/manifest.json17
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/style.css4
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.css3
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.pngbin0 -> 32284 bytes
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_extension_id.js23
-rw-r--r--chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_paragraph_style.js23
-rw-r--r--chrome/test/data/extensions/test_file_with_body.html8
20 files changed, 344 insertions, 28 deletions
diff --git a/chrome/browser/extensions/content_script_apitest.cc b/chrome/browser/extensions/content_script_apitest.cc
index c640eec..acd1a75 100644
--- a/chrome/browser/extensions/content_script_apitest.cc
+++ b/chrome/browser/extensions/content_script_apitest.cc
@@ -92,3 +92,8 @@ IN_PROC_BROWSER_TEST_F(
&styles_injected));
ASSERT_TRUE(styles_injected);
}
+
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ContentScriptCSSLocalization) {
+ ASSERT_TRUE(StartTestServer());
+ ASSERT_TRUE(RunExtensionTest("content_scripts/css_l10n")) << message_;
+}
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.cc b/chrome/browser/extensions/execute_code_in_tab_function.cc
index b5eb0d5..5c96195 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.cc
+++ b/chrome/browser/extensions/execute_code_in_tab_function.cc
@@ -4,7 +4,6 @@
#include "chrome/browser/extensions/execute_code_in_tab_function.h"
-#include "base/callback.h"
#include "base/string_util.h"
#include "base/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_service.h"
@@ -17,7 +16,10 @@
#include "chrome/common/extensions/extension.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_error_utils.h"
+#include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/common/extensions/extension_l10n_util.h"
#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
#include "content/browser/renderer_host/render_view_host.h"
#include "content/browser/tab_contents/tab_contents.h"
#include "content/browser/renderer_host/render_view_host.h"
@@ -117,13 +119,60 @@ bool ExecuteCodeInTabFunction::RunImpl() {
scoped_refptr<FileReader> file_reader(new FileReader(
resource_, NewCallback(this, &ExecuteCodeInTabFunction::DidLoadFile)));
file_reader->Start();
- AddRef(); // Keep us alive until DidLoadFile is called.
+ AddRef(); // Keep us alive until DidLoadAndLocalizeFile is called.
return true;
}
void ExecuteCodeInTabFunction::DidLoadFile(bool success,
const std::string& data) {
+ std::string function_name = name();
+ const Extension* extension = GetExtension();
+
+ // Check if the file is CSS and needs localization.
+ if (success &&
+ function_name == TabsInsertCSSFunction::function_name() &&
+ extension != NULL &&
+ data.find(ExtensionMessageBundle::kMessageBegin) != std::string::npos) {
+ BrowserThread::PostTask(
+ BrowserThread::FILE, FROM_HERE,
+ NewRunnableMethod(this, &ExecuteCodeInTabFunction::LocalizeCSS,
+ data,
+ extension->id(),
+ extension->path(),
+ extension->default_locale()));
+ } else {
+ DidLoadAndLocalizeFile(success, data);
+ }
+}
+
+void ExecuteCodeInTabFunction::LocalizeCSS(
+ const std::string& data,
+ const std::string& extension_id,
+ const FilePath& extension_path,
+ const std::string& extension_default_locale) {
+ scoped_ptr<SubstitutionMap> localization_messages(
+ extension_file_util::LoadExtensionMessageBundleSubstitutionMap(
+ extension_path, extension_id, extension_default_locale));
+
+ // We need to do message replacement on the data, so it has to be mutable.
+ std::string css_data = data;
+ std::string error;
+ ExtensionMessageBundle::ReplaceMessagesWithExternalDictionary(
+ *localization_messages, &css_data, &error);
+
+ // Call back DidLoadAndLocalizeFile on the UI thread. The success parameter
+ // is always true, because if loading had failed, we wouldn't have had
+ // anything to localize.
+ BrowserThread::PostTask(
+ BrowserThread::UI, FROM_HERE,
+ NewRunnableMethod(this,
+ &ExecuteCodeInTabFunction::DidLoadAndLocalizeFile,
+ true, css_data));
+}
+
+void ExecuteCodeInTabFunction::DidLoadAndLocalizeFile(bool success,
+ const std::string& data) {
if (success) {
Execute(data);
} else {
diff --git a/chrome/browser/extensions/execute_code_in_tab_function.h b/chrome/browser/extensions/execute_code_in_tab_function.h
index 2dbc115..b57ad49 100644
--- a/chrome/browser/extensions/execute_code_in_tab_function.h
+++ b/chrome/browser/extensions/execute_code_in_tab_function.h
@@ -33,6 +33,17 @@ class ExecuteCodeInTabFunction : public AsyncExtensionFunction,
// arguments has been loaded.
void DidLoadFile(bool success, const std::string& data);
+ // Runs on FILE thread. Loads message bundles for the extension and
+ // localizes the CSS data. Calls back DidLoadAndLocalizeFile on the UI thread.
+ void LocalizeCSS(
+ const std::string& data,
+ const std::string& extension_id,
+ const FilePath& extension_path,
+ const std::string& extension_default_locale);
+
+ // Called when contents from the loaded file have been localized.
+ void DidLoadAndLocalizeFile(bool success, const std::string& data);
+
// Run in UI thread. Code string contains the code to be executed. Returns
// true on success. If true is returned, this does an AddRef.
bool Execute(const std::string& code_string);
diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc
index 5e68098..af2abe2 100644
--- a/chrome/browser/extensions/user_script_master.cc
+++ b/chrome/browser/extensions/user_script_master.cc
@@ -4,6 +4,7 @@
#include "chrome/browser/extensions/user_script_master.h"
+#include <map>
#include <string>
#include <vector>
@@ -18,8 +19,10 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_file_util.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/extensions/extension_resource.h"
+#include "chrome/common/extensions/extension_set.h"
#include "content/browser/renderer_host/render_process_host.h"
#include "content/common/notification_service.h"
@@ -44,6 +47,12 @@ static bool GetDeclarationValue(const base::StringPiece& line,
UserScriptMaster::ScriptReloader::ScriptReloader(UserScriptMaster* master)
: master_(master) {
CHECK(BrowserThread::GetCurrentThreadIdentifier(&master_thread_id_));
+
+ // Gather extensions information needed for localization.
+ if (master && master->profile_ && master->profile_->GetExtensionInfoMap()) {
+ const ExtensionInfoMap* info_map = master->profile_->GetExtensionInfoMap();
+ info_map->extensions().GetExtensionsPathAndDefaultLocale(extensions_info_);
+ }
}
// static
@@ -160,7 +169,8 @@ void UserScriptMaster::ScriptReloader::NotifyMaster(
Release();
}
-static bool LoadScriptContent(UserScript::File* script_file) {
+static bool LoadScriptContent(UserScript::File* script_file,
+ const SubstitutionMap* localization_messages) {
std::string content;
const FilePath& path = ExtensionResource::GetFilePath(
script_file->extension_root(), script_file->relative_path());
@@ -175,6 +185,16 @@ static bool LoadScriptContent(UserScript::File* script_file) {
return false;
}
+ // Localize the content.
+ if (localization_messages) {
+ std::string error;
+ ExtensionMessageBundle::ReplaceMessagesWithExternalDictionary(
+ *localization_messages, &content, &error);
+ if (!error.empty()) {
+ LOG(WARNING) << "Failed to replace messages in script: " << error;
+ }
+ }
+
// Remove BOM from the content.
std::string::size_type index = content.find(kUtf8ByteOrderMark);
if (index == 0) {
@@ -186,24 +206,37 @@ static bool LoadScriptContent(UserScript::File* script_file) {
return true;
}
-// static
void UserScriptMaster::ScriptReloader::LoadUserScripts(
UserScriptList* user_scripts) {
for (size_t i = 0; i < user_scripts->size(); ++i) {
UserScript& script = user_scripts->at(i);
+ scoped_ptr<SubstitutionMap> localization_messages(
+ GetLocalizationMessages(script.extension_id()));
for (size_t k = 0; k < script.js_scripts().size(); ++k) {
UserScript::File& script_file = script.js_scripts()[k];
if (script_file.GetContent().empty())
- LoadScriptContent(&script_file);
+ LoadScriptContent(&script_file, NULL);
}
for (size_t k = 0; k < script.css_scripts().size(); ++k) {
UserScript::File& script_file = script.css_scripts()[k];
if (script_file.GetContent().empty())
- LoadScriptContent(&script_file);
+ LoadScriptContent(&script_file, localization_messages.get());
}
}
}
+SubstitutionMap* UserScriptMaster::ScriptReloader::GetLocalizationMessages(
+ std::string extension_id) {
+ if (extensions_info_.find(extension_id) == extensions_info_.end()) {
+ return NULL;
+ }
+
+ return extension_file_util::LoadExtensionMessageBundleSubstitutionMap(
+ extensions_info_[extension_id].first,
+ extension_id,
+ extensions_info_[extension_id].second);
+}
+
// Pickle user scripts and return pointer to the shared memory.
static base::SharedMemory* Serialize(const UserScriptList& scripts) {
Pickle pickle;
diff --git a/chrome/browser/extensions/user_script_master.h b/chrome/browser/extensions/user_script_master.h
index 2df53d8..1ef6453 100644
--- a/chrome/browser/extensions/user_script_master.h
+++ b/chrome/browser/extensions/user_script_master.h
@@ -6,10 +6,15 @@
#define CHROME_BROWSER_EXTENSIONS_USER_SCRIPT_MASTER_H_
#pragma once
+#include <map>
+#include <string>
+
#include "base/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/shared_memory.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_set.h"
#include "chrome/common/extensions/user_script.h"
#include "content/browser/browser_thread.h"
#include "content/common/notification_observer.h"
@@ -75,7 +80,6 @@ class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>,
}
private:
- private:
FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, SkipBOMAtTheBeginning);
FRIEND_TEST_ALL_PREFIXES(UserScriptMasterTest, LeaveBOMNotAtTheBeginning);
friend class base::RefCountedThreadSafe<UserScriptMaster::ScriptReloader>;
@@ -98,12 +102,20 @@ class UserScriptMaster : public base::RefCountedThreadSafe<UserScriptMaster>,
// tied to the caller.
void RunLoad(const UserScriptList& user_scripts);
- static void LoadUserScripts(UserScriptList* user_scripts);
+ void LoadUserScripts(UserScriptList* user_scripts);
+
+ // Uses extensions_info_ to build a map of localization messages.
+ // Returns NULL if |extension_id| is invalid.
+ SubstitutionMap* GetLocalizationMessages(std::string extension_id);
// A pointer back to our master.
// May be NULL if DisownMaster() is called.
UserScriptMaster* master_;
+ // Maps extension info needed for localization to an extension ID.
+ std::map<std::string, ExtensionSet::ExtensionPathAndDefaultLocale>
+ extensions_info_;
+
// The message loop to call our master back on.
// Expected to always outlive us.
BrowserThread::ID master_thread_id_;
diff --git a/chrome/browser/extensions/user_script_master_unittest.cc b/chrome/browser/extensions/user_script_master_unittest.cc
index d1ea7ac..2637c28 100644
--- a/chrome/browser/extensions/user_script_master_unittest.cc
+++ b/chrome/browser/extensions/user_script_master_unittest.cc
@@ -220,7 +220,11 @@ TEST_F(UserScriptMasterTest, SkipBOMAtTheBeginning) {
UserScriptList user_scripts;
user_scripts.push_back(user_script);
- UserScriptMaster::ScriptReloader::LoadUserScripts(&user_scripts);
+ UserScriptMaster::ScriptReloader* script_reloader =
+ new UserScriptMaster::ScriptReloader(NULL);
+ script_reloader->AddRef();
+ script_reloader->LoadUserScripts(&user_scripts);
+ script_reloader->Release();
EXPECT_EQ(content.substr(3),
user_scripts[0].js_scripts()[0].GetContent().as_string());
@@ -239,7 +243,11 @@ TEST_F(UserScriptMasterTest, LeaveBOMNotAtTheBeginning) {
UserScriptList user_scripts;
user_scripts.push_back(user_script);
- UserScriptMaster::ScriptReloader::LoadUserScripts(&user_scripts);
+ UserScriptMaster::ScriptReloader* script_reloader =
+ new UserScriptMaster::ScriptReloader(NULL);
+ script_reloader->AddRef();
+ script_reloader->LoadUserScripts(&user_scripts);
+ script_reloader->Release();
EXPECT_EQ(content, user_scripts[0].js_scripts()[0].GetContent().as_string());
}
diff --git a/chrome/browser/renderer_host/chrome_render_message_filter.cc b/chrome/browser/renderer_host/chrome_render_message_filter.cc
index 5cc1293..7486ebd 100644
--- a/chrome/browser/renderer_host/chrome_render_message_filter.cc
+++ b/chrome/browser/renderer_host/chrome_render_message_filter.cc
@@ -323,25 +323,14 @@ void ChromeRenderMessageFilter::OnGetExtensionMessageBundleOnFileThread(
IPC::Message* reply_msg) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
- std::map<std::string, std::string> dictionary_map;
- if (!default_locale.empty()) {
- // Touch disk only if extension is localized.
- std::string error;
- scoped_ptr<ExtensionMessageBundle> bundle(
- extension_file_util::LoadExtensionMessageBundle(
- extension_path, default_locale, &error));
-
- if (bundle.get())
- dictionary_map = *bundle->dictionary();
- }
-
- // Add @@extension_id reserved message here, so it's available to
- // non-localized extensions too.
- dictionary_map.insert(
- std::make_pair(ExtensionMessageBundle::kExtensionIdKey, extension_id));
+ scoped_ptr<ExtensionMessageBundle::SubstitutionMap> dictionary_map(
+ extension_file_util::LoadExtensionMessageBundleSubstitutionMap(
+ extension_path,
+ extension_id,
+ default_locale));
ExtensionHostMsg_GetMessageBundle::WriteReplyParams(
- reply_msg, dictionary_map);
+ reply_msg, *dictionary_map);
Send(reply_msg);
}
diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc
index e7ba195..55ca7de 100644
--- a/chrome/common/extensions/extension_file_util.cc
+++ b/chrome/common/extensions/extension_file_util.cc
@@ -20,6 +20,8 @@
#include "chrome/common/extensions/extension_action.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/extensions/extension_l10n_util.h"
+#include "chrome/common/extensions/extension_messages.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/extensions/extension_resource.h"
#include "chrome/common/extensions/extension_sidebar_defaults.h"
#include "content/common/json_value_serializer.h"
@@ -400,6 +402,29 @@ ExtensionMessageBundle* LoadExtensionMessageBundle(
return message_bundle;
}
+SubstitutionMap* LoadExtensionMessageBundleSubstitutionMap(
+ const FilePath& extension_path,
+ const std::string& extension_id,
+ const std::string& default_locale) {
+ SubstitutionMap* returnValue = new SubstitutionMap();
+ if (!default_locale.empty()) {
+ // Touch disk only if extension is localized.
+ std::string error;
+ scoped_ptr<ExtensionMessageBundle> bundle(
+ LoadExtensionMessageBundle(extension_path, default_locale, &error));
+
+ if (bundle.get())
+ *returnValue = *bundle->dictionary();
+ }
+
+ // Add @@extension_id reserved message here, so it's available to
+ // non-localized extensions too.
+ returnValue->insert(
+ std::make_pair(ExtensionMessageBundle::kExtensionIdKey, extension_id));
+
+ return returnValue;
+}
+
static bool ValidateLocaleInfo(const Extension& extension, std::string* error) {
// default_locale and _locales have to be both present or both missing.
const FilePath path = extension.path().Append(Extension::kLocaleFolder);
diff --git a/chrome/common/extensions/extension_file_util.h b/chrome/common/extensions/extension_file_util.h
index 7913e2f..ce60673 100644
--- a/chrome/common/extensions/extension_file_util.h
+++ b/chrome/common/extensions/extension_file_util.h
@@ -10,6 +10,7 @@
#include <map>
#include "chrome/common/extensions/extension.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
class Extension;
class ExtensionMessageBundle;
@@ -66,6 +67,14 @@ ExtensionMessageBundle* LoadExtensionMessageBundle(
const std::string& default_locale,
std::string* error);
+// Loads the extension message bundle substitution map. Contains at least
+// extension_id item.
+ExtensionMessageBundle::SubstitutionMap*
+ LoadExtensionMessageBundleSubstitutionMap(
+ const FilePath& extension_path,
+ const std::string& extension_id,
+ const std::string& default_locale);
+
// We need to reserve the namespace of entries that start with "_" for future
// use by Chrome.
// If any files or directories are found using "_" prefix and are not on
diff --git a/chrome/common/extensions/extension_set.cc b/chrome/common/extensions/extension_set.cc
index 0825089..c839fd6d 100644
--- a/chrome/common/extensions/extension_set.cc
+++ b/chrome/common/extensions/extension_set.cc
@@ -79,3 +79,13 @@ bool ExtensionSet::ExtensionBindingsAllowed(const GURL& url) const {
return false;
}
+
+void ExtensionSet::GetExtensionsPathAndDefaultLocale(
+ std::map<std::string, ExtensionPathAndDefaultLocale>& info) const {
+ info.clear();
+ ExtensionMap::const_iterator i = extensions_.begin();
+ for (; i != extensions_.end(); ++i) {
+ info[i->first] = std::make_pair(i->second->path(),
+ i->second->default_locale());
+ }
+}
diff --git a/chrome/common/extensions/extension_set.h b/chrome/common/extensions/extension_set.h
index dbd16ad..d50086b 100644
--- a/chrome/common/extensions/extension_set.h
+++ b/chrome/common/extensions/extension_set.h
@@ -6,6 +6,7 @@
#define CHROME_COMMON_EXTENSIONS_EXTENSION_SET_H_
#pragma once
+#include <map>
#include <string>
#include <vector>
@@ -18,6 +19,8 @@
// Only one extension can be in the set with a given ID.
class ExtensionSet {
public:
+ typedef std::pair<FilePath, std::string> ExtensionPathAndDefaultLocale;
+
ExtensionSet();
~ExtensionSet();
@@ -57,6 +60,10 @@ class ExtensionSet {
// permissions the given extension has been granted.
bool ExtensionBindingsAllowed(const GURL& url) const;
+ // Populates map with the path and default locale for all extension IDs.
+ void GetExtensionsPathAndDefaultLocale(
+ std::map<std::string, ExtensionPathAndDefaultLocale>& info) const;
+
private:
FRIEND_TEST_ALL_PREFIXES(ExtensionSetTest, ExtensionSet);
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/_locales/en/messages.json b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/_locales/en/messages.json
new file mode 100644
index 0000000..c35330f
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/_locales/en/messages.json
@@ -0,0 +1,6 @@
+{
+ "text_color": {
+ "message": "red"
+ }
+}
+
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/background.html b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/background.html
new file mode 100644
index 0000000..f907302
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/background.html
@@ -0,0 +1,74 @@
+<script>
+
+var firstEnter = true;
+
+chrome.test.getConfig(function(config) {
+ chrome.test.log('Creating tab...');
+
+ var URL = 'http://localhost:PORT/files/extensions/test_file_with_body.html';
+ var TEST_FILE_URL = URL.replace(/PORT/, config.testServer.port);
+
+ chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
+ if (changeInfo.status != 'complete')
+ return;
+ if (!firstEnter)
+ return;
+ firstEnter = false;
+
+ // We need to test two different paths, because the message bundles used
+ // for localization are loaded differently in each case:
+ // (1) localization upon loading extension scripts
+ // (2) localization upon injecting CSS with JavaScript
+ chrome.test.runTests([
+
+ // Tests that CSS loaded automatically from the files specified in the
+ // manifest has had __MSG_@@extension_id__ replaced with the actual
+ // extension id.
+ function extensionIDMessageGetsReplacedInContentScriptCSS() {
+ chrome.extension.onRequest.addListener(
+ function(request, sender, sendResponse) {
+ if (request.tag == 'extension_id') {
+ if (request.message == 'passed') {
+ chrome.test.succeed();
+ } else {
+ chrome.test.fail(request.message);
+ }
+ }
+ }
+ );
+ var script_file = {};
+ script_file.file = 'test_extension_id.js';
+ chrome.tabs.executeScript(tabId, script_file);
+ },
+
+ // First injects CSS into the page through chrome.tabs.insertCSS and then
+ // checks that it has had __MSG_text_color__ replaced with the correct
+ // message value.
+ function textDirectionMessageGetsReplacedInInsertCSSCall() {
+ chrome.extension.onRequest.addListener(
+ function(request, sender, sendResponse) {
+ if (request.tag == 'paragraph_style') {
+ if (request.message == 'passed') {
+ chrome.test.succeed();
+ } else {
+ chrome.test.fail(request.message);
+ }
+ }
+ }
+ );
+ var css_file = {};
+ css_file.file = 'test.css';
+ chrome.tabs.insertCSS(tabId, css_file, function() {
+ var script_file = {};
+ script_file.file = 'test_paragraph_style.js';
+ chrome.tabs.executeScript(tabId,script_file);
+ });
+ }
+
+ ]);
+ });
+
+ chrome.tabs.create({ url: TEST_FILE_URL });
+});
+
+</script>
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/manifest.json b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/manifest.json
new file mode 100644
index 0000000..cf42ca5
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/manifest.json
@@ -0,0 +1,17 @@
+{
+ "name": "Localize CSS scripts",
+ "description": "Checks that messages in CSS content scripts get localized.",
+ "version": "0.1",
+ "background_page": "background.html",
+ "default_locale": "en",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*", "file://*/*"],
+ "css": ["style.css"]
+ }
+ ],
+ "permissions": [
+ "tabs", "http://*/*", "file://*/*"
+ ]
+}
+
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/style.css b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/style.css
new file mode 100644
index 0000000..8f8c465
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/style.css
@@ -0,0 +1,4 @@
+body {
+ background-image:url('chrome-extension://__MSG_@@extension_id__/test.png');
+}
+
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.css b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.css
new file mode 100644
index 0000000..3df6b52
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.css
@@ -0,0 +1,3 @@
+p {
+ color: __MSG_text_color__;
+}
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.png b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.png
new file mode 100644
index 0000000..f41786e
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test.png
Binary files differ
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_extension_id.js b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_extension_id.js
new file mode 100644
index 0000000..ec63253
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_extension_id.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tests that the 'body' element has had CSS injected, and that the CSS code
+// has had the __MSG_@@extension_id__ message replaced ('extension_id' must
+// not be present in any CSS code).
+
+var rules = getMatchedCSSRules(document.getElementById('bodyId'));
+if (rules != null) {
+ for (var i = 0; i < rules.length; ++i) {
+ if (rules.item(i).cssText.indexOf('extension_id') != -1) {
+ chrome.extension.sendRequest({tag: 'extension_id', message:
+ 'Found unreplaced extension_id in: ' + rules.item(i).cssText});
+ break;
+ }
+ }
+ chrome.extension.sendRequest({tag: 'extension_id', message: 'passed'});
+} else {
+ chrome.extension.sendRequest({tag: 'extension_id', message:
+ 'No CSS rules found.'});
+}
+
diff --git a/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_paragraph_style.js b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_paragraph_style.js
new file mode 100644
index 0000000..b4935ee
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/content_scripts/css_l10n/test_paragraph_style.js
@@ -0,0 +1,23 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Tests that the 'p' element has had CSS injected, and that the CSS code
+// has had the __MSG_text_color__ message replaced ('text_color' must
+// not be present in any CSS code).
+
+var rules = getMatchedCSSRules(document.getElementById('pId'));
+if (rules != null) {
+ for (var i = 0; i < rules.length; ++i) {
+ if (rules.item(i).cssText.indexOf('text_color') != -1) {
+ chrome.extension.sendRequest({tag: 'paragraph_style', message:
+ 'Found unreplaced test_color in: ' + rules.item(i).cssText});
+ break;
+ }
+ }
+ chrome.extension.sendRequest({tag: 'paragraph_style', message: 'passed'});
+} else {
+ chrome.extension.sendRequest({tag: 'paragraph_style', message:
+ 'No CSS rules found.'});
+}
+
diff --git a/chrome/test/data/extensions/test_file_with_body.html b/chrome/test/data/extensions/test_file_with_body.html
new file mode 100644
index 0000000..594f540
--- /dev/null
+++ b/chrome/test/data/extensions/test_file_with_body.html
@@ -0,0 +1,8 @@
+<html>
+ <body id="bodyId">
+ <p id="pId">
+ This is a simple text.
+ </p>
+ </body>
+</html>
+