summaryrefslogtreecommitdiffstats
path: root/chrome/common/extensions/docs/static/messaging.html
blob: 2c4c02981e342bbb64461fcd98a154c48d4f4b0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
<div id="pageData-name" class="pageData">Message Passing</div>
<div id="pageData-showTOC" class="pageData">true</div>

<p>
Since content scripts run in the context of a web page and not the extension,
they often need some way of communicating with the rest of the extension. For
example, an RSS reader extension might use content scripts to detect the
presence of an RSS feed on a page, then notify the background page in order to
display a page action icon for that page.

<p>
Communication between extensions and their content scripts works by using
message passing. Either side can listen for messages sent from the other end,
and respond on the same channel. A message can contain any valid JSON object
(null, boolean, number, string, array, or object). There is a simple API for
<a href="#simple">one-time requests</a>
and a more complex API that allows you to have
<a href="#connect">long-lived connections</a>
for exchanging multiple messages with a shared context. It is also possible to
send a message to another extension if you know its ID, which is covered in
the
<a href="#external">cross-extension messages</a>
section.


<h2 id="simple">Simple one-time requests</h2>
<p>
If you only need to send a single message to another part of your extension
(and optionally get a response back), you should use the simplified
<a href="extension.html#method-sendRequest">chrome.extension.sendRequest()</a>
or
<a href="tabs.html#method-sendRequest">chrome.tabs.sendRequest()</a>
methods. This lets you send a one-time JSON-serializable message from a
content script to extension, or vice versa, respectively. An optional
callback parameter allows you handle the response from the other side, if
there is one.

<p>
Sending a request from a content script looks like this:
<pre>
contentscript.js
================
chrome.extension.sendRequest({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});
</pre>

<p>
Sending a request from the extension to a content script looks very similar,
except that you need to specify which tab to send it to. This example
demonstrates sending a message to the content script in the selected tab.
<pre>
background.html
===============
chrome.tabs.getSelected(null, function(tab) {
  chrome.tabs.sendRequest(tab.id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});
</pre>

<p>
On the receiving end, you need to set up an
<a href="extension.html#event-onRequest">chrome.extension.onRequest</a>
event listener to handle the message. This looks the same from a content
script or extension page. The request will remain open until you call
sendResponse, so it is good practice to call sendResponse with an empty
object to allow the request to be cleaned up.
<pre>
chrome.extension.onRequest.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting == "hello")
      sendResponse({farewell: "goodbye"});
    else
      sendResponse({}); // snub them.
  });
</pre>


<h2 id="connect">Long-lived connections</h2>
<p>
Sometimes it's useful to have a conversation that lasts longer than a single
request and response. In this case, you can open a long-lived channel from
your content script to an extension page, or vice versa, using
<a href="extension.html#method-connect">chrome.extension.connect()</a>
or
<a href="tabs.html#method-connect">chrome.tabs.connect()</a> respectively. The
channel can optionally have a name, allowing you to distinguish between
different types of connections.

<p>
One use case might be an automatic form fill extension. The content script
could open a channel to the extension page for a particular login, and send a
message to the extension for each input element on the page to request the
form data to fill in. The shared connection allows the extension to keep
shared state linking the several messages coming from the content script.

<p>
When establishing a connection, each end is given a
<a href="extension.html#type-Port">Port</a>
object which is used for sending and receiving messages through that
connection.

<p>
Here is how you open a channel from a content script, and send and listen for
messages:
<pre>
contentscript.js
================
var port = chrome.extension.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
  if (msg.question == "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question == "Madame who?")
    port.postMessage({answer: "Madame... Bovary");
});
</pre>

<p>
Sending a request from the extension to a content script looks very similar,
except that you need to specify which tab to connect to. Simply replace the
call to connect in the above example with
<a href="tabs.html#method-connect">chrome.tabs.connect(tabId, {name:
"knockknock"})</a>.

<p>
In order to handle incoming connections, you need to set up a
<a href="extension.html#event-onConnect">chrome.extension.onConnect</a>
event listener. This looks the same from a content script or an extension
page. When another part of your extension calls "connect()", this event is
fired, along with the
<a href="extension.html#type-Port">Port</a>
object you can use to send and receive messages through the connection. Here's
what it looks like to respond to incoming connections:
<pre>
chrome.extension.onConnect.addListener(function(port) {
  console.assert(port.name == "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke == "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer == "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer == "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});
</pre>

<p>
You may want to find out when a connection is closed, for example if you are
maintaining separate state for each open port. For this you can listen to the
<a href="extension.html#type-Port">Port.onDisconnect</a>
event. This event is fired either when the other side of the channel manually
calls
<a href="extension.html#type-Port">Port.disconnect()</a>, or when the page
containing the port is unloaded (for example if the tab is navigated).
onDisconnect is guaranteed to be fired only once for any given port.


<h2 id="external">Cross-extension messaging</h2>
<p>
In addition to sending messages between different components in your
extension, you can use the messaging API to communicate with other extensions.
This lets you expose a public API that other extensions can take advantage of.

<p>
Listening for incoming requests and connections is similar to the internal
case, except you use the
<a href="extension.html#event-onRequestExternal">chrome.extension.onRequestExternal</a>
or
<a href="extension.html#event-onConnectExternal">chrome.extension.onConnectExternal</a>
methods. Here's an example of each:
<pre>
// For simple requests:
chrome.extension.onRequestExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id == blacklistedExtension)
      sendResponse({});  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.extension.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});
</pre>

<p>
Likewise, sending a message to another extension is similar to sending one
within your extension. The only difference is that you must pass the ID of the
extension you want to communicate with. For example:
<pre>
// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.extension.sendRequest(laserExtensionId, {getTargetData: true},
  function(response) {
    if (targetInRange(response.targetData))
      chrome.extension.sendRequest(laserExtensionId, {activateLasers: true});
  });

// Start a long-running conversation:
var port = chrome.extension.connectExternal(laserExtensionId);
port.postMessage(...);
</pre>

<h2 id="security-considerations">Security considerations</h2>

<p>
When receiving a message from a content script or another extension, your
background page should be careful not to fall victim to <a
href="http://en.wikipedia.org/wiki/Cross-site_scripting">cross-site
scripting</a>.  Specifically, avoid using dangerous APIs such as the
below:
</p>
<pre>background.html
===============
chrome.tabs.sendRequest(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating an evil script!
  var resp = eval("(" + response.farewell + ")");
});

background.html
===============
chrome.tabs.sendRequest(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});
</pre>
<p>
Instead, prefer safer APIs that do not run scripts:
</p>
<pre>background.html
===============
chrome.tabs.sendRequest(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});

background.html
===============
chrome.tabs.sendRequest(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});
</pre>