Chrome拡張機能の概要から公開まで(ManifestV3対応) ~開発編~
はじめに
ManifestV3でChrome拡張機能を公開したので公開までの流れを記事にしました。
本記事は開発編です。関連記事はこちら。
にじさんじオフィシャルストアを使いやすくする拡張機能を作成しました。
本記事では開発で使ったAPIや小ネタを解説します。ここで解説するものは作る拡張機能次第では関係ない部分も多いかもしれません。
デバック方法
公開する場合はフォルダを圧縮する必要がありますが、デバックする場合はフォルダを指定することで拡張機能を動作させることができます。拡張機能の管理ページから"パッケージ化されていない拡張機能を読み込む"からプロジェクトフォルダを選択することで拡張機能を読み込むことができます。拡張機能のファイルを更新した場合は拡張機能の再読み込みボタンを押すことで更新が反映されます。なお、Content Scriptsを更新した場合は該当のwebページも再読み込みしてください。
パッケージ化していない拡張機能を読み込む
Message Passing
概要編でも紹介しましたが、具体的に送信側と受信側に実装について解説します。
送信側はtabs.sendMessage
かruntime.sendMessage
を使います。
tabs.sendMessage
はメッセージの中身と送信するタブIDを引数に取ります。タブ情報はchrome.tabs.query()
で取得できます。いつからかAPIがPromise返すようになりました(ManifestV3から?)。まだ対応していないAPIもあるので公式リファレンスを確認して下さい。
responseにはonMessage
で返された値が入ります。
//runtime.sendMessageの場合
const response = await chrome.runtime.sendMessage({"message":"hogehoge"});
//tabs.sendMessageの場合
chrome.tabs.query({}).then((result) => {
result.forEach((tab) => {
if (tab.url.match(/https:\/\/hogehoge.jp\/.*/)) {
const response = await chrome.tabs.sendMessage(tab.id, {"message":"hogehoge"});
}
});
});
受信側はchrome.runtime.onMessage.addListener()
でListenします。
ここでメッセージを受け取った処理を行い、sendResponse()
でレスポンスを返します。
このときtrue
を返されないとsendMessage
でレスポンスを受け取れません(1敗)。
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.type == 'open') {
//メッセージを受け取った処理
chrome.tabs.create({ url: 'mylibrary.html' }).then((response) => {
sendResponse({ result: 'success', tab: response.id });
});
}
return true; //trueを返す。
});
データの保存
拡張機能を作ると設定項目やユーザーデータを保存したいケースが出てくると思います。
chrome.storage
でユーザーデータの保存ができます。保存場所を用途に合わせて選んでください。保存できる容量にも制限があるのでchrome.storage.local
が無難そう。
API | 特徴 |
---|---|
storage.local | ローカルに保存されます。容量は5MBまで。unlimitedStorage をリクエストすると容量を拡張できます。 |
storage.sync | Googleアカウントに同期した領域にデータが保存できます。容量は100kBまで。 |
storage.session | ブラウザのセッション中に有効なデータ領域です。容量は10MBまで。 |
これらはmanifest.jsonのpermissions
にstorage
を指定することで使うことができます。
"permissions": ["storage"],
storageにはkey-value方式でデータを保存します。
//get keyを指定してvalueを取得
chrome.storage.local
.get("theme")
.then((value) => {
//データ取得した処理
});
//set key-valueのオブジェクトでデータを保存
chrome.storage.local
.set({ "theme": "dark" })
.then(() => {
console.log("Value is set");
});
cookieへのアクセス
拡張機能によってはcookieにアクセスしたいケースもあるかと思います。(ユーザ操作に変わって通信したいときとか)
manifest.jsonにpermissions
とhost_permissions
を記述することで使用することができます。
私はcookieの取得しかしませんでしたが、設定も可能です。
"permissions": ["cookies"],
"host_permissions": ["*://hoge.jp/"],
getAll
でhost_permissions
で指定したホストのcookieをすべて取得できます。
const cookies = await chrome.cookies.getAll({});
通信の監視
ユーザ操作のリクエストの中身を取得して拡張機能で処理したいユースーケースがあり通信の監視方法を調べました。
ManifestV2まではwebRequestAPIでリクエストの中身を見たり、通信をリダイレクトできたりしましたがManifestV3で廃止されました。
かわりにchrome.declarativeNetRequest
で静的パターンマッチしたリクエストのリダイレクトなどができるようになりました。
chrome.declarativeNetRequest
で通信の監視ができなそうだったので他の方法で実装しました。
色々調べましたが下記リンクが実装方法含めまとまっていたのでこれを参考にしています。
実装方法としてはXMLHttpRequest
にモンキーパッチを適用してXMLLHttpRequest.prototype.open
とXMLHttpRequest.send
でリクエストのpayloadとresponseを取得します。
// define monkey patch function
const monkeyPatch = () => {
let oldXHROpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function () {
this.addEventListener('load', function () {
const responseBody = this.responseText;
document.getElementById('myDataHolder').setAttribute('response', responseBody);
});
return oldXHROpen.apply(this, arguments);
};
let oldXHRsend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.send = function (postData) {
document.getElementById('myDataHolder').setAttribute('payload', postData);
return oldXHRsend.apply(this, arguments);
};
};
monkeyPatch();
補足としてinjectedScript
を元リンクではインラインスクリプトでwebページに挿入していますが、ManifestV3ではインラインスクリプトが禁止されています。そのため別途javascrptファイルを挿入する実装に変更しました。それに伴いmanifest.jsonに下記記述をする必要があります。
const injectScript = () => {
var script = document.createElement('script');
script.setAttribute('src', chrome.runtime.getURL('injectedScript.js'));
(document.head || document.documentElement).appendChild(script);
script.remove();
};
"web_accessible_resources": [
{
"resources": ["injectedScript.js"],
"matches": ["https://shop.nijisanji.jp/*"]
}
],
外部ファイルからwebページのリソースにアクセスするためにweb_accessible_resouces
でリソースとwebページを指定します。
Discussion