2010年 12月 07日

IE で postMessage 的実装をちゃんとやる

postMessage、便利でいいですね。ドメインを跨いでうまく連携してスクリプトを書けたときの楽しさはパズル的でこのうえない感じであります。しかしながら postMessage は古い IE で動かないので、代替実装が必要です。ググるといっぱい出てきはするのですが、自分の要求にかなったものがなかったのでひとまず書きました。

$ cat /etc/hosts
127.0.0.1 anotherhost
$ git clone git://gist.github.com/701752.git gist-701752 
$ cd gist-701752
$ plackup -MPlack::App::Directory -e 'Plack::App::Directory->new({root=>"."})->to_app' -p 5000

とかやっておいて、http://localhost:5000/test.html にアクセスするとなんか出るので、ボタンをクリックしたりするとどうにかなります。フレームの中と外で別ドメインです。

要件

実装するにあたって必要だったのは、まさに postMessage だったので、以下のようなことが求められました。

  • メッセージのとりこぼしがない
  • IE で動く

逆に、古いブラウザで動けばいいので速度はそれほど求められていません。

実装

とりあえず動いてほしいので、古くから伝わり慣れている location.hash によるドメイン間通信を用いることにしました。location.hash による通信を要約すると以下の通りです。

  • データを送信したいフレームの location.hash を書きかえる
    • 書きかえるだけならドメイン関係なくできます
  • 読みだし側は location.hash を監視して変化を検知したら処理する

ただ、これには以下のような問題があります。

  • IE の URL 長制限。
    • 2038 バイト制限が存在します。
  • location.hash を監視する以上の速度でデータを送信することができない
    • 監視に使用するのは setTimeout/setInterval なので、監視間隔は厳密には不定です。

なので、2038バイト制限を超えるデータを送信するためにはデータのパケット化が必要なのと、データの紛失を防ぐために何らかの方法が必要ですが、要はパケロスする環境で信頼できるデータ通信を行いたいということなので TCP のやりかたをパクって実装してあります (シーケンス番号 + ACK)。

備考

window.name を使う方法であればデータ長制限がなくなるのでパフォーマンスは向上しそうですが、実装は複雑そうです。また、空のリソースを両方のドメインに求めるので、失敗確立があがります。

ちゃんとした postMessage 互換実装があったら教えてください……