先日使い方を学んだ MessageEvent と、location.href+javascript:ハックとJSDeferred userscript版とを組み合わせ、XPCNativeWrapper と unsafeWindow の間で同期処理を行うコードを書いてみた。
まずは MessageEvent を使って前回 evalInPage 相当の処理を作る。キャンセル処理も入れたかったので UnsafeWrapper というオブジェクトにまとめることにした。前回より長くなったように見えるが、実際には MessageEvent の lastEventId が使えるようになった分、簡潔になっている。
var UnsafeWrapper = new (function() { var seqId = 0, waiting = {}, self = this, noop = function(){}; function dispatch(data, id) { var e = document.createEvent("MessageEvent"); e.initMessageEvent('GM_UnsafeWrapper_returned', true, false, data, location.protocol + "//" + location.host, id, window); document.dispatchEvent(e); } function listen(func) { document.addEventListener('GM_UnsafeWrapper_returned', func, false); } listen(function(e) { if(waiting[e.lastEventId]) { waiting[e.lastEventId].call(self, JSON.parse(e.data)); delete waiting[e.lastEventId]; } }); this.exec = function(func, args, callback) { var userFunc = "("+ func +").apply(null,"+ JSON.stringify(args || []) +")"; waiting[seqId] = callback || noop; location.href = "javascript:void "+ (function(dispatch, ret, id) { dispatch(JSON.stringify(ret), id) }) + "("+ dispatch +","+ userFunc +"," + JSON.stringify(seqId) +")"; return seqId++; }; this.cancel = function(id) { delete waiting[id]; }; });
そして Deferred に対するアダプタを書く。とても簡単。
with(D()) { function unsafeExec(func, args) { var d = new Deferred(); var id = UnsafeWrapper.exec(func, args, function(ret) { d.call(ret); }); d.canceller = function(){ UnsafeWrapper.cancel(id) }; return d; }; }
これで例えば、GM_xmlhttpRequest で外部ドメインからデータを取得して、そのデータを整形して、unsafeWindow 側で表示処理を行って、そしてその結果をuserscript側で受け取って…という処理は次のように書くことができる。
with(D()) { xhttp.get("http://example.com/") .next(function(res) { var data = res.responseText; //.... return unsafeExec(function(data) { $.each(data, function(k,v) { //... }); //... return result; }, [data]); }) .next(function(result) { //... }); }
より実用性を高めるのであれば、location.href で実行するコード全体を try {} で囲み、catch したエラーをJSONに変換して _failed イベントを dispatch して、Deferred の fail に転送すれば、unsafeWindow 側で起こったエラーをuserscript側のDeferred chainで捕捉できるようになる。
参考: