Chromeでタブの自動サスペンドを無効化する

2022/03/22に公開

Google Chromeでは少し前のバージョンからautomatic tab discardingという機能が有効化されており、一定時間非アクティブ状態のタブのメモリが自動で解放されるようになっています。

しばらくアクセスしていなかったタブを再度表示した際に自動でリロードが走って画面が更新される時ありませんか?あれです。

システムメモリが逼迫気味になっている時などをよしなに検知して発動するようなので基本的には便利な仕様なのですが、常時アクセスしているWebアプリなどメモリの解放を無効にしたい場合がありますよね。

具体的には自分の場合だとGoogle Colaboratoryでブラウザの接続を維持しておきたい時にメモリの解放を抑制したいケースがありました。

こういう時にどうすればいいのか調べて試してみました。良い感じのやり方を紹介します。

やり方

一番簡単な方法はブラウザのURLバーにchrome://discardsと入力して表示できる画面からAuto Discardableの項目をトグルすることです。


タブ単位で自動サスペンドのオン/オフ切り替えが出来る

この状態でも一旦は使えるのですが、新規タブを開く度に設定し直さないといけないしブラウザを起動するたびに全部オンに戻ってしまうのでちょっと不便です。

毎回いちいちトグルするのは面倒なので出来れば指定したURLに対して自動サスペンドが無効になるような仕組みが欲しいところですね。

そこで今回はChromeの拡張機能を作成して実現することにします。

完成版の拡張機能

まず先に完成品の紹介から。既にストアで公開されており、すぐにインストールして使用することが出来ます。

Dont't discard - Chrome Web Store

設定画面はこんな感じ。


自動サスペンドをオフにするURLを正規表現で指定できる

対象のURLを正規表現で指定しておくとその条件にマッチするURLを表示しているタブの自動サスペンドがオフになります。

これでいちいちchrome://discardsにアクセスしてトグルをポチポチする必要がなくなりました。

仕組み

この拡張機能のGitHubリポジトリはこちら

https://github.com/yuhsak/dont-discard

Chrome拡張で実装可能な機能には色々と種類があり、ものによって使えるChromeのAPIが異なります。

なので基本的には 1.目的の機能を実現するために必要な拡張の種類を選ぶ 2.アクセス可能なChromeのAPIを通して実装する という開発の流れになってきます。

実装可能な機能の例

  • バックグラウンドスクリプト
    • 拡張機能が有効な限り裏側で動作し続けるスクリプト
  • コンテントスクリプト
    • 任意のURLのJSコンテキスト上で動作するスクリプト
  • ポップアップ
    • 拡張機能のアイコンをクリックすると出てくるポップアップ
  • OmniBox
    • URL欄への入力をトリガーにしたアクション
  • キーボードショートカット
  • コンテキストメニュー
  • 拡張機能の設定画面

ここではバックグラウンドスクリプトを使って「タブのURLが変わるタイミングで条件判定を行いマッチしたタブのautoDiscardableをfalseにする」という処理をしていきます。

結局やってることはめちゃくちゃシンプルで出来る限り簡潔にするとこういう感じ。

background.ts
chrome.tabs.onUpdated.addListener((tabId, changInfo, tab) => {
  if (tab.url === 'https://example.com') {
    chrome.tabs.update(tab.id, { autoDiscardable: false })
  }
})

簡単ですね。後は条件の判定部分を設定画面で指定された正規表現を使ったものにするだけです。

Chromeの拡張機能では設定値の保存用にchrome.storageという専用APIが用意されているようなのでこれを使っていきます。

background.ts
type State = { conds: string[] }
const defaultState: State = { conds: [] }

const getConds = async (): Promise<string[]> => {
  return new Promise(resolve => {
    chrome.storage.sync.get(defaultState, state => {
      resolve(state.conds)
    })
  })
}

chrome.tabs.onUpdated.addListener(async (tabId, changInfo, tab) => {
  const conds = await getConds()
  const hit = tab.url && conds.some(cond => cond.test(tab.url))
  if (hit) {
    chrome.tabs.update(tab.id, { autoDiscardable: false })
  }
})

設定画面のスクリプトからも同様にchrome.storage.sync.set(obj, callback)のインターフェースで設定値を保存するようにしたら完成です。

(設定画面のUI部分の操作は拡張機能とあまり関係ないので割愛します)

公式ドキュメントには他にも色々な拡張機能の実装方法が記載されているので興味があれば是非参照してみて下さい。

https://developer.chrome.com/docs/extensions/mv3/

拡張機能開発時の注意点

最近拡張機能のマニフェストがv3にアップデートされました。

v2からの変更点が大きい上に2022/01/17以降はv2マニフェストの拡張機能を開発モードでChromeに読み込むこと自体できなくなってしまったので、Web上にある既存の解説記事の内容そのままでは動作しないことが多くなっています。

自分もv3からバックグラウンドスクリプトがServiceWorkerとして動作するようになったことが原因でaxiosが使えなくなったりしてちょっと困りました。

日本語の解説記事を参照しているとそこらへんでハマったりしやすいと思うので気をつけて下さい。

今は公式のドキュメントがかなり充実しているのでそちらを参照することをおすすめします。

Discussion