Check! ブラウザ拡張機能ってどういう仕組み?

2020/11/27に公開

Prologue

最近、オンライン登壇やハンズオン、ストリーミングで作業を公開したいときに、サブスクリプションIDなどの隠しておきたい文字列が画面に表示されてしまうのが非常に気になります。アーカイブ公開前に編集しなければならなかったり、ストリーミングでも画面に出さずに工夫しなければならなかったりと大変に手間です…

そこで「IDをマスクしてくれる拡張機能を作ろう!」と思い立ち、実装をはじめました🤓

手始めに、拡張機能を開発するにあたり、最初に知っておきたい情報をまとめてみます。

Chromium ベースのブラウザで動作する拡張機能

ここで取り上げるのは Google Chrome に代表される Chromium ベースのブラウザで動作する拡張機能(Extension)の開発です。今では、Microsoft Edge も Chromium ベースになり Chrome の拡張機能を利用できるようになりました。

拡張機能は、HTML, CSS そして JavaScript で作成し、 manifest を用いてそれらのファイルを管理します。ブラウザの機能を利用するには、JavaScript で制御コードを実装します。

拡張機能の実装にあたっては、下記のようなアーキテクチャが提供されています。

  • Manifest
  • バックグラウンド処理
    • 初期処理
    • イベント処理
  • UI 要素
    • アイコン、ツールチップ、バッジ、ポップアップ など
  • オプション設定

なお、ここでご紹介するのはほんの一部です。詳しくは公式ドキュメントをご参照ください。

https://developer.chrome.com/extensions

https://developer.chrome.com/extensions/overview

開発を始める際は、ぜひ公式のチュートリアルを体験してみてください。一通り知ることができるのでおすすめです。

https://developer.chrome.com/extensions/getstarted

余談: Firefox の拡張機能

なお、 Firefox でも Chromium ベースブラウザの拡張機能に互換性のある Add-on 機能を開発できるようです。

https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions

Manifest

コードを格納するディレクトリのトップに manifest.json を配置し、以下のような拡張機能の情報を記述します。

  • 拡張機能の名前や、バージョン、アイコンなどの基本情報
  • 権限(permission)を要する機能の定義
  • バックグラウンド処理やアクション時に起点となるファイル
manifest.json サンプル
{
  "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",
    ...
  ],

  ...
}

詳しいフォーマットについてはこちらをご確認ください。

https://developer.chrome.com/extensions/manifest

権限 permissions の定義

拡張機能ではいろんなことができますが、裏を返せば悪意あるコードが仕込まれ利用者が危険にさらされるケースもありうるわけです。そこで拡張機能では、許可を要する機能を manifest に定義することで、利用者は拡張機能のインストール時にどの機能が利用されるのかを把握でき、許可を与えることで初めて利用できるという仕組みになっています。

権限 permission が必要な機能は様々で、例えば下記のようなものが挙げられます。

  • activeTab
  • declarativeContent
  • storage
  • webRequest

詳しくは下記をご参照ください。

https://developer.chrome.com/extensions/permission_warnings

https://developer.chrome.com/extensions/declare_permissions

バックグラウンド処理

拡張機能の初期処理や、各種イベントのリスナ登録などは background に指定したスクリプトで実装します。

manifest.json サンプル
{
  ...
  "background": {
    "persistent": false,
    "scripts": [
      "background.js",
      '''
    ]
  },

https://developer.chrome.com/extensions/background_pages

初期処理

イベント処理の一部ではありますが、chrome.runtime.onInstalled という、拡張機能がインストールされたときに発生するイベントに対してリスナ登録を行うことで、初期処理を行うことができます。

チュートリアルでは storage に保存したデータを読込み設定を行ったり、下記のドキュメントでは、コンテキストメニューを作成する例が挙げられています。

https://developer.chrome.com/extensions/background_pages#initialization

イベント処理

Chromium ベースのブラウザでは、利用できるイベントがたくさん用意されています。バックグラウンド処理のスクリプトの中でリスナを設定することで、イベント発生時に処理を実行できます。

https://developer.chrome.com/extensions/background_pages#listeners

例えば下記のようなイベントに対してリスナを実装することができます。イベントの中には、かなり頻繁に発生するものもあるので、ドキュメントをよく確認し、必要最低限になるようよく検討してください。

実装 説明
chrome.runtime.onInstalled 拡張機能がインストールされたときに発生するイベント
chrome.permissions.onAdded サイトへのアクセスなどを含む権限 permissions が追加された際に発生するイベント
chrome.tabs.onUpdated タブの情報が更新されたときに発生するイベント
chrome.webRequest.onCompleted 通信リクエストが完了したときに発生するイベント

どんなイベントが用意されているかは、機能ごとに異なるので、詳しくはドキュメントをご参照ください。

https://developer.chrome.com/extensions/devguide

便利な機能として、イベントリスナの登録時に、反応させる条件 = 「フィルタ」を設定することができます。

下記の例では、そのイベントが発生した url に対して、指定した条件( https://www.google.com/ と一致する)が満たされる場合にのみリスナを発火するというコードです。これにより、不必要なイベント処理を避けることができ、利用者のマシン負荷への影響も軽減できるでしょう。

イベントリスナにフィルタを設定するサンプル
  chrome.webNavigation.onCompleted.addListener(function() {
      alert("This is my favorite website!");
  }, {url: [{urlMatches : 'https://www.google.com/'}]});

詳しくはドキュメントをご参照ください。

https://developer.chrome.com/extensions/background_pages#filters

https://developer.chrome.com/extensions/events#filtered

また、メッセージングを利用し、任意のイベントを定義し利用することもできます。

https://developer.chrome.com/extensions/messaging

アクションの実装

拡張機能では、page action または browser action を利用して、ブラウザに読み込まれたコンテンツに対してアクションを実装することができます。

種類 用途
page action 宣言したページでのみ処理を行いたい場合
browser action すべてのページで処理を行いたい場合

page action は、 declarative content API を利用して特定のページでアイコンの表示を変えたり、ページ内のコンテンツに対してスクリプトを実行するなど指定することができます。

https://developer.chrome.com/extensions/pageAction

https://developer.chrome.com/extensions/browserAction

ページ内のコンテンツに対して処理を行う

ページ内のコンテンツに対して処理を行うには、 content script の仕組みを利用します。

バックグラウンド処理では、基本的にはページのコンテンツにアクセスすることができません。ですので、筆者の目的のように、ページ内の要素に変更を加えたいなどの場合は、アクションが管理するイベントを契機に Content script を実行させる実装をします。

https://developer.chrome.com/extensions/content_scripts

開発中の拡張機能のロード

開発中の拡張機能をロードするには、下記の手順を行います。

  1. chrome://extensions (Edge の場合は edge://extensions )を開く
  2. 「デベロッパーモード」を有効にする
  3. 「パッケージ化されていない拡張機能を読み込む」(Load unpacked)で manifest.json があるディレクトリを指定する

詳しくは下記をご参照ください。

https://developer.chrome.com/extensions

なお、現在は、"ストアに公開されていない"パッケージ化された拡張機能( crx ファイル)はロードできないようになっています。(詳細

Epilogue

書いているうちに内容が膨らんできてしまって、いったん概要だけに留めることにしました💦

そして、記事にするにあたり復習してみると、自分の実装をもっとよくできるんではないかと見直したくなってきましたw 継続して取り組んでいきたいと思います。

なお、まだストア公開には至っていないので、開発時までの情報しか記載できませんでしたが、ストア公開することがあれば追って共有したいと思います。

Discussion