困ったときのメモ ver.2.0

主に Ruby on Rails のメモ。など。

postMessageておもしろいスな。

なぜにこんなことを。

クロスドメインAjax的なことをしようとしたら、怒られまくって困った。
いろいろ調べてみたら、postMessageがスマートで面白いのでメモ。
JSONPでもできなくはなかったけど、イマイチ面倒なのと面白くなかったので。

ちなみに、これを調べたきっかけはHerokuとAmazonのS3でやり取りするため。

はじめに

postMessageは、安全にクロスドメイン通信をするためのメソッドだそうで。もちろんJavaScriptでやる。

よくわかる解説:
https://developer.mozilla.org/ja/DOM/window.postMessage

やろうとしたこと

Herokuで実装したJavaScriptAPIと、AmazonS3においたHTML(JavaScript)とでおしゃべりしたい。
AmazonS3のHTMLは実際にはiframeの子として読み込まれる。
なんか変な設計だけど、いろいろと事情がありまして。。。

Cacooたのしい。

結果こうなった

HerokuのJavaScriptにはpostMessageとreceiverを追加。
AmazonS3においたHTML(JavaScript)はいじらずに済むよう、あともろもろの理由でラッパーをかますことに。
AmazonS3にラッパー用フレームを置いて、そこでpostMessageとreceiverを実装した。

なにをしたか

Heroku側のJavaScriptに以下のようなコードを追加した
Event.observe(window, 'message', receiveMessage, false);

function receiveMessage(evt){
  if(evt.origin === '<%= AWS_URL %>'){
    
    if(evt.data == 'geturl'){
      frames[0].postMessage('data_url=<%= @data.url %>', '<%= AWS_URL %>');
      return true;
    }
    
    if(evt.data == 'initialize'){
      frames[0].postMessage('progress=<%=raw @data.progress.to_json %>', '<%= AWS_URL %>');
      return true;
    }
    
    if(evt.data.match(/terminate=/)){
      <%=
        remote_function(
            :url => {:controller => "activities", :action => "terminate"},
            :type => :synchronous,
            :with => "'val=' + RegExp.rightContext"
        )
      %>
      location.reload();
      return true;
    }
  }
}

なんかいろいろ見えてるけど気にしない;
postMessageを受けとったら、function receiveMessage(evt)が実行される。
つまり、Amazonから呼ばれた時の動作を作る。
引数evtについての詳細は解説を読むとして。

 origin ・・・・・呼び出し元のウィンドウのアドレス?
 data ・・・・・・・送られてきたメッセージ

かならず呼び出し元をチェックする。

frames[0].postMessage()は、このウィンドウ内のフレームにpostMessageしている。


AmazonS3には以下のようなJSを追加した。
Event.observe(window, 'message', receiveMessage, false);

function receiveMessage(evt){
  if(evt.origin === 'https://localhost'){
        
    if(evt.data.match(/data_url=/)){
      $('inner').setAttribute('src', RegExp.rightContext);
      return true;
    }

    if(evt.data.match(/progress=/)){
      progress = RegExp.rightContext.evalJSON();
      return true;
    }

    if(evt.data.match('terminate')){
      Terminate();  
      return true;
    }
  }
}
function Terminate(){
  win.postMessage('terminate=' + Object.toJSON(progress), 'https://localhost');
  return true;
}
var win;
if(opener){
  win = opener;
}else if(parent){
  win = parent;
}
win.postMessage('geturl', 'https://localhost');

同じようにイベントとfunctionを追加して、HerokuのJavaScriptからメッセージを受け取るように。
はー。JavaScriptだけでこんだけ出来るってすごいなー。


IEでハマったとこ

window.openerがとれん。仕方ないのでiframeにしてwindow.parentで妥協した。
このせいで(おかげで?)あいまいだった仕様が確定したことは内緒。
そしてゴミコードが残ってるのも内緒。

ジブンヨウメモ RailsでIEが動かんとき IEばくはつしろ!

IEがdeleteしてくれない

rails 3.0.7でつくってたら見事に。
 対処 => rails.jsを入れ替えてみる。https://github.com/rails/prototype-ujs/
 参考 => http://tag3.at.webry.info/201101/article_3.html


〜〜〜 2011/08/08 追記
 prototype.jsも入れ替える。
http://prototypejs.org/2010/10/12/prototype-1-7-rc3-support-for-ie9