Firefox用WebExtensionsをChromeに対応させる

自作のXULベースのレガシー拡張機能 FoxAge2ch の WebExtensions 版 FoxAge5ch を2018年4月にリリースしたが、その後のバージョンアップにより、ソースコードを共通化したまま Chrome にも対応済みとなっている。 Firefox 用 WebExtensions を Chrome でも動作可能とするにあたり、両者のAPI差異を吸収するライブラリである browser-polyfill.js によるところが非常に大きいが、それ以外にもいくつか気付いた点があったため、ノウハウとしてまとめた。なお、前提として、ブラウザのバージョンは Firefox 60、 Chrome 67 とする。

browser API と chrome API の違い

Firefox と Chrome の一番大きな差異は、WebExtensions APIの名前空間が異なる点である。 Firefox の browser API は async / await 構文による Promise ベースのコーディングが可能である一方、 Chrome の chrome API はコールバックベースとなっている。ソースコードを共通化したまま Firefox と Chrome 両対応させるにあたり、当初はブラウザを判別してif文で分岐しながら頑張ろうとしたが、開発途中でこの差異を見事に吸収してくれるありがたいライブラリ browser-polyfill.js の存在に気付いた。

browser-polyfill.js の github にはライブラリ自体のソースコードが置かれているが、 WebExtensions で使用するには npm でパッケージ化する必要がある。現時点の最新バージョンがパッケージ化されたものも入手可能である。 WebExtensions 内での使い方としては、バックグラウンドスクリプトの場合 manifest.json の “background”.”script” にてロードし、サイドバーやポップアップなどの各種UI用HTMLの場合 script タグでロードすればよい。たったそれだけで、 Firefox 向けに記述した browser APIのコードが Chrome でも動作するようになる。(ただし一部例外あり)

ブラウザの判別

前項の browser-polyfill.js を使用することで概ね Firefox と Chrome のソースコードは共通化することができた。しかし、一部共通化できない部分やCSSをブラウザごとに若干調整する必要があり、ブラウザを判別してif文で分岐させる必要があった。ブラウザを判別する方法として、 browser-polyfill.js をロードしている前提であれば、下記の方法が手っ取り早い。ただし、これが最善かどうかは不明。

if ("sidebarAction" in browser) {
  // Firefox向けの処理
  ...
}
else {
  // Chrome向けの処理
  ...
}

background ページへのアクセス

Firefox では、サイドバーやポップアップなどの各種UI用HTML内で下記のようにして background ページへアクセスできた。

browser.runtime.getBackgroundPage(win => {
  window.MyBackgroundService = win.MyBackgroundService;
});

ところが、 browser-polyfill.js では上記の処理がエラーとなってしまうため、前述した方法でブラウザを判別し、 Chrome の場合は下記のようにした。

let win = chrome.extension.getBackgroundPage();
window.MyBackgroundService = win.MyBackgroundService;

キー押下イベントのキー判別

keydown, keypress イベントに対するイベントハンドラ内でどのキーが押されたか判別する際、 Firefox では KeyboardEvent.keyCode プロパティの値(数値)を event.DOM_VK_* 定数で判別することが可能。

if (event.keyCode == event.DOM_VK_RETURN) {
  // Enterキーが押された時の処理
  ...
}

しかし、 Chrome では event.DOM_VK_* 定数が定義されていないためエラーとなる。keyCode プロパティの使用はやめて、key または code プロパティを使用すべき。なお、 key と code プロパティはNumlockされている場合に値が異なる。

if (event.key == "Enter") {
  // Enterキーが押された時の処理
  ...
}

ミドルクリックに対するイベントハンドラ

マウスの中ボタンによるクリック(ミドルクリック)に対して何らかの処理を行う場合、 Firefox では click イベントに対してイベントハンドラを追加して event.button == 1 により中ボタンが押されたことを判別できた。しかし、 Chrome ではそもそも中ボタンによるクリックは click イベントが発生しない。そこで、 auxclick イベントに対するイベントハンドラを追加することで両ブラウザで動作可能となる。

event.targetで取得できるDOM要素の差異

document.addEventListener("click", onClick)のようにdocument全体にクリックイベントハンドラを追加しておき、イベントハンドラ側で event.target によりどのボタンが押されたかを判別する場合、ボタン(button要素)の中に img 要素を入れていたりすると、 event.target で取得できるDOM要素が Firefox と Chrome とで異なる。img 要素などでclickイベントを発生させないためには、下記のようなCSSを追加しておく。

img {
  pointer-events: none;
}

CSSの:any疑似クラス

CSSの :any 疑似クラスは将来的に :matches に代わるようだが、現時点で Firefox はベンダープレフィックス付きの :any のみ対応している。したがって、 Firefox / Chrome 両対応するには下記のように記述する必要がある。

:-moz-any(#foo, #bar) { ... }
:-webkit-any(#foo, #bar} { ... }

CSSのuser-select

各種UI用HTMLでマウスのドラッグなどで不必要な文字列選択を発生させなくするため、 user-select プロパティを設定すべきだが、現時点で Firefox ではベンダープレフィックスが必要となる。したがって、 Firefox / Chrome 両対応するには下記のように記述する必要がある。

html, body {
  user-select: none;
  -moz-user-select: none;
}

XMLHTTPRequest の User-Agent 改変

Chrome では下記のように XMLHTTPRequest によるHTTP要求の User-Agent ヘッダーを改変しようとするとエラーとなる。この処理は Firefox 限定とする必要がある。

var request = new XMLHTTPRequest();
request.setRequestHeader ("User-Agent", "...");

TOP

Add Your Comment

TOP