<

Chrome拡張機能(Manifest V3)の開発で知ったこと

皆さん、Chrome 拡張機能をご存知ですか? Chrome 拡張機能は、Chrome ブラウザをカスタマイズするための機能です。

私は、Chrome 拡張機能を過去(数年前)に 2 つ作っていて、その当時は、Chrome 拡張機能の仕様である Manifest V2 に従っていました。 そして、今再び、Chrome 拡張機能で作りたいものができたので、久々に作ろうと決意しました。 作ろうと思ったものの、どうやら今の Chrome 拡張機能の仕様は Manifest V3 を推奨しているようです。 そこで、今回、開発した際に知ったことをまとめようと思います。

ちなみに、実際に作ったものは次のものです。

※ Chrome 拡張機能の概要について詳しく知りたい方は、What are extensions? - Chrome Developersをご覧ください。

Chrome Extensions Components

Chrome 拡張機能は、主に次の 4 つのコンポーネントが存在します。

  • Background Scripts
    • サービスワーカー上で動作し、ブラウザ上のイベント駆動(ページ遷移やブックマーク差所など)で反応します。
    • manifestbackgroundフィールドで設定します。
  • Content Scripts
    • Web ページのコンテキスト上で動作し、DOM へアクセスできます。
    • manifestcontent_scriptsフィールドで設定します。
  • UI Elements
    • URL バーの右側にあるボタンを押した(Action)際に表示される UI です。
    • ブラウザ体験を損なわさない最低限の機能だけの提供を推奨されています。
    • manifestactionフィールドで設定します。
  • Options Page
    • Chrome 拡張機能アイコンを右クリックして、オプションを選択すると表示される UI です。
    • Chrome 拡張機能をカスタマイズしたい設定ページに使います。
    • manifestoptions_pageフィールドで設定します。

私なりに、これらのコンポーネントの使い分けを考えると、次になります。

  • DOM へアクセスする必要がある
    • Content Scripts を使う
  • ページに依存しない処理がある
    • Background Scripts を使う
  • 環境変数の設定が必要
    • Option Page

UI Elements は、基本的に必要ないのかなと思いました。

Debug

デバッグって、どうやるんでしょうか。

こちらにやり方が書いてありました。 私なりに解釈した結果、次の 2 つで使い分けるのかなと思います。


  • ① そもそも、Chrome 拡張機能がロードできない場合

manifest.json ファイルに記述で誤りがあるなどで、Chrome 拡張機能がロードできない場面があります。 そういうときは、次の手順を実行します。

  1. chrome://extensions へアクセス
  2. 次の図にあるような ERROR ボタンをクリック
chrome extensions debug
chrome extensions debug

恐らく、何かしらエラーメッセージが出力されていると思います。 それを解決しましょう。


  • ② ① 以外の場合

Chrome 拡張機能はロードできるが、期待通りに動作しない場面があると思います。 そういうときは、DevTools を開きましょう。

  • Background Scripts の場合
    • chrome://extensions へアクセスし、inspect viewsの右にあるリンクをクリック。(上図)
      • DevTools が開きます。
  • Content Scripts, UI Elements, Options Page の場合
    • UI 上で右クリックして Inspect をクリック
      • DevTools が開きます。

DevTools には、console タブがあるはずです。そこのログメッセージを確認しましょう。

Message Passing

各コンポーネント間で、通信するのは、どうしたら良いのでしょうか。 例えば、Content Scripts から Background Scripts へデータを渡したいときなどです。 次の資料が、参考になります。

資料を読むと、次のようなパターンの通信ができるようです。

  • 各コンポーネント間の通信
    • Background Scripts ⇔ Content Scripts など
  • Chrome 拡張機能間の通信
    • A Chrome 拡張機能 ⇔ B Chrome 拡張機能
    • Chrome 拡張機能の ID を使って通信します
  • Web ページからの通信(Sending messages from web pages)
    • Web ページ ⇔ Chrome 拡張機能のコンポーネント

通信の具体的なコードは、chrome.runtime.sendMessageメソッドを使います。 Background Scripts から Content Scripts へ通信する場合、どの Chrome タブに送信するかchrome.tabs.queryで事前に id を見つけておく必要があります。

また、後で紹介しますが、Web Accessible Resourcesでアクセス可能な Javascript を Web ページへ Inject(document.querySelector('body').append())した場合、その Javascript と Content Scripts の通信は、window.postMessagewindow.addEventListenerを使いましょう。 chrome.runtimeが使えないので。

Web Accessible Resources

Content Scripts から Web ページの DOM へアクセスできますが、window オブジェクトにある変数へアクセスすることができません。

window オブジェクトへアクセスするには、Web Accessible Resources を使う方法があります。


具体的にコードで説明しましょう。

manifest.json で必要なフィールドの例は、次のとおりです。

{
  "manifest_version": 3,
  "content_scripts": [
    {
      "js": ["content-script.js"],
      "matches": ["https://*/*"]
    }
  ],
  "web_accessible_resources": [
    {
      "resources": ["web_accessible_resources.js"],
      "matches": ["https://*/*"]
    }
  ]
}

Content Scripts と Web Accessible Resources の Javascript は次のとおりです。

// content-script.js
const injectScript = (filePath, tag) => {
  var node = document.getElementsByTagName(tag)[0];
  var script = document.createElement("script");
  script.setAttribute("type", "text/javascript");
  script.setAttribute("src", filePath);
  node.appendChild(script);
};
injectScript(chrome.runtime.getURL("web_accessible_resources.js"), "body");
// web_accessible_resources.js
console.log(window["hoge"]);
// Content Scriptsへ通信する場合は、window.postMessageを使います。

このように、web_accessible_resources.js を Web ページの body タグへ append します。 その web_accessible_resources.js では、window オブジェクトにアクセスすることができます。

chrome.webRequest API

Chrome ブラウザでネットワークトラフィックを監視する Chrome 拡張機能の API があります。 それが、chrome.webRequestです。

これがあれば、Web ページでどういうリクエストが発生しているか分かるようになります。 manifest.json のフィールドで、host_permissionsの設定が必要です。


サンプルで、Background Scripts のコードを紹介します。 まず、manifest.json の必要なフィールドを書きます。

{
  "manifest_version": 3,
  "host_permissions": ["https://*/*"],
  "background": {
    "service_worker": "background.js"
  }
}

次に、Web ページからリクエストが完了(onCompleted)したイベントを監視するコードを書きます。

// background.js
chrome.webRequest.onCompleted.addListener(
  async (details) => {
    console.log(`request url is ${details.url}`);
  },
  {
    urls: ["https://*/*"],
  },
  ["responseHeaders"] // responseHeadersをdetailsオブジェクトに含めることができます。
);

この details にはリクエストの URL が含まれています。さらに詳しく知りたい人は、こちらをご確認ください。

最後に

Chrome 拡張機能、久々に開発してみると、進化しすぎていてキャッチアップに苦労しました。 私と同じような方の助けになれば、幸いです。

役立ったら、☕でサポートしてね!

シェアしよう

関連するタグ