1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

WebExtensions(Firefox)でレスポンスを編集してWebページに渡す方法

Posted at

Introduction

JSONを受け取った後編集してWebページ側に返す拡張機能を作った際の備忘録です.

JavaScriptわからないマンです.
多分この実装はベストプラクティスじゃないです.

webRequest

  • リクエストを受け取った際に一旦止めて編集できるようにするAPIがwebRequest.filterResponseData
    • セキュリティ上の問題からChrome等のブラウザでは利用不可,Firefoxのみ利用可能
    • レスポンスボディだけでなくヘッダ等も編集可能
      • コンテンツのキャッシュ時間を長くしたりできる
    • イメージとしてはBurpのProxy Interceptに近い?
    • 結構何でもできる.こわい
  • 拡張機能側の権限としてwebRequestwebRequestBlocking,対象の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.jsonruntime.onMessage.addListenerメッセージリスナーを定義->listener関数下からメッセージを投げたらリスナーが見つからずうまくいかなかった.
サイドバーを定義してその中でListnerを立ててメッセージ投げたら普通に動作した.

1
1
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
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?