LoginSignup
149
176

More than 1 year has passed since last update.

はじめての Chrome 拡張 最小構成ハンズオン

Last updated at Posted at 2022-08-30

はじめに

これは初めて Chrome 拡張を作る人が超基本を理解するのを手助けするための資料です。
ユニークビジョン社内のハンズオン勉強会の資料として作りました。

この記事は Manifest V3 に準拠しています。

Chrome 拡張の全体像

Chrome 拡張のざっくりとした全体像を図にまとめてみました。
image.png

機能ごとに説明します。

ポップアップウィンドウの表示

image.png

拡張アイコンクリック時にポップアップウィンドウを表示できます。
popup と呼ばれています。

既存ページの DOM へのアクセス

image.png

任意のウェブサイトの DOM にアクセスし、情報を取得したり、改変したりできます。
content_script と呼ばれています。

html ファイルの表示

image.png

通常のウェブサイトのように任意の html + js ファイルを表示、動作させることができます。

ブラウザ自体の機能や外部 API へのアクセス

image.png

ブックマーク / クッキー / タブ など、ブラウザ自体が有する多くの機能にアクセスすることができるのがbackground です。

popup や content_script は通常のフロントエンドのように CORS などの制限があるのに対し、background はそのような制限がなく、通常のサーバのように外部 API などを利用することができます。

ストレージの利用

sync / local / session / managed という 4 種類のストレージを使うことができます。通常のフロントエンドにおける local storage や session storage のようなものです。

image.png

popup / content_script / background のいずれからもアクセスできます。
chrome 拡張の中でデータをやり取りする手段の 1 つです。

各機能間の通信

image.png

popup / content_script / background は互いに messaging API を介してデータの通信ができます。

popup や content_script から直接利用できないブラウザのブックマーク情報などは、この仕組みを利用して background から間接的に取得することができます。

最小構成ハンズオン

ソースコード

下記で公開しています。このハンズオンの最終形です。

作るもの

下記のような動きの拡張を作ります。

  • popup のボタンをクリックする
  • 表示しているページの URL を取得する
  • ページの上部に URL を表示する

実装の大まかな流れは以下です。

  • popup / background / content_script それぞれを単体で動かす
  • messaging API でそれぞれをつなぎこむ

それでは書いていきましょう!

popup の表示

プロジェクトの作成

ディレクトリを作成し、直下に manifest.json / popup.html / popup.js を作成します。

manifest.json
{
  "name": "mini-ext",
  "description": "mini-ext",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "popup.html"
  }
}
popup.html
<html>
  <body>
    <h1>mini ext</h1>
    <button id="display">display url</button>
    <script src="popup.js"></script>
  </body>
</html>
popup.js
// ボタンクリック時に実行する処理を定義
const button = document.getElementById('display')
button.addEventListener('click', () => {
  // とりあえず console.log してみる
  console.log('clicked!')
})

Chrome で読み込む

  • 拡張機能の画面(chrome://extensions/)を開く。
  • デベロッパーモードを ON にする。
  • [パッケージ化されていない拡張機能を読み込む] をクリックする。
    image.png
  • 作成したプロジェクトのディレクトリを選択する。
    image.png
  • アイコンをピン留めしておくと楽です。
    image.png
  • アイコンをクリックすると popup が表示されます。
    image.png
  • popup を右クリック → 検証で検証画面を開きます。
  • popup のボタンをクリックするとログが表示されます。
    image.png

background でタブの情報を取得

  • background.js をプロジェクトのルートに作成します。
background.js
// tabs.query は指定した条件に当てはまるタブを全て取得する。
// この場合はアクティブなタブが一つ取得できる。
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
  console.log({ tabs })
});
  • manifest.json の permission を追加します。
manifest.json
{
  "name": "mini-ext",
  "description": "mini-ext",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "popup.html"
  },
+  "background": {
+    "service_worker": "background.js"
+  },
+  "permissions": ["tabs"]
}
  • 拡張を再読み込みし、 [Service Worker] をクリックして検証画面を開きます。
  • タブの情報がログとして出力されます。
    image.png

