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 等をチェックして利用します。

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に使われてたりすることがあります。

それに対して、 dispatchEvent は同期的に行われるという感じです。

おわりに

最近ではFirefox26でフラグ付きですが、WebWorker同士のデータのやり取りに使えるMessageChannelが(やっと)実装されたりしたので、postMessage等を使ったWeb messagingは色々と面白い使い方ができると思います。

こういうイベントを使ったメッセージのやり取りはブラウザの拡張を書くときによく出てくる概念(特権とコンテンツのやり取り等)だったり、ウェブアプリでも疎結合なものを考えるとこうした仕組みがどこかにでてくるような気がします。

こういうメッセージのやり取りは何かを監視して通知する用途も多そうですが、ES7で予定されてる Object.observe() とかと合わせると、今色んなライブラリで頑張ってやってるような事がもっとシンプルに出来るようになって面白い感じがします。

以上、長いですが MessageEvent についてでした。