summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/extension_messages_apitest.cc6
-rwxr-xr-xchrome/chrome.gyp2
-rwxr-xr-xchrome/common/extensions/api/extension_api.json55
-rw-r--r--chrome/common/extensions/docs/extension.html403
-rw-r--r--chrome/common/extensions/docs/tabs.html189
-rw-r--r--chrome/renderer/resources/extension_process_bindings.js12
-rw-r--r--chrome/renderer/resources/renderer_extension_bindings.js58
-rw-r--r--chrome/test/data/extensions/api_test/connect/manifest.json13
-rw-r--r--chrome/test/data/extensions/api_test/connect/page.js10
-rw-r--r--chrome/test/data/extensions/api_test/connect/test.html33
-rw-r--r--chrome/test/data/extensions/api_test/connect_external/connect.html13
-rw-r--r--chrome/test/data/extensions/api_test/connect_external/test.html15
12 files changed, 784 insertions, 25 deletions
diff --git a/chrome/browser/extensions/extension_messages_apitest.cc b/chrome/browser/extensions/extension_messages_apitest.cc
index 5b061af..c4e58cb 100644
--- a/chrome/browser/extensions/extension_messages_apitest.cc
+++ b/chrome/browser/extensions/extension_messages_apitest.cc
@@ -5,6 +5,12 @@
#include "chrome/browser/extensions/extension_apitest.h"
// Tests that message passing between extensions and content scripts works.
+IN_PROC_BROWSER_TEST_F(ExtensionApiTest, Messaging) {
+ StartHTTPServer();
+ ASSERT_TRUE(RunExtensionTest("connect")) << message_;
+}
+
+// Tests that message passing from one extension to another works.
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MessagingExternal) {
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII("..").AppendASCII("good")
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index 43ea689..98ce90b 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -66,6 +66,7 @@
'browser/extensions/extension_apitest.h',
'browser/extensions/extension_bookmarks_apitest.cc',
'browser/extensions/extension_javascript_url_apitest.cc',
+ 'browser/extensions/extension_messages_apitest.cc',
'browser/extensions/extension_browsertest.cc',
'browser/extensions/extension_browsertest.h',
'browser/extensions/extension_browsertests_misc.cc',
@@ -105,6 +106,7 @@
'browser/extensions/extension_apitest.h',
'browser/extensions/extension_bookmarks_apitest.cc',
'browser/extensions/extension_javascript_url_apitest.cc',
+ 'browser/extensions/extension_messages_apitest.cc',
'browser/extensions/extension_browsertest.cc',
'browser/extensions/extension_browsertest.h',
'browser/extensions/extension_browsertests_misc.cc',
diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json
index e629088..ab44b7d 100755
--- a/chrome/common/extensions/api/extension_api.json
+++ b/chrome/common/extensions/api/extension_api.json
@@ -55,6 +55,20 @@
}
},
{
+ "name": "sendRequest",
+ "type": "function",
+ "description": "Sends a single request to other listeners within the extension. Similar to chrome.extension.connect, but only sends a single request with an optional response.",
+ "parameters": [
+ {"type": "string", "name": "extensionId", "optional": true, "description": "The extension ID of the extension you want to connect to. If omitted, default is your own extension."},
+ { "type": "any", "name": "request" },
+ {
+ "type": "function",
+ "name": "responseCallback",
+ "optional": true
+ }
+ ]
+ },
+ {
"name": "getURL",
"type": "function",
"description": "Convert a relative path within an extension install directory to a fully-qualified URL.",
@@ -133,6 +147,24 @@
"parameters": [
{"$ref": "Port", "name": "port"}
]
+ },
+ {
+ "name": "onRequest",
+ "type": "function",
+ "description": "Fired when a request is sent from either an extension process or a content script.",
+ "parameters": [
+ { "type": "any", "name": "request" },
+ { "type": "function", "name": "sendResponse", "description": "Function to call when you have a response. The argument should be any JSON-ifiable object, or undefined if there is no response." }
+ ]
+ },
+ {
+ "name": "onRequestExternal",
+ "type": "function",
+ "description": "Fired when a request is sent from another extension.",
+ "parameters": [
+ { "type": "any", "name": "request" },
+ { "type": "function", "name": "sendResponse", "description": "Function to call when you have a response. The argument should be any JSON-ifiable object, or undefined if there is no response." }
+ ]
}
]
},
@@ -367,7 +399,7 @@
{
"name": "connect",
"type": "function",
- "description": "Connects to the content script(s) in the specified tab. The <a href='extension.html#event-onConnect'>chrome.extensions.onConnect</a> event is fired in each content script running in the specified tab for the current extension. For more details, see <a href='content_scripts.html#messaging'>Content Script Messaging</a>.",
+ "description": "Connects to the content script(s) in the specified tab. The <a href='extension.html#event-onConnect'>chrome.extension.onConnect</a> event is fired in each content script running in the specified tab for the current extension. For more details, see <a href='content_scripts.html#messaging'>Content Script Messaging</a>.",
"parameters": [
{
"type": "integer",
@@ -389,6 +421,27 @@
}
},
{
+ "name": "sendRequest",
+ "type": "function",
+ "description": "Sends a single request to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The <a href='extension.html#event-onRequest'>chrome.extension.onRequest</a> event is fired in each content script running in the specified tab for the current extension.",
+ "parameters": [
+ {
+ "type": "integer",
+ "name": "tabId",
+ "minimum": 0
+ },
+ {
+ "type": "any",
+ "name": "request"
+ },
+ {
+ "type": "function",
+ "name": "responseCallback",
+ "optional": true
+ }
+ ]
+ },
+ {
"name": "getSelected",
"type": "function",
"description": "Gets the tab that is selected in the specified window.",
diff --git a/chrome/common/extensions/docs/extension.html b/chrome/common/extensions/docs/extension.html
index 514c710..af22583 100644
--- a/chrome/common/extensions/docs/extension.html
+++ b/chrome/common/extensions/docs/extension.html
@@ -193,8 +193,10 @@
<a href="#method-getToolstrips">getToolstrips</a>
</li><li jsinstance="4">
<a href="#method-getURL">getURL</a>
- </li><li jsinstance="*5">
+ </li><li jsinstance="5">
<a href="#method-getViews">getViews</a>
+ </li><li jsinstance="*6">
+ <a href="#method-sendRequest">sendRequest</a>
</li>
</ol>
</li>
@@ -203,8 +205,12 @@
<ol>
<li jsinstance="0">
<a href="#event-onConnect">onConnect</a>
- </li><li jsinstance="*1">
+ </li><li jsinstance="1">
<a href="#event-onConnectExternal">onConnectExternal</a>
+ </li><li jsinstance="2">
+ <a href="#event-onRequest">onRequest</a>
+ </li><li jsinstance="*3">
+ <a href="#event-onRequestExternal">onRequestExternal</a>
</li>
</ol>
</li>
@@ -1081,7 +1087,7 @@ For details, see
</div> <!-- /description -->
- </div><div class="apiItem" jsinstance="*5">
+ </div><div class="apiItem" jsinstance="5">
<a name="method-getViews"></a> <!-- method-anchor -->
<h4>getViews</h4>
@@ -1182,6 +1188,185 @@ For details, see
</div> <!-- /description -->
+ </div><div class="apiItem" jsinstance="*6">
+ <a name="method-sendRequest"></a> <!-- method-anchor -->
+ <h4>sendRequest</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.extension.sendRequest</span>(<span jsinstance="0" class="optional"><span style="display: none; ">, </span><span>string</span>
+ <var><span>extensionId</span></var></span><span jsinstance="1" class="null"><span>, </span><span>any</span>
+ <var><span>request</span></var></span><span jsinstance="*2" class="optional"><span>, </span><span>function</span>
+ <var><span>responseCallback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sends a single request to other listeners within the extension. Similar to chrome.extension.connect, but only sends a single request with an optional response.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>extensionId</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</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>The extension ID of the extension you want to connect to. If omitted, default is your own extension.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="1">
+ <div>
+ <dt>
+ <var>request</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>any</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*2">
+ <div>
+ <dt>
+ <var>responseCallback</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>function</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>null</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
</div> <!-- /apiItem -->
</div> <!-- /apiGroup -->
@@ -1256,7 +1441,7 @@ For details, see
</div> <!-- /decription -->
- </div><div class="apiItem" jsinstance="*1">
+ </div><div class="apiItem" jsinstance="1">
<a name="event-onConnectExternal"></a>
<h4>onConnectExternal</h4>
@@ -1320,6 +1505,216 @@ For details, see
</div> <!-- /decription -->
+ </div><div class="apiItem" jsinstance="2">
+ <a name="event-onRequest"></a>
+ <h4>onRequest</h4>
+
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.extension.</span><span>onRequest</span><span class="subdued">.addListener</span>(function(<span>any request, function sendResponse</span>) <span class="subdued">{...}</span>);
+ </div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Fired when a request is sent from either an extension process or a content script.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>request</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>any</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>sendResponse</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>function</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Function to call when you have a response. The argument should be any JSON-ifiable object, or undefined if there is no response.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ </div> <!-- /decription -->
+
+ </div><div class="apiItem" jsinstance="*3">
+ <a name="event-onRequestExternal"></a>
+ <h4>onRequestExternal</h4>
+
+ <div class="summary">
+ <!-- Note: intentionally longer 80 columns -->
+ <span class="subdued">chrome.extension.</span><span>onRequestExternal</span><span class="subdued">.addListener</span>(function(<span>any request, function sendResponse</span>) <span class="subdued">{...}</span>);
+ </div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Fired when a request is sent from another extension.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>request</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>any</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*1">
+ <div>
+ <dt>
+ <var>sendResponse</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>function</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo" style="display: none; ">
+ Undocumented.
+ </dd>
+ <dd>Function to call when you have a response. The argument should be any JSON-ifiable object, or undefined if there is no response.</dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ </div> <!-- /decription -->
+
</div> <!-- /apiItem -->
</div> <!-- /apiGroup -->
diff --git a/chrome/common/extensions/docs/tabs.html b/chrome/common/extensions/docs/tabs.html
index eb765ae..12dbdb9 100644
--- a/chrome/common/extensions/docs/tabs.html
+++ b/chrome/common/extensions/docs/tabs.html
@@ -205,7 +205,9 @@
<a href="#method-move">move</a>
</li><li jsinstance="10">
<a href="#method-remove">remove</a>
- </li><li jsinstance="*11">
+ </li><li jsinstance="11">
+ <a href="#method-sendRequest">sendRequest</a>
+ </li><li jsinstance="*12">
<a href="#method-update">update</a>
</li>
</ol>
@@ -485,7 +487,7 @@ For example:
<div class="description">
<p class="todo" style="display: none; ">Undocumented.</p>
- <p>Connects to the content script(s) in the specified tab. The <a href="extension.html#event-onConnect">chrome.extensions.onConnect</a> event is fired in each content script running in the specified tab for the current extension. For more details, see <a href="content_scripts.html#messaging">Content Script Messaging</a>.</p>
+ <p>Connects to the content script(s) in the specified tab. The <a href="extension.html#event-onConnect">chrome.extension.onConnect</a> event is fired in each content script running in the specified tab for the current extension. For more details, see <a href="content_scripts.html#messaging">Content Script Messaging</a>.</p>
<!-- PARAMETERS -->
<h4>Parameters</h4>
@@ -2755,7 +2757,188 @@ For example:
</div> <!-- /description -->
- </div><div class="apiItem" jsinstance="*11">
+ </div><div class="apiItem" jsinstance="11">
+ <a name="method-sendRequest"></a> <!-- method-anchor -->
+ <h4>sendRequest</h4>
+
+ <div class="summary"><span style="display: none; ">void</span>
+ <!-- Note: intentionally longer 80 columns -->
+ <span>chrome.tabs.sendRequest</span>(<span jsinstance="0" class="null"><span style="display: none; ">, </span><span>integer</span>
+ <var><span>tabId</span></var></span><span jsinstance="1" class="null"><span>, </span><span>any</span>
+ <var><span>request</span></var></span><span jsinstance="*2" class="optional"><span>, </span><span>function</span>
+ <var><span>responseCallback</span></var></span>)</div>
+
+ <div class="description">
+ <p class="todo" style="display: none; ">Undocumented.</p>
+ <p>Sends a single request to the content script(s) in the specified tab, with an optional callback to run when a response is sent back. The <a href="extension.html#event-onRequest">chrome.extension.onRequest</a> event is fired in each content script running in the specified tab for the current extension.</p>
+
+ <!-- PARAMETERS -->
+ <h4>Parameters</h4>
+ <dl>
+ <div jsinstance="0">
+ <div>
+ <dt>
+ <var>tabId</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>integer</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="1">
+ <div>
+ <dt>
+ <var>request</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>any</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div><div jsinstance="*2">
+ <div>
+ <dt>
+ <var>responseCallback</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>function</span>
+ </span>
+ </span>
+ )
+ </div>
+
+ </em>
+ </dt>
+ <dd class="todo">
+ Undocumented.
+ </dd>
+ <dd style="display: none; ">
+ Description of this parameter from the json schema.
+ </dd>
+
+ <!-- OBJECT PROPERTIES -->
+ <dd style="display: none; ">
+ <dl>
+ <div>
+ <div>
+ </div>
+ </div>
+ </dl>
+ </dd>
+ </div>
+ </div>
+ </dl>
+
+ <!-- RETURNS -->
+ <h4 style="display: none; ">Returns</h4>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+
+ <!-- CALLBACK -->
+ <div>
+ <div>
+ <h4>Callback function</h4>
+ <p>
+ If you specify the <em>callback</em> parameter,
+ it should specify a function that looks like this:
+ </p>
+
+ <!-- Note: intentionally longer 80 columns -->
+ <pre>function(<span>null</span>) <span class="subdued">{...}</span>);</pre>
+ <dl>
+ <div style="display: none; ">
+ <div>
+ </div>
+ </div>
+ </dl>
+ </div>
+ </div>
+
+ </div> <!-- /description -->
+
+ </div><div class="apiItem" jsinstance="*12">
<a name="method-update"></a> <!-- method-anchor -->
<h4>update</h4>
diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js
index ab5fee3..3974867 100644
--- a/chrome/renderer/resources/extension_process_bindings.js
+++ b/chrome/renderer/resources/extension_process_bindings.js
@@ -294,6 +294,18 @@ var chrome = chrome || {};
return chromeHidden.Port.createPort(portId, name);
}
+ apiFunctions["tabs.sendRequest"].handleRequest =
+ function(tabId, request, responseCallback) {
+ var port = chrome.tabs.connect(tabId,
+ {name: chromeHidden.kRequestChannel});
+ port.postMessage(request);
+ port.onMessage.addListener(function(response) {
+ if (responseCallback)
+ responseCallback(response);
+ port.disconnect();
+ });
+ }
+
apiFunctions["extension.getViews"].handleRequest = function() {
return GetExtensionViews(-1, "ALL");
}
diff --git a/chrome/renderer/resources/renderer_extension_bindings.js b/chrome/renderer/resources/renderer_extension_bindings.js
index 09021540..bf288bd 100644
--- a/chrome/renderer/resources/renderer_extension_bindings.js
+++ b/chrome/renderer/resources/renderer_extension_bindings.js
@@ -20,9 +20,16 @@ var chrome = chrome || {};
var chromeHidden = GetChromeHidden();
+ // The reserved channel name for the sendRequest API.
+ chromeHidden.kRequestChannel = "chrome.extension.sendRequest";
+
// Map of port IDs to port object.
var ports = {};
+ // Change even to odd and vice versa, to get the other side of a given
+ // channel.
+ function getOppositePortId(portId) { return portId ^ 1; }
+
// Port object. Represents a connection to another script context through
// which messages can be passed.
chrome.Port = function(portId, opt_name) {
@@ -59,11 +66,30 @@ var chrome = chrome || {};
// close both.
if (targetExtensionId != chromeHidden.extensionId)
return; // not for us
+ if (ports[getOppositePortId(portId)])
+ return; // this channel was opened by us, so ignore it
+
+ // Determine whether this is coming from another extension, so we can use
+ // the right event.
+ var isExternal = sourceExtensionId != chromeHidden.extensionId;
+
+ // Special case for sendRequest/onRequest.
+ if (channelName == chromeHidden.kRequestChannel) {
+ var requestEvent = (isExternal ?
+ chrome.extension.onRequestExternal : chrome.extension.onRequest);
+ if (requestEvent.hasListeners()) {
+ var port = chromeHidden.Port.createPort(portId, channelName);
+ port.onMessage.addListener(function(request) {
+ requestEvent.dispatch(request, function(response) {
+ port.postMessage(response);
+ });
+ });
+ }
+ return;
+ }
- // Determine whether this is coming from another extension, and use the
- // right event.
- var connectEvent = (sourceExtensionId == chromeHidden.extensionId ?
- chrome.extension.onConnect : chrome.extension.onConnectExternal);
+ var connectEvent = (isExternal ?
+ chrome.extension.onConnectExternal : chrome.extension.onConnect);
if (connectEvent.hasListeners()) {
var port = chromeHidden.Port.createPort(portId, channelName);
if (tab) {
@@ -72,6 +98,7 @@ var chrome = chrome || {};
port.sender = {tab: tab, id: sourceExtensionId};
// TODO(EXTENSIONS_DEPRECATED): port.tab is obsolete.
port.tab = port.sender.tab;
+
connectEvent.dispatch(port);
}
};
@@ -126,6 +153,8 @@ var chrome = chrome || {};
// Events for when a message channel is opened to our extension.
chrome.extension.onConnect = new chrome.Event();
chrome.extension.onConnectExternal = new chrome.Event();
+ chrome.extension.onRequest = new chrome.Event();
+ chrome.extension.onRequestExternal = new chrome.Event();
// Opens a message channel to the given target extension, or the current one
// if unspecified. Returns a Port for message passing.
@@ -144,6 +173,27 @@ var chrome = chrome || {};
throw new Error("Error connecting to extension '" + targetId + "'");
};
+ chrome.extension.sendRequest =
+ function(targetId_opt, request, responseCallback_opt) {
+ var targetId = extensionId;
+ var responseCallback = null;
+ var lastArg = arguments.length - 1;
+ if (typeof(arguments[lastArg]) == "function")
+ responseCallback = arguments[lastArg--];
+ request = arguments[lastArg--];
+ if (lastArg >= 0)
+ targetId = arguments[lastArg--];
+
+ var port = chrome.extension.connect(targetId,
+ {name: chromeHidden.kRequestChannel});
+ port.postMessage(request);
+ port.onMessage.addListener(function(response) {
+ if (responseCallback)
+ responseCallback(response);
+ port.disconnect();
+ });
+ };
+
// Returns a resource URL that can be used to fetch a resource from this
// extension.
chrome.extension.getURL = function(path) {
diff --git a/chrome/test/data/extensions/api_test/connect/manifest.json b/chrome/test/data/extensions/api_test/connect/manifest.json
new file mode 100644
index 0000000..27e16d3
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/connect/manifest.json
@@ -0,0 +1,13 @@
+{
+ "name": "connect_external",
+ "version": "1.0",
+ "description": "Tests connect from a content script.",
+ "permissions": ["tabs"],
+ "background_page": "test.html",
+ "content_scripts": [
+ {
+ "matches": ["http://*/*"],
+ "js": ["page.js"]
+ }
+ ]
+}
diff --git a/chrome/test/data/extensions/api_test/connect/page.js b/chrome/test/data/extensions/api_test/connect/page.js
new file mode 100644
index 0000000..d70f415
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/connect/page.js
@@ -0,0 +1,10 @@
+// For test onRequest.
+chrome.extension.sendRequest({step: 1}, function(response) {
+ if (response.nextStep)
+ chrome.extension.sendRequest({step: 2});
+});
+
+// For test sendRequest.
+chrome.extension.onRequest.addListener(function(request, sendResponse) {
+ sendResponse({success: (request.step2 == 1)});
+});
diff --git a/chrome/test/data/extensions/api_test/connect/test.html b/chrome/test/data/extensions/api_test/connect/test.html
new file mode 100644
index 0000000..31868b4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/connect/test.html
@@ -0,0 +1,33 @@
+<script>
+chrome.test.runTests([
+ // Tests receiving a request from a content script and responding.
+ function onRequest() {
+ chrome.extension.onRequest.addListener(function(request, sendResponse) {
+ if (request.step == 1) {
+ // Step 1: Page should send another request for step 2.
+ sendResponse({nextStep: true});
+ } else {
+ // Step 2.
+ chrome.test.assertEq(request.step, 2);
+ sendResponse({});
+ chrome.test.succeed();
+ }
+ });
+ },
+ // Tests sending a request to a tab and receiving a response.
+ function sendRequest() {
+ chrome.tabs.getSelected(null, function(tab) {
+ chrome.test.log('Selected tab: ' + tab.url);
+ chrome.tabs.sendRequest(tab.id, {step2: 1}, function(response) {
+ chrome.test.assertTrue(response.success);
+ chrome.test.succeed();
+ });
+ });
+ }
+]);
+
+chrome.test.log("Creating tab...");
+chrome.tabs.create({
+ url: "http://localhost:1337/files/extensions/test_file.html"
+});
+</script>
diff --git a/chrome/test/data/extensions/api_test/connect_external/connect.html b/chrome/test/data/extensions/api_test/connect_external/connect.html
deleted file mode 100644
index cc9def8..0000000
--- a/chrome/test/data/extensions/api_test/connect_external/connect.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<script>
-var testId = "bjafgdebaacbbbecmhlhpofkepfkgcpa";
-
-function testConnectExternal() {
- var port = chrome.extension.connect(testId, {name: "extern"});
- port.postMessage({testConnectExternal: true});
- port.onMessage.addListener(function(msg) {
- var success = msg.success && msg.senderId == location.host;
- window.domAutomationController.send(success);
- port.disconnect();
- });
-}
-</script>
diff --git a/chrome/test/data/extensions/api_test/connect_external/test.html b/chrome/test/data/extensions/api_test/connect_external/test.html
new file mode 100644
index 0000000..cc9cfa4
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/connect_external/test.html
@@ -0,0 +1,15 @@
+<script>
+var testId = "bjafgdebaacbbbecmhlhpofkepfkgcpa";
+
+chrome.test.runTests([
+ function connectExternal() {
+ var port = chrome.extension.connect(testId, {name: "extern"});
+ port.postMessage({testConnectExternal: true});
+ port.onMessage.addListener(chrome.test.callbackPass(function(msg) {
+ chrome.test.assertTrue(msg.success, "Message failed.");
+ chrome.test.assertEq(msg.senderId, location.host,
+ "Sender ID doesn't match.");
+ }));
+ }
+]);
+</script>