Pushing latest episodes #2

ということで、ブラウザ側としては拡張機能内でFirebase SDKを使用する構成にする。この組み合わせは、一応Firebaseのドキュメントでも公式に動作が保証されている。ただし、どういうわけか知らないが拡張機能内でFirebaseを使用する際のサンプルやガイダンスはドキュメントに一切ない。そのため備忘録として気付いたことをメモしておく。

ちなみに、上記の保証というのはSDKがchrome-extension:スキームを認識するということである。これは今のところSDKはFirefox上のWebExtensionsでは動作しないことを意味する。したがって、あべ☆アニの最新話の追跡機能もまた、Firefoxでは動作しない。

  • 拡張にSDKを組み込む。これは具体的には<script src="https://www.gstatic.com/firebasejs/8.2.1/firebase-app.js"></script>てな感じの要素をバックグラウンドページに仕込んでおくことである。この場合、外部サイトのファイルを直接指定しているので、manifest.jsに"content_security_policy": "script-src 'self' https://www.gstatic.com; object-src 'self'"といったCSPの記述も必要。また、この拡張をストアにアップする際にリモートコードを使用している旨の告知とその理由が必要になる。
  • Push通知を受けるためにどこかでサービスワーカーを登録しなければならないのだが、Firebase SDKを使う分には明示的にそれをする必要はない。firebase-messaging-sw.jsという名前のファイルをサイトのルート(つまり拡張のアーカイブのルート)に置き、必要な処理をそこに書いとけばいい。ワーカーの登録はFirebaseが勝手にやる。登録済みのregistrationを元にFirebaseアプリケーションオブジェクトを構築することもできるが、Firebase側でよしなにしてくれることをわざわざやることもないだろう。なお登録は不要だがサービスワーカーからのメッセージはリスンする必要があるので、navigator.serviceWorker.addEventListener('message', aFunctionDefinedSomewhereElse);とかはしておく。
  • Push通知を受けるためのトークンをfirebase.messaging().getToken()によって取得することができる。これによって得られる文字列は永続的なものであり、一度取得したら永続的なストレージに入れて使い回していい。この手のトークンにはたいていrefreshイベントをリスンして期限切れに備えましょう的なノウハウがあったりするが、必要ない。onTokenRefreshイベントは用意されているがすでにdeprecateである。
  • このトークンは通知の宛先となるものなので、通知を出す側であるアプリケーションサーバへ送り付ける。さてこれが漏れると勝手に通知を出されるので厳重に暗号化した上で適切に送信しなければならない…はずなのだが、ドキュメントでは単に「トークンを取得したら、それをアプリサーバーに送信して、適切な方法で保管します。」としか記述されていない。送信のためのベストプラクティスとかはないのである。単にpostすればいいらしい。本当? とりあえずあべ☆アニextensionではblowfishで暗号化した上でここのサーバに送信している。まあ拡張のソース見ればすぐバレちゃうけど。
  • Push通知を受けるには、サービスワーカーにおいてもFirebase SDKのコードをimportScript()して、必要な情報を元にFirebaseアプリケーションオブジェクトを生成し、onBackgroundMessageイベントをリスンする…とドキュメントには書いてあり、それが正道なんだろうが、実は単に通知を受けるだけならワーカー自身のpushイベントをリスンするだけで事足りる。FirebaseはPush通知以外にも様々な機能が統合されているので、通知を受けるとともにそれらの機能を利用する場合はドキュメント通りのやりかたが正しいのだろう。あべ☆アニextensionでは通知を単に受けるだけで良いので、直接pushイベントを見ている。importScript()云々もしていない。
  • サービスワーカーで通知を受けたらデータペイロードを拡張のバックグラウンドページへ送信する。ここで困るのはバックグラウンドページがイベントページ、つまり用が済んだら勝手にアンロードされるモードの場合だ。この場合、サービスワーカーのClients#matchAll()ではバックグラウンドページを取得できないし、起こすこともできない。どうするか。実はサービスワーカーのグローバルオブジェクトにchrome.runtime.getBackgroundClient()というものが生えており、それを呼ぶと必要ならバックグラウンドページを起こした上でそれに対応するClientオブジェクトで解決されるPromiseを返すようになっている。このメソッドはChromeの拡張APIドキュメントのどこにも載ってない、この記事だけのかなりのオトク情報である。まあいずれ載ると思うけど。

こんなもんかな。

Leave a Reply

Your email address will not be published. Required fields are marked *