Google Chrome拡張機能開発のチュートリアルを実施してみた(任意のタイミングでのスクリプト実行編)
チュートリアルの概要
前回はGoogle Chromeの拡張機能やウェブストアに関する開発者向けドキュメントを開くと、読了目安時間が表示される拡張機能を作成しました。
今回もGoogle公式のチュートリアルに従い、特定のWebページのスタイルを任意のタイミングで変更する拡張機能を作成します。 チュートリアルの流れは以下の通りです。- Step 1. マニフェストファイルを作成
- Step 2. アイコンをダウンロード
- Step 3. Service Workerを作成
- Step 4. スタイルシートを作成
- Step 5. 動作を確認
チュートリアル完了後、Google Chromeの拡張機能やウェブストアに関する開発者向けドキュメントを開いて拡張機能のアイコンをクリックすると、以下のようにページがシンプルなスタイルに変更されます。
Step 1. マニフェストファイルを作成
まず、拡張機能のルートディレクトリfocus-mode
を作成し、移動します。
次に、focus-mode
ディレクトリ直下に以下のようなマニフェストファイルmanifest.json
を作成します。
{
"manifest_version": 3,
"name": "Focus Mode",
"description": "Enable focus mode on Chrome's official Extensions and Chrome Web Store documentation.",
"version": "1.0",
"icons": {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
},
"action": {
"default_icon": {
"16": "images/icon-16.png",
"32": "images/icon-32.png",
"48": "images/icon-48.png",
"128": "images/icon-128.png"
}
},
"background": {
"service_worker": "background.js"
},
"permissions": ["scripting", "activeTab"],
"commands": {
"_execute_action": {
"suggested_key": {
"default": "Ctrl+B",
"mac": "Command+B"
}
}
}
}
manifest_version
name
description
version
icons
action.default_icon
の6フィールドの説明は前々回の記事と前回の記事に記載しているので省略します。
Service Worker
background.service_worker
フィールドではService Workerのスクリプトを1つだけ指定できます。
Service Workerとは特定のイベント(拡張機能をインストールする、タブを開くなど)発生時にバックグラウンドで実行されるスクリプトのことです。
コンテンツスクリプトとは異なり、HTML要素にアクセスすることはできません。
権限
permissions
フィールドではGoogle拡張機能APIの実行に必要な権限を宣言します。
scripting
権限はchrome.scripting
APIを使用するために必要です。
今回はスタイルシートを挿入するchrome.scripting.insertCSS
とスタイルシートを削除するchrome.scripting.removeCSS
を使用するために権限を宣言します。
activeTab
権限はユーザの操作に応じてアクティブなタブで拡張機能を実行するために必要です。
activeTab
は限定的な権限のため、拡張機能のインストール時に権限に関する警告が表示されないというメリットがあります。
ショートカットキー
commands
フィールドではショートカットキーを登録できます。
任意のコマンド名を指定できますが、今回は_execute_action
という予約されたコマンド名を使用しています。
_execute_action
フィールドではツールバー上の拡張機能のアイコンをクリックするアクションのショートカットキーを定義できます。
今回はCtrl
キー+B
キー(macOS[1]の場合はCommand
キー+B
キー)を押下すると、拡張機能のアイコンをクリックしたと見なされます。
Step 2. アイコンをダウンロード
マニフェストファイルに記載した4つの異なるサイズのアイコンを用意します。
まず、アイコン用のディレクトリimages
をfocus-mode
ディレクトリ直下に作成して移動します。
次に、Google ChromeのGitHubリポジトリからアイコンをダウンロードし、マニフェストファイルの定義通りに命名します。
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GoogleChrome/chrome-extensions-samples/main/functional-samples/tutorial.focus-mode/images/icon-16.png" -OutFile "icon-16.png"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GoogleChrome/chrome-extensions-samples/main/functional-samples/tutorial.focus-mode/images/icon-32.png" -OutFile "icon-32.png"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GoogleChrome/chrome-extensions-samples/main/functional-samples/tutorial.focus-mode/images/icon-48.png" -OutFile "icon-48.png"
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/GoogleChrome/chrome-extensions-samples/main/functional-samples/tutorial.focus-mode/images/icon-128.png" -OutFile "icon-128.png"
Step 3. Service Workerを作成
マニフェストファイルに記載したService Workerを作成します。
focus-mode
ディレクトリ直下に以下のようなスクリプトbackground.js
を作成します。
chrome.runtime.onInstalled.addListener(() => {
chrome.action.setBadgeText({
text: "OFF",
});
});
const extensions = 'https://developer.chrome.com/docs/extensions';
const webstore = 'https://developer.chrome.com/docs/webstore';
chrome.action.onClicked.addListener(async (tab) => {
if (tab.url.startsWith(extensions) || tab.url.startsWith(webstore)) {
const prevState = await chrome.action.getBadgeText({ tabId: tab.id });
const nextState = prevState === 'ON' ? 'OFF' : 'ON';
await chrome.action.setBadgeText({
tabId: tab.id,
text: nextState,
});
if (nextState === "ON") {
await chrome.scripting.insertCSS({
files: ["focus-mode.css"],
target: { tabId: tab.id },
});
} else if (nextState === "OFF") {
await chrome.scripting.removeCSS({
files: ["focus-mode.css"],
target: { tabId: tab.id },
});
}
}
});
コードの解説
chrome.runtime.onInstalled.addListener(() => {
chrome.runtime.onInstalled.addListener
は
- 拡張機能がインストールされた時
- 拡張機能がアップデートされた時
- Google Chromeがアップデートされた時
に引数のコールバック関数を実行する関数です。
chrome.action.setBadgeText({
chrome.action.setBadgeText
[2]はツールバー上の拡張機能のアイコンにバッジを表示する関数です。
text: "OFF",
});
});
ツールバー上の拡張機能のアイコンの初期状態としてOFF
というバッジを表示します。
const extensions = 'https://developer.chrome.com/docs/extensions';
Google Chromeの拡張機能の開発者向けドキュメントのURLの始めの文字列を変数extensions
に格納します。
const webstore = 'https://developer.chrome.com/docs/webstore';
Google Chromeのウェブストアの開発者向けドキュメントのURLの始めの文字列を変数webstore
に格納します。
chrome.action.onClicked.addListener(async (tab) => {
chrome.action.onClicked.addListener
はツールバー上の拡張機能のアイコンがクリックされると、引数のコールバック関数を実行する関数です。
なお、コールバック関数の中で返り値がPromise
となるAPIを使用するため、asyncを付けてコールバック関数を非同期関数としています。
if (tab.url.startsWith(extensions) || tab.url.startsWith(webstore)) {
ツールバー上の拡張機能のアイコンをクリックした時にアクティブになっているタブのURLがhttps://developer.chrome.com/docs/webstore
またはhttps://developer.chrome.com/docs/extensions
から始まるならばという条件を表しています。
const prevState = await chrome.action.getBadgeText({ tabId: tab.id });
chrome.action.getBadgeText
は特定のタブにおけるバッジを取得する関数です。
今回はアイコンをクリックしたタイミングでアクティブになっているタブにおけるバッジを取得し、変数prevState
に格納します。
なお、chrome.action.getBadgeText
の返り値はPromise
のため、await
を付けています。
const nextState = prevState === 'ON' ? 'OFF' : 'ON';
バッジがON
ならばOFF
を、OFF
ならばON
を変数nextState
に格納します。
await chrome.action.setBadgeText({
tabId: tab.id,
text: nextState,
});
アクティブなタブにおいてバッジをON
からOFF
に、またはOFF
からON
に設定します。
if (nextState === "ON") {
変更後のバッジがON
ならばという条件を表しています。
await chrome.scripting.insertCSS({
files: ["focus-mode.css"],
target: { tabId: tab.id },
});
chrome.scripting.insertCSS
はCSSファイルを挿入する関数です。
ここではアクティブなタブにfocus-mode.css
を挿入します。
} else if (nextState === "OFF") {
変更後のバッジがOFF
ならばという条件を表しています。
await chrome.scripting.removeCSS({
files: ["focus-mode.css"],
target: { tabId: tab.id },
});
}
}
});
chrome.scripting.removeCSS
は拡張機能によって挿入されたCSSファイルを削除する関数です。
ここではアクティブなタブからfocus-mode.css
を削除します。
Step 4. スタイルシートを作成
Service Workerに記載したスタイルシートを作成します。
focus-mode
ディレクトリ直下に以下のようなスタイルシートfocus-mode.css
を作成します。
* {
display: none !important;
}
html,
body,
*:has(article),
article,
article * {
display: revert !important;
}
[role='navigation'] {
display: none !important;
}
article {
margin: auto;
max-width: 700px;
}
コードの解説
* {
display: none !important;
}
*
はユニバーサルセレクタと呼ばれ、全ての要素を指定します。
display: none
は要素を非表示にします。
ここでは全ての要素を非表示にします。
!important
は宣言の優先順位が高いことを示しています。
html,
前段で全ての要素を非表示にしたので、ここからは必要最低限の要素を再表示します。
html
要素を再表示します。
body,
body
要素を再表示します。
*:has(article),
子要素にarticle
を持つ全ての親要素を再表示します。
article,
article
要素を再表示します。
article * {
display: revert !important;
}
article
内の全ての要素を再表示します。
その際、revert
を指定することで、ブラウザ既定のスタイルで表示します。
また、!important
と宣言することで、* { display: none !important; }
より優先順位が高いことを示します。
[role='navigation'] {
display: none !important;
}
ナビゲーションは不要なので、ここで再度非表示にしておきます。
article {
margin: auto;
max-width: 700px;
}
article
要素の左右の余白を自動設定にし、横幅の最大値を700pxとします。
Step 5. 動作を確認
拡張機能が完成したので、Google Chromeに読み込みます。
なお、拡張機能の読み込み方法は前々回の記事に記載しているので省略します。
作成した拡張機能が有効になっている状態で、今回のチュートリアルのページを開きます。
そして、Ctrl
キー+B
キーを押下するとバッジがOFF
からON
に切り替わり、スタイルシートが挿入されてページの表示がシンプルになりました。
今回のチュートリアルはこれにて終了です。
Discussion