HTML5から、ウィンドウ(フレーム)間でメッセージの送受信を行うための仕組みが用意された。この仕組みを用いると、対象となるウィンドウのインスタンスさえ手に入れば、同じオリジン(プロトコル+ドメイン + ポート番号)のWebページはもちろん、違うオリジンのWebページとも通信を行うことが可能だ。

まず、他のウィンドウから送られてきたメッセージを受信するには、windowオブジェクトのmessageイベントを監視する必要がある。

// messageイベントの監視
window.addEventListener("message", function() {...}, false);

他のウィンドウに対してメッセージを送信する場合は、window.postMessage()メソッドを使用する。

postMessage(data, "targetOrigin");

postMessage()の第1引数は送信するメッセージ本文で、任意のJavaScriptオブジェクトを指定することができる。

第2引数は対象となるウィンドウのオリジンURLを文字列で指定する(例: "http://localhost:8080/")。これが実際のウィンドウのオリジンと一致していなければ、メッセージの送信に失敗する。ここには「すべてのサイト」を表す"*"(アスタリスク)を指定する事も可能だが、悪意のあるWebサイトに対して不用意にメッセージを送信してしまわないよう、既知のURLをできる限り指定したほうがよい。

以上の知識があれば、ウィンドウ間でメッセージを送受信することができる。実際のサンプルを見てみよう。

サンプル

このサンプルは、iframe内のページとメッセージの送受信を行うものだ。

  1. 画面を表示するとiframe内のドキュメントに対してメッセージを送信する。
  2. iframe内のページはメッセージを受け取ると受け取ったメッセージをbody内に表示した後、メッセージをメインページに返信する。

メッセージ受信

  1. メインページは受け取ったメッセージをアラートで表示する。

メッセージをアラートで表示

また、ページ自体はlocalhostの80番ポートで動作するWebサーバに配置したが、iframe内のページは「http://localhost:8080」がオリジンとなっており、クロスオリジンのデータ送受信を実現している。

サンプルのソースコードを以下に示す。まずはメインページだ。

main.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
// (1) messageイベントを監視
window.addEventListener("message", function(ev) {
  // (2) 既知のオリジンからのメッセージ以外は無視
  if (ev.origin != "http://localhost:8080") {
    return;
  }
  // (3) データの表示
  alert(ev.origin + "からのメッセージを受信しました:\n「" + ev.data + "」");
}, false);

function hello() {
  var iframe = window.frames[0];
  // (4) メッセージの送信
  iframe.postMessage("こんにちは", "http://localhost:8080/");
}
</script>
</head>
<body>
<h1>クロスドキュメントメッセージングのテスト</h1>
<iframe width="400" src="http://localhost:8080/frame.html" onload="hello()">
</iframe>
</body>
</html>

次に示すのはiframe内に表示するページのソースコードだ。

frame.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <script type="text/javascript">
    window.addEventListener("message", function(ev) {
        if (ev.origin != "http://localhost") {
            alert(ev.origin);
            return;
        }
        document.body.innerHTML = ev.origin + "からのメッセージを受信しました:<br>「" + ev.data + "」";
        // (5) メインページに対してメッセージ送信
        ev.source.postMessage("こんにちは。こちらは" + this.location + "です。", ev.origin);
    }, false);
    </script>
</head>
<body></body>
</html>

このサンプルにおけるポイントを以下に示す。

(1) windowオブジェクトのmessageイベントを監視することで、メッセージの受信を行える。

(2) messageイベントのoriginプロパティにアクセスすると、メッセージ送信元のオリジンを取得することができる(この例では「http://localhost:8080」が返る)。悪意のあるページからのメッセージを処理しないためにも、オリジンのチェックは必須だ。

(3) messageイベントのdataプロパティにアクセスすると、メッセージ本文(任意のJavaScriptオブジェクト)を取得することができる。

(4) メッセージの送信にはpostMessage()を使用する。

(5) messageイベントのsourceプロパティにアクセスすると、メッセージ送信元のwindowオブジェクト(正確にはWindowProxy)を取得することができる。

このサンプルは、Firefox3.5、Safari4、Chrome3、Opera10での動作を確認した。