はじめてのブラウザ拡張開発

# 本記事について
Firefoxのブラウザ拡張がChromeと互換性のあるWebExtensionという形式になったことを知り、FirefoxとChrome両方で動くブラウザ拡張を作ってみた。

これが、自身にとってはじめてのブラウザ拡張開発である。

その作成の過程を記す。

## ブラウザ拡張の題材（作ったもの）
ブラウザのアドレスバーに、日本語を含むURLが表示されている場合があるが、そのような日本語を含むURLをブラウザからクリップボードにコピーすると、日本語ではなくURLエンコードされた状態のURLになる。
URLを日本語のままコピーしたいときがあったので、日本語のままコピーできるブラウザ拡張を作成した。

* [ソースコード](https://github.com/szk0u/copy-decoded-url)
* [Firefox版の拡張](https://addons.mozilla.org/ja/firefox/addon/%E3%82%BF%E3%83%96%E3%81%AEurl%E3%82%92%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E3%81%BE%E3%81%BE%E3%82%B3%E3%83%94%E3%83%BC/)
* [Chrome版の拡張](https://chrome.google.com/webstore/detail/url%E3%82%92%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AE%E3%81%BE%E3%81%BE%E3%82%B3%E3%83%94%E3%83%BC/phldfdmkaopoaabbgmilpboplanfmhne?hl=ja)

# ブラウザ拡張の作成方法をざっと学ぶ
MDN Web Docsの[ブラウザー拡張機能のページ](https://developer.mozilla.org/ja/Add-ons/WebExtensions)に載っている入門的な記事を読んだり軽く手を動かしたりしながら、ブラウザ拡張の作成方法をざっと学んだ。

* 以下のページをみながら実際に手を動かして、ブラウザ拡張を作成する簡単な流れを把握した
  * [初めての拡張機能 - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Your_first_WebExtension)
  * [2 つめの拡張機能 - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Walkthrough)
* 以下のページを見て、ブラウザ拡張に必要なもの（ブラウザ拡張の構成要素）を把握した
  * [拡張機能の中身 - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Anatomy_of_a_WebExtension)

# ブラウザ拡張の実装サンプルから、自分の作りたい拡張に近いものがないか探す
GitHub上にmozillaによるWebExtensionの実装サンプルが公開されている。

https://github.com/mdn/webextensions-examples

この中から、自分が作りたいものと関連ありそうなものがないか探してみた。あれば、そこから実装方法等を拝借できると思ったので。

見つけたのが以下。
https://github.com/mdn/webextensions-examples/tree/master/context-menu-copy-link-with-types

予想以上に、自分が作りたいものに必要な要素が含まれているものが見つかった。

# とりあえずFirefoxだけを対象に実装を進める
上記サンプルを土台にして、[mdnのWebExtensionのページ](https://developer.mozilla.org/ja/Add-ons/WebExtensions)のAPIのページ等を眺めつつ実装を進めて最初のバージョンが完成。

勉強のために、[国際化 - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Internationalization)のページを見ながら、メッセージ文言は最初からコード内に書かず、messages.jsonに外出しした。（最初は日本語のメッセージしか用意しなかったけれど）

[アドオン開発者センター](https://addons.mozilla.org/ja/developers/)から作成した拡張を登録。
登録した後、公開される前に審査があり若干ドキドキしたが、とくに指摘もなく公開された。

この時点のソースコード: https://github.com/szk0u/copy-decoded-url/tree/v1.0

# Chromeにも対応してみる
結論から言うと、[mozilla/webextension-polyfill: A lightweight polyfill library for Promise-based WebExtension APIs in Chrome](https://github.com/mozilla/webextension-polyfill)を使用した。

最初は、Firefox向けに作成したものがそのままChromeでも動くと思っていたが、そんなことはなかった。
[Chrome との非互換性 - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Chrome_incompatibilities)を見て、ChromeとFirefoxの差異を知った。

* MDNを見ながら実装を進めた結果、`browser`というネームスペースを使用して各種APIを呼び出す実装にしていたが
* Chromeでは`chrome`というネームスペースを使用して各種APIを呼び出すらしい
* 互換性のためにFirefoxでも`chrome`というネームスペースが使用できるみたいなので、`browser`と書いている箇所を`chrome`に置換すれば良さそうだが、もっと良い方法はないかと思っていると
* `browser`を使って書いた拡張機能をChromeでも動かせるようにするpolyfillなるものがあるということなので、このpolyfillを使うことにした

以下の手順でpolyfillを適用し、Chromeでも動作させることに成功。

* https://github.com/mozilla/webextension-polyfill をPC上にクローン
* クローンしてできた webextension-polyfill ディレクトリに移動して`npm install`と`npm build`を実行
* すると、distディレクトリにファイルが作成されているので、その中から browser-polyfill.min.js をコピーして自分のブラウザ拡張のコードベースに追加
* [このような感じ](https://github.com/szk0u/copy-decoded-url/blob/f4bdabc1068e870cd812e896aa1837e8e275ac8f/manifest.json#L12)で、manifest.jsonの background > scripts に browser-polyfill.min.js を追加

ただし一部の機能がFirefoxのみ対応していてChromeでは動作しなかったので、Firefoxの場合のみ当該機能を使用するようにした。

* Firefoxでのみ動作したという機能は、タブを右クリックしたときのコンテキストメニューにメニューを追加する機能
* 実行中のブラウザがFirefoxなのかChromeなのか判定する方法に悩んだが、[browser.runtime.getBrowserInfoを使用してブラウザの情報を取得して判定するように実装した](https://github.com/szk0u/copy-decoded-url/blob/f4bdabc1068e870cd812e896aa1837e8e275ac8f/background.js#L27-L30)。

この時点のソースコード: https://github.com/szk0u/copy-decoded-url/tree/f4bdabc1068e870cd812e896aa1837e8e275ac8f

# web-extツールを使ってみる
作成したブラウザ拡張をアドオン開発センターから公開する際、ソースコードをZIPに固めてアップロードする必要がある。
(ZIPコマンド等で固めてもよいのだが）ブラウザ拡張用のパッケージングツールのようなものはないかと思い、調べてみたところ見つかったので、使用してみた。

ツールの名前はweb-extで、以下のページを見ながら使ってみた。

* [はじめての web-ext - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/Getting_started_with_web-ext)
* [web-ext command reference - Mozilla | MDN](https://developer.mozilla.org/ja/Add-ons/WebExtensions/web-ext_command_reference)

このツールを使うと

* ブラウザ拡張を登録するためのZIP化
* ブラウザ拡張の静的コード解析(lint)
* 開発中の拡張をインストールしたFirefoxの起動

などがコマンドライン操作で実行できる。

web-extツールにはいくつかコマンドラインオプションがある。
それらのオプションは設定ファイルでの指定も可能なので[設定ファイル](https://github.com/szk0u/copy-decoded-url/blob/c021a80cdce8590e6c55c4f93a59dbec23585c00/web-ext-config.js)を作成した。

* web-extはカレントディレクトリにある「web-ext-config.js」という名前の設定ファイルを自動で読み取るため、設定ファイルの名前はこの名前にした

最終的にはyarnでweb-extをインストールし、`yarn build`でパッケージングしたり、`yarn start`で開発中の拡張をインストールしたブラウザを起動するように[設定してみた](https://github.com/szk0u/copy-decoded-url/blob/c021a80cdce8590e6c55c4f93a59dbec23585c00/package.json)。

# （機能追加）アドレスバーにアイコンを表示してみる
ブラウザのアドレスバーにアイコンを表示して、そのアイコンをクリックしたらURLをコピーできるようにした。
[pageAction](https://developer.mozilla.org/ja/Add-ons/WebExtensions/API/pageAction)を使用することで実現できた。

ただし、Chromeではアドレスバーにアイコンを表示することはできなかった。

* Chromeでは、昔はアドレスバーにアイコンを表示できたが、現在はツールバーに表示するようになったらしい
  * [Google Chrome bans extensions from the address bar, forcing them to the side](https://thenextweb.com/apps/2016/03/04/google-chrome-bans-extensions-from-the-address-bar-forcing-them-to-the-side/)