summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xchrome/common/extensions/api/extension_api.json3
-rw-r--r--chrome/common/extensions/docs/content_scripts.html2
-rw-r--r--chrome/common/extensions/docs/i18n.html146
-rw-r--r--chrome/renderer/extensions/extension_api_client_unittest.cc4
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.cc99
-rw-r--r--chrome/renderer/extensions/extension_process_bindings.h6
-rw-r--r--chrome/renderer/extensions/renderer_extension_bindings.cc115
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js6
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js13
-rw-r--r--chrome/test/data/extensions/api_test/i18n/a.js1
-rw-r--r--chrome/test/data/extensions/api_test/i18n/manifest.json9
-rw-r--r--chrome/test/data/extensions/api_test/i18n/test.js12
12 files changed, 280 insertions, 136 deletions
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index e40ce74..29622b9 100755
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -1614,6 +1614,7 @@
},
{
"namespace": "i18n",
+ "unprivileged": true,
"types": [],
"functions": [
{
@@ -1633,7 +1634,7 @@
{
"name": "getMessage",
"type": "function",
- "nodoc": true,
+ "unprivileged": true,
"description": "Get a message from the extension language catalog, for a current locale.",
"parameters": [
{ "type": "string",
diff --git a/chrome/common/extensions/docs/content_scripts.html b/chrome/common/extensions/docs/content_scripts.html
index 04399ff..1eef989 100644
--- a/chrome/common/extensions/docs/content_scripts.html
+++ b/chrome/common/extensions/docs/content_scripts.html
@@ -772,5 +772,5 @@ sending a request to its parent extension.
</div> <!-- /gc-container -->
</body></html>
-Blocked access to external URL http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&
Blocked access to external URL http://www.youtube.com/v/B4M_a7xejYI&hl=en_US&fs=1&
+Blocked access to external URL http://www.youtube.com/v/laLudeUmXHM&hl=en_US&fs=1&
diff --git a/chrome/common/extensions/docs/i18n.html b/chrome/common/extensions/docs/i18n.html
index e07d919..b771f0e 100644
--- a/chrome/common/extensions/docs/i18n.html
+++ b/chrome/common/extensions/docs/i18n.html
@@ -217,8 +217,8 @@
<ol>
<li>
<a href="#method-getAcceptLanguages">getAcceptLanguages</a>
- </li><li style="display: none; ">
- <a href="#method-anchor">methodName</a>
+ </li><li>
+ <a href="#method-getMessage">getMessage</a>
</li>
</ol>
</li>
@@ -469,27 +469,105 @@ For other examples and for help in viewing the source code, see
</div> <!-- /description -->
- </div><div class="apiItem" style="display: none; ">
- <a></a> <!-- method-anchor -->
- <h4>method name</h4>
+ </div><div class="apiItem">
+ <a name="method-getMessage"></a> <!-- method-anchor -->
+ <h4>getMessage</h4>
- <div class="summary"><span>void</span>
+ <div class="summary"><span>string</span>
<!-- Note: intentionally longer 80 columns -->
- <span>chrome.module.methodName</span>(<span><span>, </span><span></span>
- <var><span></span></var></span>)</div>
+ <span>chrome.i18n.getMessage</span>(<span class="null"><span style="display: none; ">, </span><span>string</span>
+ <var><span>messageName</span></var></span><span class="optional"><span>, </span><span>string or array of string</span>
+ <var><span>substitutions</span></var></span>)</div>
<div class="description">
- <p class="todo">Undocumented.</p>
- <p>
- A description from the json schema def of the function goes here.
- </p>
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Get a message from the extension language catalog, for a current locale.</p>
<!-- PARAMETERS -->
<h4>Parameters</h4>
<dl>
<div>
<div>
- </div>
+ <dt>
+ <var>messageName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Message name from the extension catalog.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div>
+ <div>
+ <dt>
+ <var>substitutions</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string or array of string</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>1 - 9 substitution parameters, if the message requires any.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
</div>
</dl>
@@ -498,12 +576,50 @@ For other examples and for help in viewing the source code, see
<dl>
<div>
<div>
- </div>
+ <dt>
+ <var style="display: none; ">paramName</var>
+ <em>
+
+ <!-- TYPE -->
+ <div style="display:inline">
+ (
+ <span class="optional" style="display: none; ">optional</span>
+ <span id="typeTemplate">
+ <span style="display: none; ">
+ <a> Type</a>
+ </span>
+ <span>
+ <span style="display: none; ">
+ array of <span><span></span></span>
+ </span>
+ <span>string</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Message localized for current locale.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
</div>
</dl>
<!-- CALLBACK -->
- <div>
+ <div style="display: none; ">
<div>
<h4>Callback function</h4>
<p>
diff --git a/chrome/renderer/extensions/extension_api_client_unittest.cc b/chrome/renderer/extensions/extension_api_client_unittest.cc
index abe3f5c..05c2ddf 100644
--- a/chrome/renderer/extensions/extension_api_client_unittest.cc
+++ b/chrome/renderer/extensions/extension_api_client_unittest.cc
@@ -620,7 +620,9 @@ TEST_F(ExtensionAPIClientTest, GetAcceptLanguages) {
"i18n.getAcceptLanguages", "null");
}
-TEST_F(ExtensionAPIClientTest, GetL10nMessage) {
+// TODO(cira): re-enable when we get validation going for
+// renderer_process_bindings.js
+TEST_F(ExtensionAPIClientTest, DISABLED_GetL10nMessage) {
ExpectJsFail("chrome.i18n.getMessage()",
"Uncaught Error: Parameter 0 is required.");
diff --git a/chrome/renderer/extensions/extension_process_bindings.cc b/chrome/renderer/extensions/extension_process_bindings.cc
index c1009c3e..f065dd9 100644
--- a/chrome/renderer/extensions/extension_process_bindings.cc
+++ b/chrome/renderer/extensions/extension_process_bindings.cc
@@ -14,7 +14,6 @@
#include "base/singleton.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/extension.h"
-#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/extensions/url_pattern.h"
#include "chrome/common/render_messages.h"
#include "chrome/common/url_constants.h"
@@ -56,13 +55,6 @@ typedef std::map<std::string, bool> PermissionsMap;
// A map of extension ID to permissions map.
typedef std::map<std::string, PermissionsMap> ExtensionPermissionsMap;
-// A map of message name to message.
-typedef std::map<std::string, std::string> L10nMessagesMap;
-
-// A map of extension ID to l10n message map.
-typedef std::map<std::string, L10nMessagesMap >
- ExtensionToL10nMessagesMap;
-
const char kExtensionName[] = "chrome/ExtensionProcessBindings";
const char* kExtensionDeps[] = {
BaseJsV8Extension::kName,
@@ -76,7 +68,6 @@ struct SingletonData {
std::set<std::string> function_names_;
PageActionIdMap page_action_ids_;
ExtensionPermissionsMap permissions_;
- ExtensionToL10nMessagesMap extension_l10n_messages_map_;
};
static std::set<std::string>* GetFunctionNameSet() {
@@ -91,20 +82,6 @@ static PermissionsMap* GetPermissionsMap(const std::string& extension_id) {
return &Singleton<SingletonData>()->permissions_[extension_id];
}
-static ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
- return &Singleton<SingletonData>()->extension_l10n_messages_map_;
-}
-
-static L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id) {
- ExtensionToL10nMessagesMap::iterator it =
- Singleton<SingletonData>()->extension_l10n_messages_map_.find(extension_id);
- if (it != Singleton<SingletonData>()->extension_l10n_messages_map_.end()) {
- return &(it->second);
- } else {
- return NULL;
- }
-}
-
static void GetActiveExtensionIDs(std::set<std::string>* extension_ids) {
ExtensionPermissionsMap& permissions =
Singleton<SingletonData>()->permissions_;
@@ -254,8 +231,6 @@ class ExtensionImpl : public ExtensionBase {
return v8::FunctionTemplate::New(StartRequest);
} else if (name->Equals(v8::String::New("GetRenderViewId"))) {
return v8::FunctionTemplate::New(GetRenderViewId);
- } else if (name->Equals(v8::String::New("GetL10nMessage"))) {
- return v8::FunctionTemplate::New(GetL10nMessage);
} else if (name->Equals(v8::String::New("GetPopupView"))) {
return v8::FunctionTemplate::New(GetPopupView);
} else if (name->Equals(v8::String::New("GetPopupParentWindow"))) {
@@ -412,71 +387,6 @@ class ExtensionImpl : public ExtensionBase {
return page_action_vector;
}
- static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args) {
- if (args.Length() != 2 || !args[0]->IsString()) {
- NOTREACHED() << "Bad arguments";
- return v8::Undefined();
- }
-
- std::string extension_id = ExtensionIdForCurrentContext();
- if (extension_id.empty())
- return v8::Undefined();
-
- L10nMessagesMap* l10n_messages = GetL10nMessagesMap(extension_id);
- if (!l10n_messages) {
- // Get the current RenderView so that we can send a routed IPC message
- // from the correct source.
- RenderView* renderview = bindings_utils::GetRenderViewForCurrentContext();
- if (!renderview)
- return v8::Undefined();
-
- L10nMessagesMap messages;
- // A sync call to load message catalogs for current extension.
- renderview->Send(new ViewHostMsg_GetExtensionMessageBundle(
- extension_id, &messages));
-
- if (messages.empty())
- return v8::Undefined();
-
- ExtensionProcessBindings::SetL10nMessages(extension_id, messages);
- l10n_messages = GetL10nMessagesMap(extension_id);
- if (!l10n_messages)
- return v8::Undefined();
- }
-
- std::string message_name = *v8::String::AsciiValue(args[0]);
- std::string message =
- ExtensionMessageBundle::GetL10nMessage(message_name, *l10n_messages);
-
- std::vector<std::string> substitutions;
- if (args[1]->IsNull() || args[1]->IsUndefined()) {
- // chrome.i18n.getMessage("message_name");
- // chrome.i18n.getMessage("message_name", null);
- return v8::String::New(message.c_str());
- } else if (args[1]->IsString()) {
- // chrome.i18n.getMessage("message_name", "one param");
- std::string substitute = *v8::String::Utf8Value(args[1]->ToString());
- substitutions.push_back(substitute);
- } else if (args[1]->IsArray()) {
- // chrome.i18n.getMessage("message_name", ["more", "params"]);
- v8::Array* placeholders = static_cast<v8::Array*>(*args[1]);
- uint32_t count = placeholders->Length();
- DCHECK(count > 0 && count <= 9);
- for (uint32_t i = 0; i < count; ++i) {
- std::string substitute =
- *v8::String::Utf8Value(
- placeholders->Get(v8::Integer::New(i))->ToString());
- substitutions.push_back(substitute);
- }
- } else {
- NOTREACHED() << "Couldn't parse second parameter.";
- return v8::Undefined();
- }
-
- return v8::String::New(ReplaceStringPlaceholders(
- message, substitutions, NULL).c_str());
- }
-
// Common code for starting an API request to the browser. |value_args|
// contains the request's arguments.
static v8::Handle<v8::Value> StartRequestCommon(
@@ -656,15 +566,6 @@ void ExtensionProcessBindings::SetPageActions(
}
// static
-void ExtensionProcessBindings::SetL10nMessages(
- const std::string& extension_id,
- const std::map<std::string, std::string>& l10n_messages) {
- ExtensionToL10nMessagesMap& l10n_messages_map =
- *GetExtensionToL10nMessagesMap();
- l10n_messages_map[extension_id] = l10n_messages;
-}
-
-// static
void ExtensionProcessBindings::SetAPIPermissions(
const std::string& extension_id,
const std::vector<std::string>& permissions) {
diff --git a/chrome/renderer/extensions/extension_process_bindings.h b/chrome/renderer/extensions/extension_process_bindings.h
index 08f068d2..2fd7613 100644
--- a/chrome/renderer/extensions/extension_process_bindings.h
+++ b/chrome/renderer/extensions/extension_process_bindings.h
@@ -7,7 +7,6 @@
#ifndef CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
#define CHROME_RENDERER_EXTENSIONS_EXTENSION_PROCESS_BINDINGS_H_
-#include <map>
#include <set>
#include <string>
#include <vector>
@@ -47,11 +46,6 @@ class ExtensionProcessBindings {
static void SetHostPermissions(const GURL& extension_url,
const std::vector<URLPattern>& permissions);
- // Set l10n messages for a particular extension.
- static void SetL10nMessages(
- const std::string& extension_id,
- const std::map<std::string, std::string>& l10n_messages);
-
// Check if the extension in the currently running context has permission to
// access the given extension function. Must be called with a valid V8
// context in scope.
diff --git a/chrome/renderer/extensions/renderer_extension_bindings.cc b/chrome/renderer/extensions/renderer_extension_bindings.cc
index 42882d07..54231b6 100644
--- a/chrome/renderer/extensions/renderer_extension_bindings.cc
+++ b/chrome/renderer/extensions/renderer_extension_bindings.cc
@@ -4,10 +4,16 @@
#include "chrome/renderer/extensions/renderer_extension_bindings.h"
+#include <map>
+#include <string>
+
#include "app/resource_bundle.h"
#include "base/basictypes.h"
+#include "base/singleton.h"
#include "base/values.h"
+#include "chrome/common/extensions/extension_message_bundle.h"
#include "chrome/common/render_messages.h"
+#include "chrome/common/url_constants.h"
#include "chrome/renderer/extensions/bindings_utils.h"
#include "chrome/renderer/extensions/event_bindings.h"
#include "chrome/renderer/render_thread.h"
@@ -29,6 +35,12 @@ using bindings_utils::ExtensionBase;
namespace {
+// A map of message name to message.
+typedef std::map<std::string, std::string> L10nMessagesMap;
+
+// A map of extension ID to l10n message map.
+typedef std::map<std::string, L10nMessagesMap > ExtensionToL10nMessagesMap;
+
struct ExtensionData {
struct PortData {
int ref_count; // how many contexts have a handle to this port
@@ -36,18 +48,38 @@ struct ExtensionData {
PortData() : ref_count(0), disconnected(false) {}
};
std::map<int, PortData> ports; // port ID -> data
+ // Maps extension ID to message map.
+ ExtensionToL10nMessagesMap extension_l10n_messages_map_;
};
-bool HasPortData(int port_id) {
+
+static bool HasPortData(int port_id) {
return Singleton<ExtensionData>::get()->ports.find(port_id) !=
Singleton<ExtensionData>::get()->ports.end();
}
-ExtensionData::PortData& GetPortData(int port_id) {
+
+static ExtensionData::PortData& GetPortData(int port_id) {
return Singleton<ExtensionData>::get()->ports[port_id];
}
-void ClearPortData(int port_id) {
+
+static void ClearPortData(int port_id) {
Singleton<ExtensionData>::get()->ports.erase(port_id);
}
+static ExtensionToL10nMessagesMap* GetExtensionToL10nMessagesMap() {
+ return &Singleton<ExtensionData>()->extension_l10n_messages_map_;
+}
+
+static L10nMessagesMap* GetL10nMessagesMap(const std::string extension_id) {
+ ExtensionToL10nMessagesMap::iterator it =
+ Singleton<ExtensionData>()->extension_l10n_messages_map_.find(
+ extension_id);
+ if (it != Singleton<ExtensionData>()->extension_l10n_messages_map_.end()) {
+ return &(it->second);
+ } else {
+ return NULL;
+ }
+}
+
const char kPortClosedError[] = "Attempting to use a disconnected port object";
const char* kExtensionDeps[] = { EventBindings::kName };
@@ -72,6 +104,8 @@ class ExtensionImpl : public ExtensionBase {
return v8::FunctionTemplate::New(PortAddRef);
} else if (name->Equals(v8::String::New("PortRelease"))) {
return v8::FunctionTemplate::New(PortRelease);
+ } else if (name->Equals(v8::String::New("GetL10nMessage"))) {
+ return v8::FunctionTemplate::New(GetL10nMessage);
}
return ExtensionBase::GetNativeFunction(name);
}
@@ -158,6 +192,81 @@ class ExtensionImpl : public ExtensionBase {
}
return v8::Undefined();
}
+
+ static v8::Handle<v8::Value> GetL10nMessage(const v8::Arguments& args) {
+ if (args.Length() != 3 || !args[0]->IsString()) {
+ NOTREACHED() << "Bad arguments";
+ return v8::Undefined();
+ }
+
+ std::string extension_id;
+ if (args[2]->IsNull() || !args[2]->IsString()) {
+ return v8::Undefined();
+ } else {
+ extension_id = *v8::String::Utf8Value(args[2]->ToString());
+ if (extension_id.empty())
+ return v8::Undefined();
+ }
+
+ L10nMessagesMap* l10n_messages = GetL10nMessagesMap(extension_id);
+ if (!l10n_messages) {
+ // Get the current RenderView so that we can send a routed IPC message
+ // from the correct source.
+ RenderView* renderview = bindings_utils::GetRenderViewForCurrentContext();
+ if (!renderview)
+ return v8::Undefined();
+
+ L10nMessagesMap messages;
+ // A sync call to load message catalogs for current extension.
+ renderview->Send(new ViewHostMsg_GetExtensionMessageBundle(
+ extension_id, &messages));
+
+ if (messages.empty())
+ return v8::Undefined();
+
+ // Save messages we got.
+ ExtensionToL10nMessagesMap& l10n_messages_map =
+ *GetExtensionToL10nMessagesMap();
+ l10n_messages_map[extension_id] = messages;
+
+ l10n_messages = GetL10nMessagesMap(extension_id);
+ if (!l10n_messages)
+ return v8::Undefined();
+ }
+
+ std::string message_name = *v8::String::AsciiValue(args[0]);
+ std::string message =
+ ExtensionMessageBundle::GetL10nMessage(message_name, *l10n_messages);
+
+ std::vector<std::string> substitutions;
+ if (args[1]->IsNull() || args[1]->IsUndefined()) {
+ // chrome.i18n.getMessage("message_name");
+ // chrome.i18n.getMessage("message_name", null);
+ return v8::String::New(message.c_str());
+ } else if (args[1]->IsString()) {
+ // chrome.i18n.getMessage("message_name", "one param");
+ std::string substitute = *v8::String::Utf8Value(args[1]->ToString());
+ substitutions.push_back(substitute);
+ } else if (args[1]->IsArray()) {
+ // chrome.i18n.getMessage("message_name", ["more", "params"]);
+ v8::Array* placeholders = static_cast<v8::Array*>(*args[1]);
+ uint32_t count = placeholders->Length();
+ if (count <= 0 || count > 9)
+ return v8::Undefined();
+ for (uint32_t i = 0; i < count; ++i) {
+ std::string substitute =
+ *v8::String::Utf8Value(
+ placeholders->Get(v8::Integer::New(i))->ToString());
+ substitutions.push_back(substitute);
+ }
+ } else {
+ NOTREACHED() << "Couldn't parse second parameter.";
+ return v8::Undefined();
+ }
+
+ return v8::String::New(ReplaceStringPlaceholders(
+ message, substitutions, NULL).c_str());
+ }
};
// Convert a ListValue to a vector of V8 values.
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index 1ccbd85..4558eb9 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -15,7 +15,6 @@ var chrome = chrome || {};
native function GetNextRequestId();
native function OpenChannelToTab();
native function GetRenderViewId();
- native function GetL10nMessage();
native function GetPopupParentWindow();
native function GetPopupView();
native function SetExtensionActionIcon();
@@ -427,11 +426,6 @@ var chrome = chrome || {};
return tabIdProxy;
}
- apiFunctions["i18n.getMessage"].handleRequest =
- function(message_name, placeholders) {
- return GetL10nMessage(message_name, placeholders);
- }
-
apiFunctions["experimental.popup.show"].handleRequest =
function(url, showDetails, callback) {
// Second argument is a transform from HTMLElement to Rect.
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index 5e28537..c8d2557 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -17,6 +17,7 @@ var chrome = chrome || {};
native function PortRelease(portId);
native function PostMessage(portId, msg);
native function GetChromeHidden();
+ native function GetL10nMessage();
var chromeHidden = GetChromeHidden();
@@ -201,6 +202,11 @@ var chrome = chrome || {};
return "chrome-extension://" + extensionId + "/" + path;
};
+ chrome.i18n = chrome.i18n || {};
+ chrome.i18n.getMessage = function(message_name, placeholders) {
+ return GetL10nMessage(message_name, placeholders, extensionId);
+ };
+
if (warnOnPrivilegedApiAccess) {
setupApiStubs();
}
@@ -236,13 +242,14 @@ var chrome = chrome || {};
var privileged = [
// Entire namespaces.
"bookmarks", "browserAction", "devtools", "experimental.extension",
- "experimental.history", "experimental.popup", "i18n", "pageAction",
- "pageActions", "tabs", "test", "toolstrip", "windows",
+ "experimental.history", "experimental.popup", "pageAction", "pageActions",
+ "tabs", "test", "toolstrip", "windows",
// Functions/events/properties within the extension namespace.
"extension.getBackgroundPage", "extension.getExtensionTabs",
"extension.getToolstrips", "extension.getViews", "extension.lastError",
- "extension.onConnectExternal", "extension.onRequestExternal"
+ "extension.onConnectExternal", "extension.onRequestExternal",
+ "i18n.getAcceptLanguages"
];
for (var i = 0; i < privileged.length; i++) {
createStub(privileged[i]);
diff --git a/chrome/test/data/extensions/api_test/i18n/a.js b/chrome/test/data/extensions/api_test/i18n/a.js
new file mode 100644
index 0000000..503f8c9
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/i18n/a.js
@@ -0,0 +1 @@
+chrome.extension.sendRequest(chrome.i18n.getMessage("message_with_one_placeholder", "19"));
diff --git a/chrome/test/data/extensions/api_test/i18n/manifest.json b/chrome/test/data/extensions/api_test/i18n/manifest.json
index 16e65ac..e2aa00a 100644
--- a/chrome/test/data/extensions/api_test/i18n/manifest.json
+++ b/chrome/test/data/extensions/api_test/i18n/manifest.json
@@ -3,5 +3,12 @@
"version": "0.1",
"description": "end-to-end browser test for chrome.i18n API",
"background_page": "test.html",
- "default_locale": "en_US"
+ "permissions": ["http://*/*", "tabs"],
+ "default_locale": "en_US",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*"],
+ "js": ["a.js"]
+ }
+ ]
}
diff --git a/chrome/test/data/extensions/api_test/i18n/test.js b/chrome/test/data/extensions/api_test/i18n/test.js
index 405de5b..40807f42 100644
--- a/chrome/test/data/extensions/api_test/i18n/test.js
+++ b/chrome/test/data/extensions/api_test/i18n/test.js
@@ -24,4 +24,16 @@ chrome.test.runTests([
chrome.test.succeed();
},
+ function getMessageFromContentScript() {
+ chrome.extension.onRequest.addListener(
+ function(request, sender, sendResponse) {
+ chrome.test.assertEq(request, "Number of errors: 19");
+ }
+ );
+ chrome.test.log("Creating tab...");
+ chrome.tabs.create({
+ url: "http://localhost:1337/files/extensions/test_file.html"
+ });
+ chrome.test.succeed();
+ }
]);