MessageEventコンストラクタとpostMessageとWeb messagingについて
MessageEventって何?
MessageEvent
というのは、server-sent events, Web sockets, cross-document messaging, channel messaging, broadcast channels などで使われてる、データをやり取りとりするときに使われてるEventの一種です。
よく見かけるのだと、 window.postMessage で渡されるeventは MessageEvent
という型になってます。(WebWorkerとかとやりとりするときもこれですね)
MessageEvent
型のオブジェクトは以下のようなプロパティを持ってます。
data データ本体(仕様ではanyとなってるので配列とかも渡せます) origin データ送信元のオリジン(targetOriginで指定されたやつ) lastEventId このメッセージイベントの ID source データ送信元の window オブジェクト ports MessagePortオブジェクトの配列
DOMを介したりしないのでバブリングはしないですし、キャンセルはできない、又デフォルトアクションがない事が特徴です。
また MessageEvent は DOM の Event interface を継承しているが、バブリングしない、キャンセルできない、デフォルトのアクションを持たないという特徴がある
詳しくは HTML5 Web Message のイントロダクション – Please Sleep を見るといいです。(An Introduction to HTML5 web messaging – Dev.Opera)
また、今回やろうとしてる事と関係ありますが、このEventはスクリプトで作ることができるのでisTrusted属性はfalseとなってます。
そのままだと信用出来ないので、利用するときは、どこからデータが来たのかオリジン示す origin
(postMessageの第二引数で指定するtargetOrigin)やデータ送信元の window オブジェクトを示す source
等をチェックして利用します。
- DOM Event その10 – イベントがブラウザーによって作成されたイベントか、スクリプトによって作成されたイベントか調べる(isTrusted) – Web kledgeb
- 9 Communication — HTML Standard
originとはなにかについてはSame-Origin Policy とは何なのか。 – 葉っぱ日記が詳しいです。
postMessageに興味をもった場合は 第3回 localStorageとpostMessageの使いどころ(2):フロントエンドWeb戦略室|gihyo.jp … 技術評論社 等を読むといいです。
逆に、DOMを関する click
とかよく見るDOMのイベントを自分で作って発火させる場合は、CustomEventというのが最近のブラウザではサポートされているのでこちらを利用できます。
(FYI: document.createEvent)
MessageEventコンストラクタ
Firefox26では、 MessageEvent
を直接作ろうとした時に
void initMessageEvent(in DOMString aTypeArg, in boolean aCanBubbleArg, in boolean aCancelableArg, in DOMString aDataArg, in DOMString aOriginArg, in nsISupports aSourceArg);
という内部仕様がそのまま外に出てしまった感じのひどいAPIではなく、オブジェクトを使ったEventコンストラクタの形式で MessageEvent
を作れるようになっています。
(というかinitMessageEventは廃止されました)
そのため、MessageEvent
を作るときは、MessageEventコンストラクタから作ります。
MessageEventコンストラクタとは名前の通りコンストラクタ関数なので以下のようにnew
したものを、dispatchEvent
で発火させて使います。
var messageEvent = new MessageEvent("event-name", {
data: "data-data",
origin: "watashi-wo-trust-me"
});
document.dispatchEvent(messageEvent);
普通のウェブサイトなら postMessage
で大体必要な事は出来るはずなので、あえて MessageEvent
コンストラクタで作って使うユースケースはイマイチ思いつかないですが、こういうことも出来る的な話として紹介です。
MessageEventを作る
今回使ったテストは以下に置いてあります。
(Firefox26以上、ChromeとWebkitも最近のはサポートされてるっぽいですがいつからかは調べてない)
MessageEventテスト にアクセスすれば実行出来ます。
もう使えない initMessageEvent
を使ってMessageEventイベントを作る場合は以下のようになりますが、これをMessageEventコンストラクタで書きなおしてみます。
// これはもう使えないです!
var messageEvent = document.createEvent("MessageEvent");
messageEvent.initMessageEvent("event-name", false, false,
"data-data",
location.protocol + "//" + location.host,
"", window);
document.dispatchEvent(messageEvent);
dipatchEventでの発火された結果を受ける側は普通に addEventListener
で受け取る事ができます。
document.addEventListener("event-name", function (event) {
// event は MessageEvent型
});
MessageEventコンストラクタ版
Eventコンストラクタを使うと以下のように書けます。
var messageEvent = new MessageEvent("event-name", {
data: "data-data",
origin: location.protocol + "//" + location.host,
source: window
});
document.dispatchEvent(messageEvent);
postMessage版
postMessageを使った場合は以下が大体同様の内容になると思います。
window.postMessage("data-data", location.protocol + "//" + location.host);
自分でMessageEventを作った場合と違って、イベントの名前は決まってるので以下のように受け取ることが出来ます。
window.addEventListener("message", function (event) {
// event は MessageEvent型
}, false);
dispatchEventとpostMessageの違い
上記のMessageEventコンストラクタとpostMessageをみてもらうと殆ど違いがないように見えますが、テストコードであるmessageevent.test.jsを見ると挙動が違う部分があることがわかります。
大きな違いとしてはpostMessageは非同期で発火されるのに対して、MessageEventコンストラクタ+dispatchEventは同期的に発火される点が違います。
postMessage
は非同期で発生する中でもかなり早く発火するので、setImmediate
的なpolyfillに使われてたりすることがあります。
- 非同期パフォーマンス – JavaScriptで遊ぶよ – g:javascript
- MutationObserverを使った高速setImmediate/nextTick – 素人がプログラミングを勉強していたブログ
- NobleJS/setImmediate
それに対して、 dispatchEvent
は同期的に行われるという感じです。
- [DOM4] EventTarget.dispatchEvent() is synchronous, right? from David Flanagan on 2011-09-19 ([email protected] from July to September 2011)
- dispatchEvent(event) (dispatchEvent 日本語)
- JavaScript Tips – dispatchEvent を使いこなそう!! | TM Life
- EventTarget.dispatchEvent – Web API リファレンス | MDN
おわりに
最近ではFirefox26でフラグ付きですが、WebWorker同士のデータのやり取りに使えるMessageChannelが(やっと)実装されたりしたので、postMessage等を使ったWeb messagingは色々と面白い使い方ができると思います。
- MessageChannel – JavaScriptで遊ぶよ – g:javascript
- JavaScript初級者から中級者になろう:十三章第四回 HTML5 Web Messaging 2
- HTML5 / Producer-consumer-problem solved with WebWorkers | softwarechaos
- 677638 – (MessageChannel) Implement HTML5’s channel messaging API
こういうイベントを使ったメッセージのやり取りはブラウザの拡張を書くときによく出てくる概念(特権とコンテンツのやり取り等)だったり、ウェブアプリでも疎結合なものを考えるとこうした仕組みがどこかにでてくるような気がします。
こういうメッセージのやり取りは何かを監視して通知する用途も多そうですが、ES7で予定されてる Object.observe()
とかと合わせると、今色んなライブラリで頑張ってやってるような事がもっとシンプルに出来るようになって面白い感じがします。
- Object.observe-sept2013 by Rafael Weinstein
- Plight Of The Butterfly – Everything You Wanted To Know About Object.observe() – Addy Osmani [Video]
以上、長いですが MessageEvent
についてでした。
お知らせ欄
JavaScript Primerの書籍版がAmazonで購入できます。
JavaScriptに関する最新情報は週一でJSer.infoを更新しています。
GitHub Sponsorsでの支援を募集しています。