よんちゅBlog

― このブログは自分用のメモや日々の問題などを共有するためのものです ―

20121005185841 お知らせ:  2013/07/17 ブログデザインをリニューアルしました。

Chrome拡張では、Background pages よりも Event pages を使用したほうが良い

これからChrome拡張を作る方には是非知っておいて欲しい機能、Event pages についての話です。

Event pages は、作成したChrome拡張によっては、必ずしも使用できるわけではありませんが、もし可能なら積極的に使用してほしいと思います。

公式ドキュメントでも、Background pages のページ冒頭には、以下の様な注意書きがなされています。

Caution: Consider using event pages instead. Learn more. via: Background Pages - Google Chrome

なぜ Event pages なのか

理由を詳しく説明すると、
Background pages を使用している拡張は、Chrome起動時にプロセスが起動され、以降Chromeが終了するまで生き続けます。
よって、例え Background pages が何もしていなくてもメモリその他のリソースを専有し続けることになります。

Chrome拡張が起動しているかどうかは、Chromeの Task Manager を見ることで簡単に確認することができます。
実際に見てもらえれば分かりますが、このメモリ消費が結構ばかになりません。

また、Background pages が起動しているかどうかは、Chrome拡張の一覧 (chrome://extensions/) で確認することができます。
(以下は英語表記ですが、日本語にしていれば日本語が表示されると思います。)

f:id:yonchu:20130509190710p:plain
f:id:yonchu:20130509190715p:plain

Chrome拡張 Vimmers follow status の場合

先日私が作ったChrome拡張 Vimmers follow status でも Background pages を使用していました。
(現在のバージョンでは Event pages 対応になっています)


この拡張は、一見すると Content script のみで実現できそうなのですが、Oauth認証を実装するためにどうしても Background pages を用意する必要がありました。
ですが、Oauth認証も常時使用しているわけではありません。
Vimmersページ にアクセスして "Show All!" ボタンをクリックしたときしか使用していません。
それ以外のときは、Background pages では何も行なっておらず、ただメモリだけが専有され続けていました。
ひどいですね。自分で作っといてなんですが、こんな拡張使えたものじゃありません。

そこで、Event pages の出番です。
Backgroud pages とほぼ同じ機能を持ちながら、必要な時だけ起動され、不要になると自動的に終了してくれるという、まさに今回のようなケースに適したものとなっています。

Chrome拡張 ニコ生アリーナ の場合

しかし冒頭でも述べたとおり、必ずしも Event pages が使えるわけではありません。

例えば、Vimmers follow status よりも前にリリースしたChorome拡張 ニコ生アリーナ では、Background pages 内で番組情報などのデータを持っており、バックグランドで定期的に新しい番組のチェックなどを行なっています。

定期的なチェックぐらいなら、alarms API を使用することでなんとかなりますが、番組情報をキャッシュとして持っていなければ、ポップアップを開くたびに取得しなければならず、非常に使いづらいものとなってしまいます。
(他にも極力無駄な通信が発生しないような工夫を施したりしています)

こういった拡張では Event pages を使用することは難しいでしょう。

Background pages を Event pages に移行する方法

では、実際にどうすれば Background pages を Event pages に変更できるか。
そのための指針は公式ドキュメントに示されています。


対象となるChrome拡張がどのような機能を有するかによって修正内容が違ってくるため、まずは Vimmers follow status で行った変更を説明していきます。
といっても大した変更ではありません。

手順1(共通): Manifest(manifest.json)の変更

Manifestを以下のように変更します。

{
  "name": "Vimmers follow status",
  "version": "0.1.0",
  "manifest_version" : 2,
  "description": "VimmersページにTwitterのフォロー状態を表示します。",
  "background" : {
    "page": "background.html"
  },
  // .... 以下省略
}

{
  "name": "Vimmers follow status",
  "version": "0.1.0",
  "manifest_version" : 2,
  "description": "VimmersページにTwitterのフォロー状態を表示します。",
  "background" : {
    "scripts": [
      "js/lib/chrome_ex_oauthsimple.js",
      "js/lib/chrome_ex_oauth.js",
      "js/eventPage.js",
      "js/lib/ga.js"
    ],
    "persistent": false
  },
  // .... 以下省略
}

この変更は Event pages 使用時に必須の変更です。

backgroud.html が不要になったので削除し、background.html 内で読み込んでいたjsファイルを全て scripts に書き出しています。
さらに、"persistent": false を設定することで、Background pages の非永続化を行い、Event pages 方式に変更しています。

手順2: chrome.extension.sendRequest -> chrome.runtime.sendMessage

これは私が知らなかっただけなのですが、
chrome.extension.sendRequest が Chrome ver.20 から deprecated となっていました。
それでも通常の Background pages では使用できていたんですが、Event pages では使用できなくなっていました。

誤って使用すると以下のようなエラーが出ます。

Uncaught Error: sendRequest and onRequest are obsolete. Please use sendMessage and onMessage instead.

親切ですね。おかげで気づけました。

また、Background pages でもいつ使用できなくなるか分かりませんので、使用している方は早めに移行した方が良いかと思います。


新方式の chrome.runtime.sendMessage については以下を参照下さい。


私が対応した限りでは、引数などは変わっていなかったため、リネームするだけで対応できました。

以上が、今回 Vimmers forllow status で修正した点になります。

これで、Vimmers follow status も無駄にメモリを専有しなくなり、経済的な拡張となりました。

以下に diff があるので良ければ参考にして下さい。

Background pages -> Event pages のまとめ

公式ページの Event pages へ移行するためのチェックリスト migrating existing background pages の日本語訳がどこにもないようでしたので意訳してみました。
参考にどうぞ。

background page から event page への変更

background page から event page 変更する場合は、以下のチェックリストに従って下さい。

  1. Manifest に "persistent": false を追加する。
  2. Event page 読み込み時に、毎回必要な受信eventを登録する。event page はChrome拡張をバージョンアップしたときにも一度だけ読み込まれ、登録したイベントの処理が行われる。
  3. Install時、またはUpgrade時のみ必要な初期化処理がある場合は、runtime.onInstalled をイベントを使用する。
  4. もし実行状態をメモリに保持しておく必要が有るなら、storage API または IndexdDB を使用する。event page は長時間ロードされていないため、実行状態を保持するのに global変数 は使用しないほうが良い。
  5. 必要なら、イベント通知を抑制するために event filters を使用する。例えば、tabs.onUpdated イベントを使用したい場合、代わりに filter 付きの webNavigation.onCompleted イベントを使用する (tabs API には filter がありません)。そうすることで、必要なイベントでのみ event page を読み込むことができる。
  6. runtime.onSuspend event を使用することで、event page 終了直前に処理を行うことができる。しかし、それよりも定期的な実行を推奨する。そうすることで、onSuspend イベントを受け取れずにChrome拡張がクラッシュした場合にも、データが失われません。
  7. window.setTimeout() または window.setInterval() の代わりに、alarms API を使用する。もし event page が途中でが終了してしまうような場合、このような DOM-based timers は使用出来ない。
  8. extension.getBackgroundPage の代わりに runtime.getBackgroundPage を使用する。このメソッドは、必要時に event page を起動するため、非同期で実行される。
  9. message passing を使用する場合、不要になった message ports は閉じなければならない。message ports が閉じられるまで、event page は終了することができません。
  10. context menu API を使用する場合、contextMenus.create に String型の id パラメータを設定(idは通常optional)する必要がある。また、onclick パラメータの代わりに contextMenus.onClicked を使用してlistener を登録する。

via: Event Pages - Google Chrome

誤りなどがありましたら、ご指摘頂ければと思います。

また、公式のサンプルアプリも非常に参考になるので、見ておくと良いかと思います。

以上、Event pages の説明でした。