Check! ブラウザ拡張機能ってどういう仕組み?
Prologue
最近、オンライン登壇やハンズオン、ストリーミングで作業を公開したいときに、サブスクリプションIDなどの隠しておきたい文字列が画面に表示されてしまうのが非常に気になります。アーカイブ公開前に編集しなければならなかったり、ストリーミングでも画面に出さずに工夫しなければならなかったりと大変に手間です…
そこで「IDをマスクしてくれる拡張機能を作ろう!」と思い立ち、実装をはじめました🤓
手始めに、拡張機能を開発するにあたり、最初に知っておきたい情報をまとめてみます。
Chromium ベースのブラウザで動作する拡張機能
ここで取り上げるのは Google Chrome に代表される Chromium ベースのブラウザで動作する拡張機能(Extension)の開発です。今では、Microsoft Edge も Chromium ベースになり Chrome の拡張機能を利用できるようになりました。
拡張機能は、HTML, CSS そして JavaScript で作成し、 manifest を用いてそれらのファイルを管理します。ブラウザの機能を利用するには、JavaScript で制御コードを実装します。
拡張機能の実装にあたっては、下記のようなアーキテクチャが提供されています。
- Manifest
- バックグラウンド処理
- 初期処理
- イベント処理
- UI 要素
- アイコン、ツールチップ、バッジ、ポップアップ など
- オプション設定
なお、ここでご紹介するのはほんの一部です。詳しくは公式ドキュメントをご参照ください。
開発を始める際は、ぜひ公式のチュートリアルを体験してみてください。一通り知ることができるのでおすすめです。
余談: Firefox の拡張機能
なお、 Firefox でも Chromium ベースブラウザの拡張機能に互換性のある Add-on 機能を開発できるようです。
Manifest
コードを格納するディレクトリのトップに manifest.json
を配置し、以下のような拡張機能の情報を記述します。
- 拡張機能の名前や、バージョン、アイコンなどの基本情報
- 権限(permission)を要する機能の定義
- バックグラウンド処理やアクション時に起点となるファイル
{
"manifest_version": 2,
"name": "My Extension",
"version": "0.0.1",
"description": "A plain text description",
"icons": {
"16": "images/icon16x16.png",
"48": "images/icon48x48.png",
"128": "images/icon128x128.png"
},
"background": {
"persistent": false,
"scripts": [
...
]
},
"page_action": {...},
"permissions": [
"activeTab",
"storage",
...
],
...
}
詳しいフォーマットについてはこちらをご確認ください。
権限 permissions の定義
拡張機能ではいろんなことができますが、裏を返せば悪意あるコードが仕込まれ利用者が危険にさらされるケースもありうるわけです。そこで拡張機能では、許可を要する機能を manifest に定義することで、利用者は拡張機能のインストール時にどの機能が利用されるのかを把握でき、許可を与えることで初めて利用できるという仕組みになっています。
権限 permission が必要な機能は様々で、例えば下記のようなものが挙げられます。
activeTab
declarativeContent
storage
webRequest
詳しくは下記をご参照ください。
バックグラウンド処理
拡張機能の初期処理や、各種イベントのリスナ登録などは background
に指定したスクリプトで実装します。
{
...
"background": {
"persistent": false,
"scripts": [
"background.js",
'''
]
},
初期処理
イベント処理の一部ではありますが、chrome.runtime.onInstalled
という、拡張機能がインストールされたときに発生するイベントに対してリスナ登録を行うことで、初期処理を行うことができます。
チュートリアルでは storage に保存したデータを読込み設定を行ったり、下記のドキュメントでは、コンテキストメニューを作成する例が挙げられています。
イベント処理
Chromium ベースのブラウザでは、利用できるイベントがたくさん用意されています。バックグラウンド処理のスクリプトの中でリスナを設定することで、イベント発生時に処理を実行できます。
例えば下記のようなイベントに対してリスナを実装することができます。イベントの中には、かなり頻繁に発生するものもあるので、ドキュメントをよく確認し、必要最低限になるようよく検討してください。
実装 | 説明 |
---|---|
chrome.runtime.onInstalled |
拡張機能がインストールされたときに発生するイベント |
chrome.permissions.onAdded |
サイトへのアクセスなどを含む権限 permissions が追加された際に発生するイベント |
chrome.tabs.onUpdated |
タブの情報が更新されたときに発生するイベント |
chrome.webRequest.onCompleted |
通信リクエストが完了したときに発生するイベント |
どんなイベントが用意されているかは、機能ごとに異なるので、詳しくはドキュメントをご参照ください。
便利な機能として、イベントリスナの登録時に、反応させる条件 = 「フィルタ」を設定することができます。
下記の例では、そのイベントが発生した url
に対して、指定した条件( https://www.google.com/
と一致する)が満たされる場合にのみリスナを発火するというコードです。これにより、不必要なイベント処理を避けることができ、利用者のマシン負荷への影響も軽減できるでしょう。
chrome.webNavigation.onCompleted.addListener(function() {
alert("This is my favorite website!");
}, {url: [{urlMatches : 'https://www.google.com/'}]});
詳しくはドキュメントをご参照ください。
また、メッセージングを利用し、任意のイベントを定義し利用することもできます。
アクションの実装
拡張機能では、page action または browser action を利用して、ブラウザに読み込まれたコンテンツに対してアクションを実装することができます。
種類 | 用途 |
---|---|
page action | 宣言したページでのみ処理を行いたい場合 |
browser action | すべてのページで処理を行いたい場合 |
page action は、 declarative content API を利用して特定のページでアイコンの表示を変えたり、ページ内のコンテンツに対してスクリプトを実行するなど指定することができます。
ページ内のコンテンツに対して処理を行う
ページ内のコンテンツに対して処理を行うには、 content script の仕組みを利用します。
バックグラウンド処理では、基本的にはページのコンテンツにアクセスすることができません。ですので、筆者の目的のように、ページ内の要素に変更を加えたいなどの場合は、アクションが管理するイベントを契機に Content script を実行させる実装をします。
開発中の拡張機能のロード
開発中の拡張機能をロードするには、下記の手順を行います。
-
chrome://extensions
(Edge の場合はedge://extensions
)を開く - 「デベロッパーモード」を有効にする
- 「パッケージ化されていない拡張機能を読み込む」(Load unpacked)で
manifest.json
があるディレクトリを指定する
詳しくは下記をご参照ください。
なお、現在は、"ストアに公開されていない"パッケージ化された拡張機能( crx ファイル)はロードできないようになっています。(詳細)
Epilogue
書いているうちに内容が膨らんできてしまって、いったん概要だけに留めることにしました💦
そして、記事にするにあたり復習してみると、自分の実装をもっとよくできるんではないかと見直したくなってきましたw 継続して取り組んでいきたいと思います。
なお、まだストア公開には至っていないので、開発時までの情報しか記載できませんでしたが、ストア公開することがあれば追って共有したいと思います。
Discussion