互換性
ここいらで、ちょっと落ち着いて仕様の話をしたいと思います。各ブラウザーでAPIはある程度同じように使えて、先の例のタイマーも、同じコードでChromeとFirefoxの両方で動きます。
が、実装状況だけじゃなくて、実装方針もちょっとずつ違ってたりします。
Chromeではグローバルの chrome
オブジェクト以下にAPIが用意されています。例えば今回利用したAPIだと chrome.runtime.sendMessage()
というものを利用します。
Firefoxもグローバルに chrome
オブジェクトが用意されており、これはChromeのAPIとある程度の互換性を持っています。さっきも chrome.runtime.sendMessage()
が動きましたね。ただし完全ではなく、一部実装されていなかったり、あるいは逆にFirefoxにしかないものもあります。
加えて、Firefoxはグローバルに browser
というオブジェクトも用意しています。これも chrome
と同様の機能のAPI群なのですが、実はインターフェイス(書き方)がちょっと違っています。どう違うのかはいったん置いておきます。
最後にEdgeですが、ChromeのAPIと互換のものが、やはり不完全ながら chrome
ではなく browser
配下に用意されています。 chrome
も存在するんだけど、無関係の別物っぽい。何だろこれ。ともかくEdge用には browser.runtime.sendMessage()
のように書く必要があります。
標準API
Firefoxが2種類のAPIを用意してる件について。ブラウザー拡張のAPIは各ベンダーがそれぞれ用意してる感じなんだけど、W3Cによる標準化も試みられているようです。
で、Firefoxの browser
はそれに従っているようです。(どうかな違うかも?)
Promise化されたインターフェイス
chrome
はコールバックが必要な場合に引数で受け取るのですが、 browser
の方は代わりにPromiseオブジェクトを返して、そちらで制御するようになります。すると、ES2017の async
/await
を使った読みやすいコードにすることが可能です。
例として、記憶した情報を読み出す処理を考えます。 storage.local.get()
というものを使います。(これは "storage"
を manifest.json
の "permissions"
で設定する必要があります。)
まずはChromeで動くコード。
chrome.storage.local.get(["item1", "item2"], (result) => {
console.log('# result', result.item1, result.item2);
});
Firefoxもこれで動きます。
ではそのFirefoxの別の選択肢、 browser
の方も見てみましょう。Promiseになります。
browser.storage.local.get(["item1", "item2"]).then((result) => {
console.log('# result', result.item1, result.item2);
});
まあこれ見るとそんなに変わってないね。でも、Promiseの場合は async
な関数の中で await
を使って、こう書けるんです。
const result = await browser.storage.local.get(["item1", "item2"]);
console.log('# result', result.item1, result.item2);
これならまっすぐになって、だいぶ見やすいんじゃないでしょうか。見やすいよね。
コードを共通化する
Promiseの方が使いやすいので、Firefoxに寄せる方針です。
最近こちらに書いたのでどうぞ。他にEdge対応とかも。