今回、私が作成したマウスジェスチャのChrome拡張機能 "Our Mouse Gesture"のManifestファイルをv2→v3に変更した際に一番困った「一定時間を経過するとevent.jsでChrome拡張機能のAPIが動作しなくなる。」現象で私が実施した回避方法を記載します。
この現象、ネットで検索しても全然ヒットせず、「私だけ・・・?」と困惑しました。 英語のサイトまで幅を広げて調べると、同じ現象で困っている人は少しヒットするものの誰も問題を解決できていない状況でした。
1. 一定時間を経過するとevent.jsでChrome拡張機能のAPIが動作しなくなる原因
Manifestファイルv3では、event.jsのChrome拡張機能のAPIを動かしてくれるのは、Service Workerと呼ばれるバックグラウンドプロセスになったそうです。
このService WorkerはChromeを起動した後、一定時間経過すると「無効(Inactive)」状態になる時があります。
これが問題の原因でした。
私の作ったマウスジェスチャのChrome拡張機能では、Service Workerが「無効(Inactive)」状態になると、event.jsでChrome拡張機能のAPIが全然機能しなくなり、マウスジェスチャが機能しなくなるという事が判明しました。
2. 原因調査
最初、「これは、もしかしてManifest v3の新しいService Workerという機能の不具合なのでは?」と思い、Chrome拡張機能の小さなサンプルプログラムを作ってテストをしてみました。
するとなんと、サンプルプログラムでは、Service Workerが「無効(Inactive)」状態で、event.jsでChrome拡張機能のAPIを実行すると、Service Workerが「有効(Active)」にちゃんと変わってChrome拡張機能のAPIが動作しました!?
「ということは、私のプログラムに何か問題があるのか?」とも思ったのですが、単にcontents.jsからメッセージを受け取ってevent.jsでAPIを実行しているだけなので、何が悪いのかは全く分かりませんでした。。。
3. 問題の回避策
色々と試行錯誤をしていると、Service Workerが「無効(Inactive)」状態でも、chrome.tabs.create()(タブ作成API)を実行すると、Service Workerが「有効(Active)」になることが分かりました。
そこで以下の回避策を実装しました。
- event.jsがcontents.jsからメッセージを受け取る。
- 「chrome.tabs.create()」を実行。(※同期処理で実行)
- 「chrome.tabs.remove」を実行して、作ったタブをすぐに削除。(※同期処理で実行)
- Service Workerが「有効(Active)」になるまで、0.03秒スリープ。
- マウスジェスチャで実行したいChrome APIを実行。
ポイントは、全て「同期処理」で行うところです。Chrome拡張機能のAPIは基本的に非同期で実行されます。 しかし非同期で実行されると、上記の1~5までを順番に実行できません。 そのため、JavaScriptの「async」「await」を用いて、同期処理で実行されるようにしています。
タブを作ってすぐ削除というコードを実行することで、ブラウザ画面上でタブが一瞬でも作成されるところがユーザーに見えると嫌だなと思ったのですが、幸運にも画面上でタブは作成されませんでした。
以下が実際に実装したソースとなります。
//------------------------------------------------------ // content/optionsからメッセージが送られてきた時の処理 //------------------------------------------------------ chrome.runtime.onMessage.addListener( async(message, sender, sendResponse) =>{ // Service WorkerがInactiveの場合に、Activeへ戻すためタブの作成・削除処理を行う。 await tabCreateRemove(); // マウスジェスチャの処理を実行。 : }
// Service WorkderをInactive → Activeへ変更する為にタブを作成・削除する処理。 let tabCreateRemove = async () => { // Service WorkderをInactive → Activeへ変更する為にタブを作成・削除を実施 const tab = await chrome.tabs.create({active : false}); await chrome.tabs.remove(tab.id); // タブの作成によりService WorkerがActiveになるまで0.03秒だけ待つ。 await _sleep(30); // 呼び出し元でawaitできる用に、Promiseインスタンスを戻す。 return new Promise((resolve, reject) => {resolve()}); } // スリープ処理 const _sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
後書き
私は今回記載した回避策でこの問題に対処しました。
同様の問題に直面している人の助けになれたら幸いです。
もし同様の問題に直面されて困っている人や、他の方法で解決した人がいらっしゃいましたら、コメント欄に記載して頂けますと嬉しいです。
(既に解決した人は、このページに辿り着かないとは思いますが・・・(^_^;))
最後まで読んで頂きありがとうございました。