Subscribed unsubscribe Subscribe Subscribe

Content ScriptsなChrome拡張をFirefox拡張に

お手製のしょうもないChrome拡張をFirefox拡張にしてみた。といってもタイトルにある通りContent Scripts、つまりユーザースクリプトレベルなものなので、そんな面倒ではなかった。

Firefox拡張のContent Scripts

まずは同じコンセプトのものがFirefoxにあるのか、ドキュメントを探す。あった。

Many add-ons need to access and modify the content of web pages. But the main add-on code doesn't get direct access to web content. Instead, SDK add-ons need to factor the code that gets access to web content into separate scripts that are called content scripts. This page describes how to develop and implement content scripts.

Content scripts can be one of the more confusing aspects of working with the SDK, but you're very likely to have to use them. There are five basic principles:

  • the add-on's main code, including "main.js" and other modules in "lib", can use the SDK high-level and low-level APIs, but can't access web content directly
  • content scripts can't use the SDK's APIs (no access to globals exports, require) but can access web content
  • SDK APIs that use content scripts, like page-mod and tabs, provide functions that enable the add-on's main code to load content scripts into web pages
  • content scripts can be loaded in as strings, but are more often stored as separate files under the add-on's "data" directory
  • a message-passing API allows the main code and content scripts to communicate with each other

拡張からページのDOMは直接触れられずContent Scripts経由というのはChromeと同じらしい。あとIsolated Worldというコンセプトも共通。

スクリプトの読み込み方は違いがあるけど、Content Scriptsのファイルはそのまま流用できそう(Chromeでしか対応してないものを使ってない限りは)。わーい。

jpm

Firefoxの場合はどうやらAdd-on SDKをインストールしないといけないらしい。これまではそのSDKPythonベースのやつだったらしいんだけど、Fx38からNodeベースのjpmというものになったとのこと。

というわけで npm install -g jpm でさくっとインストール後、jpm init で名前やらエンドポイントやらを設定。

Content Scriptsの読み込み

Chrome拡張では manifest.json からContent Scriptを動作させたいURL(のパターン)とそのファイルを明示的に指定していた。

...
"content_scripts": [
  {
    "matches": ["https://foo.bar/*"],
    "js": [ "baz.js" ],
    "css": [ "quux.css" ]
  },
  ...

Firefox拡張の場合はエンドポイントのファイルを用意して、そこからプログラマブルな感じで指定するらしい。jpm initした場合はindex.jsができる。

今回の拡張はページにスクリプトファイルをぶっこむものなので、page-modというAPIを使う。

const data = require('sdk/self').data
const pageMod = require('sdk/page-mod')

pageMod.PageMod({
  include: "https://foo.bar/*",
  contentScriptFile: [ data.url('baz.js') ],
  contentStyleFile: [ data.url('quux.css') ]
})

ファイルの置き場

エンドポイントを指定したら jpm init したディレクトリ直下にできたのだけれど、Content Scriptsのファイルとかはその中に data ディレクトリを作ってそこに置かないといけないらしい。ここで少しはまった。initしたら作ってほしいな。

Chrome拡張はとくにディレクトリわけとかしてなかったので、そのまま読み込めないか試すもアウト。どうやらファイルを指定したとこで使ったself.data.url()data ディレクトリ内のファイルのみしか読めないらしい。まじか…

気になったのでソースを見てみる。

exports.data = Object.freeze({
  url: uri,
  load: function read(path) {
    return readURISync(uri(path));
  }
});

はいはい。では uri はというと…

const uri = (path="") =>
  path.contains(":") ? path : addonDataURI + path.replace(/^\.\//, "");

ええ。それで addonDataURI はというと、

const addonDataURI = baseURI + "data/";

(´Д`)

固定だ…ちょうmagicだ……

というわけであきらめかけてたんだけど、ふと data.url('../hoge.js') としてみたら読み込めた。でもいいのかな…まあ動かなくなったら考えよう。

ディレクトリ固定、もやっとすることはあるけど拡張のレビューとか、他のひとの拡張を知りたいときには見るところが決まっていて便利なのだろうなとも思う。Chromeだとmanifest.json見ないといけないし。

その他

作った拡張は jpm xpi でパッケージ化すれば再起動不要なアドオンとしてインストールできる。

Content Scriptレベルならふつーにできたけど、UIになにかぶっこむとかそういうのはAPIがだいぶ違うので、できるだけモジュールに落とすとかしないと共通部分が減るばかりな気がした。試したいんだけどそういう拡張を作ったことがない(…)ので、なんかアイデアを持とう。