風柳メモ

ソフトウェア・プログラミング関連の覚書が中心。

【覚書】Chrome拡張機能にて、オプション画面の変更を即座に反映させたい場合

Google Chrome の拡張機能で、オプション画面で変更した内容を即座に反映させたい場合には、何らかの方法でオプション画面(options_page)からスクリプト(content_scripts)が動作しているタブ(tabs)に変更を通知してやる必要があるが、そのやり方の一例。

概要

タブ(tabs)上で動作している contents_script は、オプションが格納される(extension 用の) localStorage を直接参照することはできないため、オプションを取得するためにはbackground スクリプトを介在させる必要がある。

このため、典型的には、

  1. background は、あらかじめメッセージ待ち受け状態にしておく(chrome.runtime.onMessage.addListener())。
  2. contents_script がオプションを取得したいタイミングで、background へメッセージを送信(chrome.runtime.sendMessage())。
  3. background は、contents_script よりメッセージを受信すると、localStorage よりオプション値を読み出して適宜変換後*1、contents_script に返す。

のような作りとなる。

また、オプション画面(options_page)の典型的な作りは、

  1. 開いたタイミングで、localStorage よりオプション値を読み出し、ユーザーにわかりやすい形で表示。
  2. ユーザーの入力に応じて、localStorage にオプション値を書き出し。

のようになっている。

ここに新たに

  1. contents_script 側であらかじめメッセージを受け付けられる状態にしておく(chrome.runtime.onMessage.addListener())。
  2. options_page にて、localStorage にオプション値を書き出したタイミングで、contents_scriptの動作しているタブ(tabs)に対して、メッセージを送信(chrome.tabs.sendMessage())。
  3. contents_script は、options_page よりメッセージを受信すると、background よりオプションを取得する。

のような手順を追加してやることで、オプション画面からの変更が即座にタブ側(contents_script)もに反映されるようになる。
なお、options_page→contents_scriptへメッセージを送信するときに変更したオプション内容も一緒に送信してやれば、backgroundへのオプション問い合わせは省略できる。ただしその場合、options_page側でもオプション値の翻訳(ユーザーの入力値から、contents_scriptで用いる値への変換)を実装する必要があり、やや煩雑になる可能性がある。

サンプル

具体的な実装例は、

github.com

を参照のこと。

注意
  • タブに対するメッセージ送信のために、manifest.json に
    "permissions" : [ "tabs" ]
    の記述が必要。

経緯

GoogleChrome拡張機能「twitter画像原寸ボタン」ver. 2.0公開 - hogashi.*を入れていると、ときどき、Originalボタンが表示されなくなってしまう現象があり、調べていた
現時点の最新版である、2.0.4でも発生。

状況としては、

  • しばらく(一晩とか)放置しておいたタイムラインにて、新しいツイートを表示すると、[Original]ボタンが表示されなくなっていることがある。
  • 現象発生時、DOM ツリーに変化があるたびにコールされるようになっている関数(main.js 内の start())までは呼び出されているが、オプション設定を読み込むために発行したchrome.runtime.sendMessage()のコールバック関数が呼び出されていないように見える。
  • background.js 側のchrome.runtime.onMessage.addListener()で設定した関数も呼び出されていない(ただし、他タブでタイムラインを表示したところ、正しく呼び出される)。

ということで、sendMessage() コール後のメッセージ送受信処理のどこかで滞ってしまっている可能性が高い。

もし上記推測通りとすると、Chrome の不具合の可能性が高くなってくるため、ユーザー(拡張機能開発者)側で抜本的な対策を取るのは難しいかもしれない。

回避策としては、現状イベント発生(DOMツリー更新や[Enter]キー押下時)のたびにオプション取得(background.js とのメッセージ送受信)処理を実施しているのをやめ、なるべく頻度を減らす、という方向性はどうか?

  1. main.js が起動した直後の一回だけにする(結果として、オプション変更内容の反映はタブのリロード時に行われることとなるので、リアルタイムではなくなる)。
  2. 起動時とオプション画面からの変更時にのみ、実施する。

自作のスクリプト(原寸びゅー等)では手を抜いて基本、1. の方針にしているが、本来は 2. にすべきだろう……では、オプション変更時にどうやって各タブに通知すればよいのか?

という経緯と思考過程があって、記事にまとめてみた次第。

*1:localStorage には文字列しか格納できないため、contents_script が利用しやすい形(連想配列など)に変換してやる必要がある