Introduction
JSONを受け取った後編集してWebページ側に返す拡張機能を作った際の備忘録です.
JavaScriptわからないマンです.
多分この実装はベストプラクティスじゃないです.
webRequest
- リクエストを受け取った際に一旦止めて編集できるようにするAPIが
webRequest.filterResponseData
- セキュリティ上の問題からChrome等のブラウザでは利用不可,Firefoxのみ利用可能
- レスポンスボディだけでなくヘッダ等も編集可能
- コンテンツのキャッシュ時間を長くしたりできる
- イメージとしてはBurpのProxy Interceptに近い?
- 結構何でもできる.こわい
- 拡張機能側の権限として
webRequest
とwebRequestBlocking
,対象のURLを定義する必要がある - データが分割された状態で受信される
-
ondata
でバッファに格納,onstop
でパース処理 - Content-Lengthによる?
-
/manifest.json
{
"manifest_version": 2,
"name": "XHREditTest",
"version": "1.0",
"description": "HogeHoge",
"permissions": [
"webRequest",
"webRequestBlocking",
"https://example.com/*" // フィルタ対象のURL
],
"background": {
"scripts": ["background.js"],
"type": "module",
"persistent": true
},
}
/background.js
import { createRequestListener } from "./modules/createRequestListener.js";
console.log("Background Script Loaded");
createRequestListener();
/modules/createRequestListener.js
import { listener } from "./listener.js";
export { createRequestListener };
// フィルタ対象のURLを列挙
const urlPatterns = [
"https://example.com/api/hoge", // w/o URL Params
"https://example.com/api/hoge*", // w/ URL Params
];
function createRequestListener() {
browser.webRequest.onBeforeRequest.addListener(
listener,
{
urls: urlPatterns,
types: ["xmlhttprequest"],
},
["blocking"]
);
}
/modules/listener.js
export { listener };
async function listener(details) {
let filter = browser.webRequest.filterResponseData(details.requestId);
let decoder = new TextDecoder("utf-8");
let encoder = new TextEncoder();
let buf = "";
let url = new URL(details.url);
filter.ondata = (event) => {
// 受信時の処理. データは複数回に分けて受信される場合がある
// 受け取った文字列をデコードする
let str = decoder.decode(event.data, { stream: true });
buf += str;
};
filter.onstop = (event) => { // すべてのデータを受け取った際の処理
if (isCompleteJSON(buf)) { // JSONのチェック
let json, res;
try {
json = JSON.parse(String.raw`${buf}`); // JSONをパース
} catch (error) { // エラー処理
console.error("Error parsing JSON: " + error.message);
// 何もせず返す
filter.write(event.data);
filter.disconnect();
return;
}
res = hoge(); // 煮るなり焼くなりする
// 編集したJSONを文字列にして返す
filter.write(encoder.encode(JSON.stringify(res)));
filter.disconnect();
} else {
console.error("Error: Incomplete JSON");
// JSONがダメそうだったときは何もせずに返す
filter.write(event.data);
filter.disconnect();
}
buf = "";
};
}
function isCompleteJSON(str) { // チェック用関数
try {
JSON.parse(String.raw`${str}`);
return true;
} catch (e) {
return false;
}
}
ハマったところ
background.json
内runtime.onMessage.addListener
メッセージリスナーを定義->listener
関数下からメッセージを投げたらリスナーが見つからずうまくいかなかった.
サイドバーを定義してその中でListnerを立ててメッセージ投げたら普通に動作した.