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
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
|
---
title: runtime.onMessage
slug: Mozilla/Add-ons/WebExtensions/API/runtime/onMessage
tags:
- API
- Add-ons
- Event
- Extensions
- Non-standard
- Reference
- WebExtensions
- onmessage
- runtime
translation_of: Mozilla/Add-ons/WebExtensions/API/runtime/onMessage
---
<div>{{AddonSidebar()}}</div>
<div>このイベントを使って、拡張機能の別の部品からのメッセージを受け取ることができます。例えば、次のような場面で使います。</div>
<div></div>
<ul>
<li><a href="/ja/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Content_scripts">コンテンツスクリプト</a>の中で、 <a href="/ja/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Background_scripts">バックグラウンドスクリプト</a>からのメッセージを受け取る。</li>
<li>バックグラウントスクリプトの中で、コンテンツスクリプトからのメッセージを受け取る。</li>
<li><a href="/ja/Add-ons/WebExtensions/Anatomy_of_a_WebExtension#Options_pages">オプションページ</a>や<a href="/ja/Add-ons/WebExtensions/User_interface_components#Popups">ポップアップ</a>のスクリプトの中で、バックグラウンドスクリプトからのメッセージを受け取る。</li>
<li>バックグラウンドスクリプトの中で、オプションページやポップアップのスクリプトからのメッセージを受け取る。</li>
</ul>
<p><code>onMessage</code> リスナーに受信させるメッセージを送るには、{{WebExtAPIRef("runtime.sendMessage()")}}、または (コンテンツスクリプトにメッセージを送るときは) {{WebExtAPIRef("tabs.sendMessage()")}} を使います。</p>
<div class="blockIndicator note">
<p>同じ種類のメッセージに対する <code>onMessage</code> リスナーを複数作ることは避けてください。複数のリスナーが実行される順番は保証されていないからです。特定のリスナーへのメッセージ伝送を保証したいときは、<a href="/ja/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Connection-based_messaging">コネクションベースのメッセージ</a> を使ってください。</p>
</div>
<p>メッセージ本体の他に、リスナーは次のものを受け取ります。</p>
<ul>
<li><code>sender</code> オブジェクト。メッセージ送信側の詳細情報です。</li>
<li><code>sendResponse</code> 関数。送信側への返信を送るために使います。</li>
</ul>
<p>メッセージに対して同期的に返信するには、<code>sendResponse</code> 関数をリスナーの中で実行します。<a href="/ja/Add-ons/WebExtensions/API/runtime/onMessage#Sending_a_synchronous_response">例を参照してください</a>。</p>
<p>非同期的に返信するには、二つの方法があります。</p>
<ul>
<li>イベントリスナーから <code>true</code> を返す。こうすることで、リスナーから復帰した後でも <code>sendResponse</code> 関数が有効なままになるため、後で実行することができます。<a href="/ja/Add-ons/WebExtensions/API/runtime/onMessage#Sending_an_asynchronous_response_using_sendResponse">例を参照してください</a>。</li>
<li>イベントリスナーから <code>Promise</code> を返して、返信が準備できた後にそれを解決する (またはエラーの場合は拒否する)。<a href="/ja/Add-ons/WebExtensions/API/runtime/onMessage#Sending_an_asynchronous_response_using_a_Promise">例を参照してください</a>。</li>
</ul>
<div class="warning">
<p><code>Promise</code> を返すほうがより望ましい方法です。<code>sendResponse</code> は <a href="https://github.com/mozilla/webextension-polyfill/issues/16#issuecomment-296693219">W3C 仕様から削除される予定です</a>。 人気のある <a href="https://github.com/mozilla/webextension-polyfill">webextension-polyfill</a> ライブラリーは、すでに <code>sendResponse</code> 関数を実装から削除しました。</p>
</div>
<div class="blockIndicator note">
<p>また、<a href="/ja/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#Connection-based_messaging">コネクションベースのメッセージ</a>を使うこともできます。</p>
</div>
<h2 id="構文">構文</h2>
<pre class="syntaxbox brush:js">browser.runtime.onMessage.addListener(listener)
browser.runtime.onMessage.removeListener(listener)
browser.runtime.onMessage.hasListener(listener)
</pre>
<p>イベントには 3 つの関数があります。</p>
<dl>
<dt><code>addListener(callback)</code></dt>
<dd>リスナーをこのイベントに追加する。</dd>
<dt><code>removeListener(listener)</code></dt>
<dd>このイベントの受け取りを中止する。<code>listener</code> 引数は削除するリスナーです。</dd>
<dt><code>hasListener(listener)</code></dt>
<dd><code>listener</code> がこのイベントに登録されているかどうかを確認する。登録されている場合は <code>true</code> を、そうでない場合は <code>false</code> を返す。</dd>
</dl>
<h2 id="addListener_の構文">addListener の構文</h2>
<h3 id="引数">引数</h3>
<dl>
<dt><code>function</code></dt>
<dd>
<p>このイベントが発生したときに実行されるリスナー関数。関数には次の引数が渡される。</p>
<dl class="reference-values">
<dt><code>message</code></dt>
<dd><code>object</code> 型。メッセージ本体。これは JSON 化できるオブジェクトです。</dd>
</dl>
<dl class="reference-values">
<dt><code>sender</code></dt>
<dd>{{WebExtAPIRef('runtime.MessageSender')}} オブジェクト。メッセージの送信側を表します。</dd>
</dl>
<dl class="reference-values">
<dt><code>sendResponse</code></dt>
<dd>
<p>メッセージに対する返信を送るために、最大で一回実行できる関数。この関数は引数を一つ受け取り、それは JSON 化できるオブジェクトのはずです。その引数はメッセージ送信側に返送されます。</p>
<p>同じドキュメント中に <code>onMessage</code> リスナーが一つ以上ある場合、返信を返すことができるのは一つだけです。</p>
<p>同期的に返信するには、リスナー関数が復帰する前に <code>sendResponse</code> を実行します。非同期的に返信するには、次のどちらかを実行します。</p>
<ul>
<li><code>sendResponse</code> に対する参照を保持したままリスナー関数から <code>true</code> を返す。そうすると、リスナー関数から復帰した後でも <code>sendResponse</code> を実行できます。</li>
<li>リスナー関数から <code><a href="/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></code> を返して、返信の準備ができたときにその Promise を解決する。こちらがより好ましい方法です。</li>
</ul>
</dd>
</dl>
<p>リスナー関数は、Boolean か <code><a href="/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></code> のいずれかを返します。</p>
<div class="blockIndicator warning">
<p><code>addListener</code> を次のような <code>async</code> 関数を使って実行しないでください。</p>
<pre><code>browser.runtime.onMessage.addListener(async (data, sender) => {
if (data.type === 'handle_me') return 'done';
});
</code></pre>
<p>このようなリスナーは全ての受け取ったメッセージを消費するため、実際には他のリスナーがメッセージを受信したり処理することを妨げてしまいます。</p>
<p>非同期的な実装を使いたい場合は、次のように Promise を使ってください。</p>
<pre><code>browser.runtime.onMessage.addListener((data, sender) => {
if (data.type === 'handle_me') return Promise.resolve('done');
});
</code></pre>
</div>
</dd>
</dl>
<h2 id="ブラウザー実装状況">ブラウザー実装状況</h2>
<p>{{Compat("webextensions.api.runtime.onMessage")}}</p>
<h2 id="使用例">使用例</h2>
<h3 id="単純な使用例">単純な使用例</h3>
<p>次のコンテンツスクリプトは、ウェブページ上のクリックイベントを待ち受けます。リンクがクリックされた場合、対象の URL をバックグラウンドページにメッセージ送信します。</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// content-script.js</span>
window<span class="punctuation token">.</span><span class="function token">addEventListener</span><span class="punctuation token">(</span><span class="string token">"click"</span><span class="punctuation token">,</span> notifyExtension<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">function</span> <span class="function token">notifyExtension</span><span class="punctuation token">(</span>e<span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">if</span> <span class="punctuation token">(</span>e<span class="punctuation token">.</span>target<span class="punctuation token">.</span>tagName <span class="operator token">!=</span> <span class="string token">"A"</span><span class="punctuation token">)</span> <span class="punctuation token">{</span>
<span class="keyword token">return</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span>
browser<span class="punctuation token">.</span>runtime<span class="punctuation token">.</span><span class="function token">sendMessage</span><span class="punctuation token">(</span><span class="punctuation token">{</span><span class="string token">"url"</span><span class="punctuation token">:</span> e<span class="punctuation token">.</span>target<span class="punctuation token">.</span>href<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;
}</span></code>
</pre>
<p>バックグラウンドスクリプトはこのメッセージが送信されるまで待ち、<code><a href="/ja/docs/Mozilla/Add-ons/WebExtensions/API/notifications">notifications</a></code> API を使って通知を表示します。</p>
<pre class="brush: js line-numbers language-js"><code class="language-js"><span class="comment token">// background-script.js</span>
browser<span class="punctuation token">.</span>runtime<span class="punctuation token">.</span>onMessage<span class="punctuation token">.</span><span class="function token">addListener</span><span class="punctuation token">(</span>notify<span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="keyword token">function</span> <span class="function token">notify</span><span class="punctuation token">(</span>message<span class="punctuation token">)</span> <span class="punctuation token">{</span>
browser<span class="punctuation token">.</span>notifications<span class="punctuation token">.</span><span class="function token">create</span><span class="punctuation token">(</span><span class="punctuation token">{</span>
<span class="string token">"type"</span><span class="punctuation token">:</span> <span class="string token">"basic"</span><span class="punctuation token">,</span>
<span class="string token">"iconUrl"</span><span class="punctuation token">:</span> browser<span class="punctuation token">.</span>extension<span class="punctuation token">.</span><span class="function token">getURL</span><span class="punctuation token">(</span><span class="string token">"link.png"</span><span class="punctuation token">)</span><span class="punctuation token">,</span>
<span class="string token">"title"</span><span class="punctuation token">:</span> <span class="string token">"リンクをクリックしました!"</span><span class="punctuation token">,</span>
<span class="string token">"message"</span><span class="punctuation token">:</span> message<span class="punctuation token">.</span>url
<span class="punctuation token">}</span><span class="punctuation token">)</span><span class="punctuation token">;</span>
<span class="punctuation token">}</span></code></pre>
<h3 id="同期的に返信する">同期的に返信する</h3>
<p>次のコンテンツスクリプトは、ユーザーがページ上をクリックしたとき、バックグラウンドスクリプトにメッセージを送信します。また、バックグラウンドスクリプトから送信された応答があればログ出力します。</p>
<pre class="brush: js">// content-script.js
function handleResponse(message) {
console.log(`バックグラウンドスクリプトが応答しました: ${message.response}`);
}
function handleError(error) {
console.log(`Error: ${error}`);
}
function sendMessage(e) {
var sending = browser.runtime.sendMessage({content: "コンテンツスクリプトからのメッセージです"});
sending.then(handleResponse, handleError);
}
window.addEventListener("click", sendMessage);</pre>
<p>これが対応するバックグラウンドスクリプトで、リスナー内部から同期的に応答を返します。</p>
<pre class="brush: js">// background-script.js
function handleMessage(request, sender, sendResponse) {
console.log(`コンテンツスクリプトがメッセージを送信しました: ${request.content}`);
sendResponse({response: "バックグラウンドスクリプトからの応答です"});
}
browser.runtime.onMessage.addListener(handleMessage);</pre>
<p>これは同期的に応答を返す別の方法で、Promise.resolve() を使うものです。</p>
<pre class="brush: js">// background-script.js
function handleMessage(request, sender, sendResponse) {
console.log(`コンテンツスクリプトがメッセージを送信しました: ${request.content}`);
return Promise.resolve({response: "バックグラウンドスクリプトからの応答です"});
}
browser.runtime.onMessage.addListener(handleMessage);</pre>
<h3 id="非同期的な返信を_sendResponse_により行う">非同期的な返信を sendResponse により行う</h3>
<p>次は直前の例のバックグラウンドスクリプトの別バージョンです。これは、リスナーが復帰した後、非同期的に返信を送ります。リスナーの中の <code>return true;</code> に注目してください。このようにすることで、リスナーが復帰した後に <code>sendResponse</code> 引数を使う意図があることをブラウザーに伝えています。</p>
<pre class="brush: js">// background-script.js
function handleMessage(request, sender, sendResponse) {
console.log(`コンテンツスクリプトがメッセージを送信しました: ${request.content}`);
setTimeout(() => {
sendResponse({response: "非同期的なバックグラウンドスクリプトからの応答です"});
}, 1000);
return true;
}
browser.runtime.onMessage.addListener(handleMessage);
</pre>
<h3 id="非同期的な返信を_Promise_により行う">非同期的な返信を Promise により行う</h3>
<p>次のコンテンツスクリプトは、まずページ上の <a> リンクを取得し、そしてそのリンクの場所がブックマークされているかどうかを尋ねるメッセージを送信します。このスクリプトは、その場所がブックマークされている場合は <code>true</code> を、そうでない場合は <code>false</code> というような、Boolean 型の応答が返ってくることを想定しています。</p>
<pre class="brush: js">// content-script.js
const firstLink = document.querySelector("a");
function handleResponse(isBookmarked) {
if (isBookmarked) {
firstLink.classList.add("bookmarked");
}
}
browser.runtime.sendMessage({
url: firstLink.href
}).then(handleResponse);</pre>
<p>これが対応するバックグラウンドスクリプトです。<code>{{WebExtAPIRef("bookmarks.search()")}}</code> を使うことで、リンクがブックマークされているかを確認する <code><a href="/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise">Promise</a></code> を返します。</p>
<pre class="brush: js">// background-script.js
function isBookmarked(message, sender, response) {
return browser.bookmarks.search({
url: message.url
}).then(function(results) {
return results.length > 0;
});
}
browser.runtime.onMessage.addListener(isBookmarked);</pre>
<p>非同期的なハンドラーが Promise を返さない場合、明示的に Promise を作ることができます。これは少し不自然な例ですが、<code><a href="/ja/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout">Window.setTimeout()</a></code> を使って 1 秒の遅延を発生させた後に応答を返します。</p>
<pre class="brush: js">// background-script.js
function handleMessage(request, sender, sendResponse) {
return new Promise(resolve => {
setTimeout(() => {
resolve({response: "非同期的なバックグラウンドスクリプトからの応答です"});
}, 1000);
});
}
browser.runtime.onMessage.addListener(handleMessage);</pre>
<p>{{WebExtExamples}}</p>
<div class="note"><strong>謝辞</strong>
<p>この API は Chromium の <a href="https://developer.chrome.com/extensions/runtime#event-onMessage"><code>chrome.runtime</code></a> API. このドキュメントは <a href="https://chromium.googlesource.com/chromium/src/+/master/extensions/common/api/runtime.json"><code>runtime.json</code></a> における Chromium のコードに基づいています。</p>
<p>Microsoft Edge での実装状況は Microsoft Corporation から提供されたものであり、ここでは Creative Commons Attribution 3.0 United States License に従っています。</p>
</div>
<div class="hidden">
<pre>// Copyright 2015 The Chromium Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
</pre>
</div>
|