はじめに
これは初めて Chrome 拡張を作る人が超基本を理解するのを手助けするための資料です。
ユニークビジョン社内のハンズオン勉強会の資料として作りました。
この記事は Manifest V3 に準拠しています。
Chrome 拡張の全体像
Chrome 拡張のざっくりとした全体像を図にまとめてみました。
機能ごとに説明します。
ポップアップウィンドウの表示
拡張アイコンクリック時にポップアップウィンドウを表示できます。
popup と呼ばれています。
既存ページの DOM へのアクセス
任意のウェブサイトの DOM にアクセスし、情報を取得したり、改変したりできます。
content_script と呼ばれています。
html ファイルの表示
通常のウェブサイトのように任意の html + js ファイルを表示、動作させることができます。
ブラウザ自体の機能や外部 API へのアクセス
ブックマーク / クッキー / タブ など、ブラウザ自体が有する多くの機能にアクセスすることができるのがbackground です。
popup や content_script は通常のフロントエンドのように CORS などの制限があるのに対し、background はそのような制限がなく、通常のサーバのように外部 API などを利用することができます。
ストレージの利用
sync / local / session / managed という 4 種類のストレージを使うことができます。通常のフロントエンドにおける local storage や session storage のようなものです。
popup / content_script / background のいずれからもアクセスできます。
chrome 拡張の中でデータをやり取りする手段の 1 つです。
各機能間の通信
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 を作成します。
{
"name": "mini-ext",
"description": "mini-ext",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
}
}
<html>
<body>
<h1>mini ext</h1>
<button id="display">display url</button>
<script src="popup.js"></script>
</body>
</html>
// ボタンクリック時に実行する処理を定義
const button = document.getElementById('display')
button.addEventListener('click', () => {
// とりあえず console.log してみる
console.log('clicked!')
})
Chrome で読み込む
- 拡張機能の画面(chrome://extensions/)を開く。
- デベロッパーモードを ON にする。
- [パッケージ化されていない拡張機能を読み込む] をクリックする。
- 作成したプロジェクトのディレクトリを選択する。
- アイコンをピン留めしておくと楽です。
- アイコンをクリックすると popup が表示されます。
- popup を右クリック → 検証で検証画面を開きます。
- popup のボタンをクリックするとログが表示されます。
background でタブの情報を取得
- background.js をプロジェクトのルートに作成します。
// tabs.query は指定した条件に当てはまるタブを全て取得する。
// この場合はアクティブなタブが一つ取得できる。
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
console.log({ tabs })
});
- manifest.json の permission を追加します。
{
"name": "mini-ext",
"description": "mini-ext",
"version": "1.0",
"manifest_version": 3,
"action": {
"default_popup": "popup.html"
},
+ "background": {
+ "service_worker": "background.js"
+ },
+ "permissions": ["tabs"]
}
content_script で文字を挿入
- content.js をプロジェクトのルートに作成します。
const body = document.querySelector('body');
const addElement = document.createElement('h1');
addElement.textContent = 'Hello!!';
body.prepend(addElement);
- manifest.json に content_scripts の設定を追加します。
{
"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"]
}
messaging API でをつなぎこむ
実装イメージ
次のように実装して、それぞれの機能を繋ぎこみます。
- popup click で background にメッセージを送る。
- background で tab の URL を取得し、content_script に送る。
- content_script は、受け取った URL をウェブサイトに差し込む。
popup -> background
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
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
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)
}
});
これですべてのコードが出揃いました!
動かしてみる
完成~!!
※動かない場合は、拡張の再読込と、ページのリロードを試してみてください。
次のステップ
ここまで、シンプルな javascript + html で最小構成のデモンストレーションをしてきました。
更に複雑なものを作ってみたい、という方には、ブラウザ拡張開発のためのフレームワークを使ってみることをオススメします。
面倒な設定がテンプレート化されていたり、TypeScript で開発できたりします。
vue ベースだと vitesse-webext などが著名なものとして挙げられます。
vitesse-webext については過去に解説記事を書いたので、興味があれば御覧ください。
終わりに
以上でハンズオンは終了です。Chrome 拡張の大まかな理解の助けになれば幸いです。
分かりづらい点や、フィードバックなどあれば、是非コメントしていただけると助かります。
おしらせ
フロントエンド周りのことをよく呟いています。よければフォローお願いします。
ユニークビジョンという会社のエンジニアです。
— シライシ@ユニークビジョン (@punksy2) October 3, 2022
フロントエンド開発と品質向上に取り組んでいるので、そのあたり興味ある方はフォローしていただけると嬉しいです。
品質の高いサービスを提供し続ける会社であるために/品質向上チーム https://t.co/vI7YsKL6QW