Opera をはじめ、Chrome と Firefox でも dropbox に対する読み書きを行えるようにした。
Chrome でも動くようにするのは、ぜんぜん難しくないのだ。んが、Firefox で動かすのがなかなか面倒。他のブラウザが、エクステンションといいつつも実態は html ページなのに対して、Firefox (Add-on SDK) で動くエクステンションのコードというのはグローバルオブジェクトが [object Proxy] だかなんだかという見慣れた window とはちょっと違う何かだったりする。
で、jsOAuth.js という独立したライブラリを参照する際、Opera と Chrome ではスタートアップとなる html ページにいつものよーに script 要素を書くだけでよいのだが、Firefox の場合は CommonJS に従ったルールで lib ディレクトリに置く必要がある。jsOAuth は Node.js & CommonJS コンパチブルだと謳っているので、まあ Firefox でもうまくいけばそのまま動くのだろうなどと思っていたら、甘かった。
CommonJS に則った方式で再利用可能なモジュールを書く場合、
exports.foo = function () {
console.log('hello, world');
};
などと exports に対して登録する形になる。jsOAuth もその先頭で
var exports = exports || this;
exports.OAuth = (function (global) {
:
})(this);
とやっている。これで、CommonJS 下ではエクスポート、一般的な javascript では window に OAuth オブジェクトが結び付けられる。めでたしめでたし……のはずが、なぜか Firefox ではいくら再利用する側で
var OAuth = require('./jsOAuth');
とやっても読み込まれない。正確には、読み込まれるのだが(つまり jsOAuth.js 自体は評価されるのだが)、再利用できない。OAuth は undefined のままなのだ。
結論から書くと
typeof window == 'object' && eval('var exports = this;');
exports.OAuth = (function (global) {
:
})(this);
のようにする必要がある。SDK が提供する javascript の実行環境は前述の object Proxy だか sandbox だかの、管理された空間みたいなのだが、その空間のグローバルに提供される exports やら require() やらはちょっと特殊な扱いっぽいのだな。なのでおそらく、var exports なんてしちゃうと本来の exports を「管理された空間上の exports 」で覆ってしまって役に立たなくなるということなのだと思う。したがって、exports の polyfill を定義するのは本当に window 下で実行されているときだけに厳密に分けないといけない。
そもそも require とか exports とか、どこでどう定義されているのかと SDK のソースをいろいろ探ってみたのだけど、どうも見つけられなかったのは内緒。いやまあ packages/api-utils/lib/loader.js とか cuddlefish.js とかがキーだと思うんだけど……。なんか bootstrap から追っていってもいきなり require() とか使い出してる感じだしよく分からなかった。
このほか、XMLHttpRequest も SDK 専用品を使わなければならなかったり(しかも upload プロパティまだ実装してまへんテヘペロ とかいう未完成品だったりする)、Firefox はなかなかいろいろと勝手が違うのだった。