Chrome拡張機能の開発中にManifestをV2からV3変えて動かなくなったので調査

結論

まずは、結論から。

chromeの拡張機能のService Worker上で他オリジンにアクセスしてレスポンスを取得する場合(つまり、オリジン間リソース共有 (CORS))

・axiosは使えない。axiosのadapter取得時にxhrとhttpを使用しようとするが、Service Worker上では両方使えない。なので、fetchを使う。 CORSモードとすること。

・fetchをcrosモードで使うために、host_permissionsに対象のURLを記載する。(permissionsでURLを記述している例を見るが、host_permissionsじゃないとエラーがでる。)


実際のコードとしては、以下のような感じにする。(一部抜粋)

Manifest.json 

"permissions": ["tabs"],  
"host_permissions": ["https://sample.com/*"]


background.ts      

const res = await fetch(baseUrl + word, {        
method: "GET",        
mode: "cors",      
});      
const txt = await res.text();

結論にたどり着くまでの経緯

V2で正常に動いていたけど、V3に変えて動かなくなったのでデバッグして追いかけていた。

エラーの内容は以下。

TypeError: adapter is not a function
at dispatchRequest (dispatchRequest.js:52)    
at background.ts:18    at step (global.js:20)    
at Object.next (global.js:20)    
at global.js:20    
at new Promise (<anonymous>)    
at global.js:20    
at background.ts:8

当初エラーの内容がわからなかったので、まずはどのタイミングでエラーが発生しているか、原因の切り分けをしていくことに。

・選択文字が何かというところまでは、content.jsで把握できており、consoleでも選択文字が認識されている。ここで、調査対象はService Worke(background.js)ということがわかった。

・そもそもManifestがV2時は、axiosでエラーが出てなかった。でもV3に変えるとエラーが出るようになった。ここから考えられるのは、V3だとService Worke(background.js)上でaxiosが使えなくなった?或いは、権限の設定が必要な可能性がある。

background.jsでaxios.getメソッドでエラー吐いている。標準のjavascriptのXMLHttpRequestを使ってリクエストを投げようとしたけど、XMLHttpRequestが未定義と出る。
これが未定義と出るのは、node.jsとかの環境だとあるようだ。
結局、background.jsで使えるHttpRequestの機能が必要なので、それを調査する必要がある。MDNのサイトなので、firefoxとかそっち系?かもしれないけど、以下URLが参考になりそう。

https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Intercept_HTTP_requests

今考えてるのは、上記サイトを参考にして、chrome自体がもつhttpRequest機能を使えるようになる。→chromeの公式docを読んでhttprequestの機能を探す。


途中までchromeの公式doc読んでましたが、挫けた。

っで途中から、v3のService Workerでbackground.jsで他のサイトにアクセスすることが今回の問題ではないかと考えた。
なので、Service Workerからどこかにリクエストを投げてるサンプルを探すことにした。結果、以下youtubeをぼーっとみていたら、なんとなくわかってきた気がする。

https://www.youtube.com/watch?v=UjH2INUPmF4

この動画をみて、現状axiosが正しくimportされてないんじゃないかということを思った。
っが、見ていくとどうもaxiosは正しくimportされ、Service Workerからデバッグしてもaxiosライブラリまで到達できる。
では、これ以外に何が考えられるか。

・axiosは正しくimportされているけど、thenでこけているので非同期処理がおかしい?
一度以下のようにthenではなく、awaitに書き方を変えてみたりした。

結局だめで、デバッグしていくと昨日と同じエラーが出た。

エラーをもう一度じっくり読むと気づいた。
これdispatchRequest.jsがaxiosの一部だから、axiosの原因じゃない?となった。

ただ、axiosが原因だとしても、なんでv2とv3で違いが出るのかはよくわからない。

昨日と同様、adapter is not a functionで調べようと思う。だけど今日は、axiosを添えて。

っで、ここの内容が近い気がする。

https://stackoverflow.com/questions/66305856/typeerror-adapter-is-not-a-function-error-when-using-axios-and-webpack-in-chrom

