Chrome拡張機能のアップデート直後にだけ特定の処理を走らせたい

# はじめに
Chrome拡張機能にかかわらず、自身が開発している製品について何らかの仕様変更をリリースする際、
`今回のバージョンから仕様変更したから、バージョンアップ直後にだけこの処理を走らせたいな～`ということってありますよね。

Chrome拡張機能の開発時に上記をやろうとして、意外と調べるのに時間がかかったのでまとめておきます。

なお、Manifest V3ベースでのChrome拡張機能の作り方は、以下にまとめているので参考にしてください。

https://qiita.com/moromi25/items/dada185e5f1e07fc78fb




# 実装方法
`chrome.runtime.onInstalled`を利用します。
https://developer.chrome.com/docs/extensions/reference/runtime/#event-onInstalled

onInstalledという名前ですが、イベント実行時に渡されるオブジェクトの中にある`reason`で更新があったことを判定できます。

reasonが`update`のときのみ処理を実行するように`Service Worker`(=background.js)に記載します。

具体的な実装はこちら。
```json:manifest.json
{
  ...
  "background" : {
    "service_worker" : "./js/background.js"
  }
}
```
```js:background.js
chrome.runtime.onInstalled.addListener((details) => {
    if (!(details.reason === "update" && details.previousVersion === "1.0.0")) {
        console.log("対象外バージョンのため終了します");
        return;
    }
    console.log("対象バージョンのため更新処理をかけます");
    ...
});
```
:::note warn
Chrome拡張機能は、個人利用であれば基本的に自動更新されるはずです。
ただし、Chrome Enterprise/Educationでは[Chrome拡張機能の更新を制御する方法](https://support.google.com/chrome/a/answer/11190170?hl=JA)があるので、previousVersionの判定を1つ前だけに固定していいのかは、よく検討した方がよさそうです。
:::
## chrome.runtime.onInstalled実行時に渡されるオブジェクトの中身
### reason
chrome.runtime.onInstalledの実行理由です。以下のいずれかが入ります。
- update: この拡張機能が更新された
- install: この拡張機能がインストールされた
- chrome_update: Chromeが更新された
- shared_module_update: この拡張機能が利用している別の拡張機能が更新された

今回利用したのは`update`でしたが、`install`も初期設定などをしてもらいたいときに使えそうです。

### previousVersion
reasonがupdateの場合のみ、更新前のバージョン番号が入ります。manifest.jsonの`version`部分です。string型です。

### id
reasonがshared_module_updateのときのみ、更新のあったこの拡張機能が利用している別の拡張機能のIDが入ります。


# デバッグ方法

## 1. 拡張機能の管理ページからService Workerビューを検証
拡張機能の管理ページ[^拡張機能の管理ページ] > ビューを検証 > Service Worker をクリックします。

[^拡張機能の管理ページ]:`chrome://extensions/`の一覧、または開発中の拡張機能のアイコン右クリック > 拡張機能を管理をクリックで管理画面へ遷移できます

すべての拡張機能の管理ページの場合：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366737/53b45fd7-8a75-b05e-0b49-041062136774.png)

詳細ページの場合：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366737/b63b60f2-5b9a-ebd2-a671-5f4018ddd8af.png)

## 2. manifest.jsonで指定したjsファイルを探し、ブレークポイントを貼る
Service WorkerをクリックするとDev Toolsが起動するので、
ソースタブ > ページタブ > 拡張機能のidディレクトリ > 指定したjsファイル（今回の例だとjs/background.js）
と辿り、ブレークポイントを貼ります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366737/077f06fa-a53a-9900-c461-47ca5706fc12.png)

## 3. 拡張機能を更新
拡張機能の管理ページ > 更新ボタンをクリックします。

2で貼ったブレークポイントがchrome.runtime.onInstalled実行直後の行であれば、このタイミングで止まるはずです。

# 注意点：`previousVersion`は拡張機能の更新ボタンを押す直前に読み込まれていたバージョンが入る
`previousVersion`はその名の通り、拡張機能を更新する直前のバージョンを取ってきます。
開発中であれば、それは**更新ボタンを押す直前のバージョン**となります。

開発中の場合、同じバージョン内で何度も修正→デバッグを繰り返すために拡張機能の更新を繰り返しますよね？

たとえば

現在のバージョン 1.0.0 に機能追加をすることになった
↓
manifest.jsonのversionを 1.1.0 に書き換えて機能追加
↓
拡張機能を更新ボタンクリック！　　<--- previousVersionは **1.0.0**
↓
仕様変更
↓
拡張機能を更新ボタンクリック！　　<--- previousVersionは **1.1.0**
↓
不具合を見つけたので修正・・・
↓
拡張機能を更新ボタンクリック！　　<--- previousVersionは **1.1.0**

となります。

もしロジックの中でpreviousVersionを 1.1.0 直指定して何らかの判定していると途中で動かなくなりハマる可能性があるので注意しましょう。


# 余談：アンインストール時に処理を実行させる方法はあるのか？
[アンインストール時に遷移させるURLの指定](https://developer.chrome.com/docs/extensions/reference/runtime/#method-setUninstallURL)ができました。
アンインストールに伴うサーバーサイドのデータ削除やユーザーフィードバックを得るのに活用できそうです。


# おわりに
以上がChrome拡張機能の更新時に処理を実行させるための実装サンプルとデバッグ方法+αでした。
どなたかのお役に立てると幸いです。
記載内容に齟齬があればご指摘ください。

# 参考資料
- [Chrome Developers公式ドキュメント#chrome.runtime.onInstalled](https://developer.chrome.com/docs/extensions/reference/runtime/#event-onInstalled)
- [Chrome Developers公式ドキュメント#chrome.runtime.setUninstallURL](https://developer.chrome.com/docs/extensions/reference/runtime/#method-setUninstallURL)
- [Chrome のアプリまたは拡張機能の更新を固定する - Chrome Enterprise and Education ヘルプ](https://support.google.com/chrome/a/answer/11190170?hl=JA)