Chrome Extension Manifest V3 では long-lived connection が切断される
Chrome Extension Manifest V3
Chrome Extension を作るときには各種情報を manifest.json に定義しますが、
この Manifest には V2 と V3 が存在しており、それぞれで大きく仕様が異なります。
background の Service Worker 化
Chrome Extension はざっくりと 3 つのレイヤーに分けて考えることができます。
- Inspected window … 実際のタブごとの画面に相当し、Content scripts を介して DOM にアクセスできる
- DevTools page … DevTools ウィンドウに紐づくページ。パネルを作ったり
- Background … その名の通りバックグラウンドで動く。 Inspected window と DevTools page で通信する場合はここを中継する
うち、Background page は V3 では Service Worker として動作します。
短命
Service Worker 版の background スクリプトは、不要になると終了する可能性がある旨がドキュメントに記載されています。
そのため、Chrome Extension が有効な間にデータを永続化したいケースにおいて、V2 では background 用スクリプトでグローバル変数に保持しておけば OK でしたが、V3 では破棄される可能性があるためそのままでは使えなくなりました。
対処として chrome.storage
の Storage API の利用がドキュメントで紹介されています。
long-lived connection が切断される
Inspected window と DevTools page 間の通信では Background を中継しますが、特定のタブと関連付けて通信したい場合には、long-lived connection を利用します。
chrome.runtime.connect
/ chrome.runtime.onConnect
を接続して得られる Port を介して通信することができます。
ここで問題になるのが、Background 側で chrome.runtime.onConnect
で確立した Port が Service Worker と共に一定時間で破棄されてしまい、その時点で接続が切れてしまう事象が起きる点です。
自分が調べた限りでは、Storage API ではこの Port をシリアライズして保持することはできませんでした。
回避策
StackOverflow 上に類似の質問があり、回答の 1 つとして Service Worker のアンロードを防ぐ方法が何点か紹介されています。
ただ、根本な部分は Chromium 自体の問題なようで、現状では long-lived connection が必要なケースでは諦めて Manifest V2 を使うのが手っ取り早かったです。
かなりハマったので、Chrome Extension を作る際はご注意ください 🙏
Discussion