Firefox はバージョン 48 から WebExtensions のサポートを始め、そして 2017 年 11 月にリリースされるバージョン 57 で、旧 API を使った Add-ons のサポートを打ち切ります。 この意向に嘆く Firefox ユーザも少なくないでしょうが、Mozilla も脆弱性の温床だった旧 Add-ons API を早く捨てたかったんじゃないでしょうか。 Microsoft Edge も WebExtensions をサポートしているそうで、より WebExtensions の機運が高まってます。
この記事では webpack と babel を使ったモダンな、WebExtensions の開発環境を構築する方法を紹介します。 完成品は GitHub で公開してます。
ビルド環境を整える
ビルド環境は、webpack と babel を使って ES2015 の JavaScript をビルドして、バンドルされた JavaScript を出力したいと思います。 まず npm で必要なパッケージをインストールします。
npm init
npm install -D webpack babel-loader babel-core babel-preset-es2015
次に webpack.config.js
をプロジェクトのルートに配置します。
const path = require("path")
const src = path.resolve(__dirname, "src")
const dist = path.resolve(__dirname, "build")
module.exports = {
entry: {
content: path.join(src, "content"),
background: path.join(src, "background"),
},
output: {
path: dist,
filename: "[name].js",
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader",
query: {
presets: ["es2015"],
},
},
],
},
resolve: {
extensions: [".js"],
},
}
この設定で、background.js
とcontent.js
の 2 つの JavaScript を生成します。
content.js
は content scripts と呼ばれるもので、HTML 上でロードされるファイルです。
background.js
は background scripts と呼ばれるもので、contet scripts と独立したプロセスで動作します。
webpack はsrc/content
, src/background
に配置した javaScript をビルドして、バンドルされた JavaScript をbuild
ディレクトリに生成します。
空の JavaScript を配置して、webpack がビルドできるようにします。
mkdir -p src/{background,content}
touch src/{background,content}/index.js
webpack が正常にビルドできるか確認します。
ビルドするとbuild
ディレクトリにcontent.js
とbackground.js
が生成されるはずです。
node_modules/.bin/webpack
開発用にpackage.json
にビルドスクリプト"start": "webpack -w --debug"
を登録しましょう。
マニフェストファイルを配置する
WebExtensions はマニフェストファイルが必要になります。
プロジェクトのルートにmanifest.json
をファイルを作ります。
{
"manifest_version": 2,
"name": "Web Extension Template",
"description": "Web Extensions tutorial for Google Chrome/Chromium and Firefox.",
"version": "1.0.0",
"content_scripts": [
{
"matches": ["http://*/*", "https://*/*"],
"js": ["build/content.js"]
}
],
"background": {
"scripts": ["build/background.js"]
}
}
そしてブラウザでプラグインをロードします。
Firefox の場合はabout:config
を開き「Load Temporary Add-ons」からロードします。
Google Chrome/Chromium はchrome://extensions
を開き「Load unbacked extension...」からロードします。
これでエラーが表示されなければ OK です。
WebExtensions の開発を始める
ここまでで必要な環境は揃いました。
簡単な WebExtensions を記述してみましょう。
まずは先程登録したnpm start
でwebpack
を起動してます。
これで webpack がファイル変更を検知して、自動でビルドします。
h
/l
キーでタブの切り替えをできる簡単な WebExtensions を作ってみます。
まずはsrc/content/index.js
からです。
// src/content/index.js
window.addEventListener("keypress", e => {
if (e.key === "h") {
chrome.runtime.sendMessage({ type: "tabs.prev" })
} else if (e.key === "l") {
// L
chrome.runtime.sendMessage({ type: "tabs.next" })
} else {
return
}
e.stopPropagation()
e.preventDefault()
})
content scripts からはwindow
や DOM 操作ができます。
そしてchrome
オブジェクトで WebExtensions API にアクセスできます。
この例ではキーが押されるとchrome.runtime.sendMessage()
を呼び出して、background scripts にメッセージを送ります。
つぎに background scripts 側です。
chrome.runtime.onMessage.addListener()
で、メッセージのイベントリスナを登録します。
この例ではメッセージを受け取った気にタブを切り替える処理を記述しています。
// src/background/index.js
function selectPrevTab(current) {
chrome.tabs.query({ currentWindow: true }, tabs => {
let index = (current.index - 1 + tabs.length) % tabs.length
chrome.tabs.update(tabs[index].id, { active: true })
})
}
function selectNextTab(current) {
chrome.tabs.query({ currentWindow: true }, tabs => {
let index = (current.index + 1) % tabs.length
chrome.tabs.update(tabs[index].id, { active: true })
})
}
chrome.runtime.onMessage.addListener((request, sender) => {
switch (request.type) {
case "tabs.prev":
selectPrevTab(sender.tab)
break
case "tabs.next":
selectNextTab(sender.tab)
break
}
})
以上で全ての手順は終了です。 WebExtensions も基本的に JavaScript なので、モダンな環境で開発を始められます。 また ESLint や mocha といった既存の資源を使って、普通の JavaScript と同じように開発を進めることができます。