content_script で文字を挿入

  • content.js をプロジェクトのルートに作成します。
content.js
const body = document.querySelector('body');
const addElement = document.createElement('h1');
addElement.textContent = 'Hello!!';
body.prepend(addElement);
  • manifest.json に content_scripts の設定を追加します。
manifest.json
{
  "name": "mini-ext",
  "description": "mini-ext",
  "version": "1.0",
  "manifest_version": 3,
  "action": {
    "default_popup": "popup.html"
  },
  "background": {
    "service_worker": "background.js"
  },
+  "content_scripts": [
+    {
+      "js": [
+        "content.js"
+      ],
+      "matches": [
+        // 対象とするウェブサイトの URL のパターンを記述する。この場合は https であればすべて対象となる。
+        "https://*/*" 
+      ]
+    }
+  ],
  "permissions": ["tabs"]
}
  • 拡張機能を再読込します。
    image.png

  • 適当なウェブサイトを開くと、上部に「Hello!!」が表示されます。(既に開いていたページはリロードしないと反映されません。)
    image.png

messaging API でをつなぎこむ

実装イメージ

次のように実装して、それぞれの機能を繋ぎこみます。

  • popup click で background にメッセージを送る。
  • background で tab の URL を取得し、content_script に送る。
  • content_script は、受け取った URL をウェブサイトに差し込む。
    image.png

popup -> background

popup.js
const button = document.getElementById('display')
button.addEventListener('click', () => {
-  console.log('clicked!')
+  chrome.runtime.sendMessage({ name: 'displayUrl:background' })
})

chrome.runtime.sendMessage の引数には任意の文字列またはオブジェクトが渡せます。
今回は { name: '関数名:送り先'} というオリジナルのフォーマットを使っています。
関数名や送り先を指定しているのは、sendMessage したデータはこの拡張の中であればどこでも拾えてしまうためです。(例外あり)
受け取った側で期待しているリクエストかどうかを判別するための情報として使用します。

background -> content_script

background.js
chrome.runtime.onMessage.addListener((request) => {

  // 期待通りのリクエストかどうかをチェック
  if (request.name === 'displayUrl:background') {
    let url;
    let id;

    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
      console.log({ tabs })
      url = tabs[0].url;
      id = tabs[0].id

      // content_script へデータを送る
      chrome.tabs.sendMessage(id, { // content_script はタブごとに存在するため ID 指定する必要がある
        name: 'displayUrl:contentScripts',
        data: {
          url
        }
      })
    });
  }
});

chrome.runtime.onMessage.addListener で message イベント時の処理を定義できます。
chrome.tabs.sendMessage は content_script にメッセージを送る関数です。tab ID を指定する必要があります。

content_script

content.js
chrome.runtime.onMessage.addListener((request, options) => {
  if (request.name === 'displayUrl:contentScripts') {
    const body = document.querySelector('body')
    const addElement = document.createElement('h1');

    // 受け取った URL を画面に表示する
    addElement.textContent = `URL is ${ request.data.url }`;
    
    body.prepend(addElement)
  }
});

これですべてのコードが出揃いました!

動かしてみる

  • 拡張を再読込します。
  • 任意のページを開きます。
  • popup のボタンをクリックします。
    chromeextdemo.gif

完成~!! :tada:

※動かない場合は、拡張の再読込と、ページのリロードを試してみてください。

次のステップ

ここまで、シンプルな javascript + html で最小構成のデモンストレーションをしてきました。
更に複雑なものを作ってみたい、という方には、ブラウザ拡張開発のためのフレームワークを使ってみることをオススメします。
面倒な設定がテンプレート化されていたり、TypeScript で開発できたりします。

vue ベースだと vitesse-webext などが著名なものとして挙げられます。

vitesse-webext については過去に解説記事を書いたので、興味があれば御覧ください。

終わりに

以上でハンズオンは終了です。Chrome 拡張の大まかな理解の助けになれば幸いです。
分かりづらい点や、フィードバックなどあれば、是非コメントしていただけると助かります。

おしらせ

フロントエンド周りのことをよく呟いています。よければフォローお願いします。

149
176
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
149
176