っがしかし、まだ回答が付いてなかった。
 →これについては、結論に書いたことを回答してあげたほうがいいので後日やっておく。

つぎにこちら。

https://community.cloudflare.com/t/typeerror-e-adapter-s-adapter-is-not-a-function/166469/2

axiosでは、cloudflare workerで実行すると、アダプターチェック(xhrとhttp)の両方が失敗します。解決策としては、フェッチを使う(クロスフェッチではない)とのこと。

cloudflareというのは、以下のサービスみたいで、一緒かわからないけどChrome拡張機能と類似する点があるかもしれない。

https://www.cloudflare.com/ja-jp/learning/what-is-cloudflare/

っで結局、以下の部分が「(xhrとhttp)の両方が失敗します。」の部分だと思われる。

var adapter = config.adapter || defaults.adapter;

ということで、まだ試していないフェッチで書き換えてみる。

let fromTabId = -1;
const baseUrl: string = "https://xxxxxx/";
chrome.runtime.onMessage.addListener(async (request, sender, sendMessage) => {
  fromTabId = sender?.tab?.id ?? -1;
  if (fromTabId == -1) return;
  const contentMsg = plainToClass(
    ContentMessage,
    request.msg as ContentMessage
  );
  if (contentMsg.type === SendMessageMode.SelectMsg) {
    const word = encodeURI(contentMsg.searchWord);
    try {
      const res = await fetch(baseUrl + word, {
        method: "GET",
        mode: "cors",
      });
      const txt = await res.text();
      console.log(txt);
    } catch (error) {
      console.log(error);
    }
  }
});

コンソールで以下のようなエラーが出た。

Access to fetch at ‘https://sample.com/aaaaaa’ from origin ‘chrome-extension://feecfimihahpeagadoldmhnjbfanppce’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.fetch

でモード’no-cors’ってのが使えるのか?こちらを参考に設定する。

https://qiita.com/ryokkkke/items/79f1d338e141d4b7201b

エラー出なくなったけど、次にこちらのエラー。

以下参考に解決できそう。

https://torimemoblog.wordpress.com/2020/07/15/node-js%E3%81%A7dom%E3%82%92%E8%A7%A6%E3%82%8B/

でもまず、responseの中身がみたいので、一度コメントアウトして進める。確認してみると、何の情報もないレスポンスが返ってきてる。

やばい、心が折れそうだ。。

少し寝かせて、以下使えばいいんじゃない?となった。

https://developer.chrome.com/docs/extensions/reference/declarativeNetRequest/

それかこれ

https://developer.chrome.com/docs/extensions/reference/webRequest/

見たけど、なんか違う。

これたぶん、リクエスト受けた時にブロックしたりする機能を作ったりするために使う?っぽい。

原点に立ち返り、エラーの内容。
corsがふわっとしか理解できてない。
なので、こちらの記事を読む。

https://qiita.com/att55/items/2154a8aad8bf1409db2b

なるほど、わかった気にはなった。
ただ、だからどうすればいいかがわからない。

Chrome ExtensionでCORS対応しないといけないとこまではわかった。

ここから再度色々ググってると、以下を発見。

https://qiita.com/okmttdhr/items/09b34e9dadb9895092da

実験してみるが、エラーが出る。

以下のサイトを発見。
これめちゃ重要。

https://www.extension.ninja/blog/post/solved-permission-is-unknown-or-url-pattern-is-malformed/

もともと、permissionsの中に対象のurl入れてたけど、これじゃあダメみたいでhost_permissionsに入れないといけないようだ。  

“permissions”: [“tabs”],  “host_permissions”: [“https://sample.com/*”]
この状態で、もう一度modeをcrosでやるとちゃんとレスポンスデータを受け取ることができた!

っで、再度冒頭の結論となるが、chromeの拡張機能のService Worker上で他オリジンにアクセスしてレスポンスを取得する場合(つまりCROS)・host_permissionsに対象のURLを記載する・xhrとhttpの両方が使えないので、crosモードでfetchを使う。