現在のページChrome機能拡張のイベントページについて投稿日:2015年7月9日13000イベントページを制するものはChromeの機能拡張を制する「Chrome機能拡張の解説」第5弾。このページではイベントページという仕組みについて解説します。うまく導入するとメリットの大きな仕組みなのですが、なかなか癖が強く、挙動を把握すのに骨が折れます。イベントページの仕組みを理解することは、機能拡張の仕組みを把握するのに大いに役立ちます。そのため、このページではイベントページを通して、メッセージパッシングやストレージ、アラートについて解説します。目次イベントページのメリットバッググラウンドページをイベントページとして機能拡張を作成するイベントページではまりやすいポイントメッセージパッシングの仕組みと、イベントページで実装するポイントバックグラウンドページからページアクション側へメッセージを送信するチャンネルを作成して双方向の通信を行うストレージを利用して変数をやり取りするアラームを利用して長時間のディレイを行うイベントページのメリット前回の解説でバックグラウンドページは機能拡張のロードとともに読み込まれ、常に裏で実行されているということが理解していただけたと思います。常に実行していることは言い換えると、常にメモリに駐在し続けるということでもあります。バックグラウンドページは、現在表示しているページのDOM要素やコンテンツスクリプトとは隔絶されています。それぞれの要素でjQueryなどのライブラリを読み込んでいた場合バージョン違いによるバッティグが起こらないというメリットもありますが、同時にサイズの重いライブラリを複数実行し続けていることになります。下のイラストは1ページ表示するのに3つのjQueryを読み込んでいる例。その対策として実装されたのがイベントページです。イベントページはバックグラウンドページと同じようにインストールや起動時に読み込まれますが、一定の時間が経過すると無効になり、メモリを開放します。再度必要になった時にだけ起動して、再び無効になります。下のイラストはコンテンツスクリプトは実行されて、必要の無くなったページアクションが無効になった例。詳しくは「イベントページの公式ドキュメント」をご覧ください。ありがたいことにバックグラウンドページからイベントページへの移行方法を和訳してくれているサイトがあるので紹介します。使用したほうが良いバッググラウンドページをイベントページとして機能拡張を作成するメモリが開放されるので良いことばかりかというと、そうでもありません。解説を読んだだけでは理解しにくい思うので、実例でイベントページを作成してみます。6ディレクトリを作成し、バージョンを0.6にしました。falseがイベントページ用の記述になります。これで「バックグラウンドページがイベントページとして実行されている」という状態になります。extention」を指定しています。コンテキストメニューはイベントページだとIDを指定しないと表示されないので注意してください。すると通常通り以下のように表示されます。そのまま十数秒待ってください。すると下の画像のように(無効)と表示されます。「これで、無事にイベントページを実装できた...」とはなりません。試しに表示が(無効)になった状態で右クリックから「hoge」をクリックしてみてください。本来であれば「onClickedイベントでクリック」だけが読み込まれるはずですが、「コンテキストメニュー登録完了」も呼び出されてしまいます。イベントページ全体を実行したことがわかります。このようにドキュメントを一読しただけでは理解しにくい不思議な挙動をするので、安易にイベントページを導入すると落とし穴にハマってしまいます。イベントページではまりやすいポイント更にイベントページの特性をわかりやすくするためにgetBackgroundPageを使って解説します。getBackgroundPageは名前から分かる通り、バックグラウンドのWindowオブジェクトを取得できるメソッドです。取得したオブジェクトは普通のWindowオブジェクトと同じように利用できます。7を作り、バージョンを0.7へ変更。"16.png"}}background.jsbackgroundFunctionという名前の関数を作ります。{alert("バックグラウンドの関数を実行!");}myscript.jsgetBackgroundPageでバックグラウンドページのwindowオブジェクトを取得して、backgroundFunctionメソッドを実行。7を読み込み、ツールバーのアイコンをクリックしてください。すると「バックグラウンドの関数を実行!」というアラートが表示されるはずです。このようにバックグラウンドページと、ブラウザアクションページは分離されていますが、APIを利用すれば相互にやり取りすることができます。そのまましばらく待って機能拡張のバックグラウンドページに(無効)が表示されるまで待ちます。(無効)が表示されたら再びツールバーのアイコンをクリックしてください。ポップアップが表示されるだけでアラートが表示されないと思います。これはイベントページが無効になったため、getBackgroundPageでWindowオブジェクトが取得できないためです。pageの項目にruntime.getBackgroundPageに変えるとあります。そこでruntimeのgetBackgroundPageメソッドの解説を見ると「コールバック関数を実行時にバックグラウンドページが読み込まれていることを確認する」とあります。myscript.jsextensionからruntimeに置き換えてコールバックで引数に入ったWindowオブジェクトのメソッドを実行しています。){backgroundPage.backgroundFunction();});バックグラウンドページが(無効)の状態でツールバーのアイコンをクリックしてください。今度は「バックグラウンドの関数を実行!」と表示されると思います。これでイベントページとして読み込んだバックグラウンドページが(無効)になっても必要に応じてイベントページの関数を読み込むことができるようになりました。実はもうひとつはまりやすいポイントがあります。background.js関数の外に「関数の外にあるもの」というアラートを追加します。{alert("バックグラウンドの関数を実行!");}alert("関数の外にあるもの");機能拡張をリロードします。すると初回の読み込みなので「関数の外にあるもの」のアラートが表示されます。(無効)の表示が出た後に、ツールバーのアイコンをクリックしてください。すると「関数の外にあるもの」のアラートが表示されます。これはruntime.getBackgroundPageを実行するとイベントページ全体を読み込んでしまうからです。ページアクション側でruntimeを使って工夫したように、イベントページでも工夫する必要があります。background.js機能拡張のインストール時に実行されるruntime.onInstalledと、起動時に実行されるruntime.onStartupを利用して、アラートを関数内で実行するようにします。7のダウンロード機能拡張をリロードすると「関数の外にあるもの」がアラート表示され、バックグラウンドページが(無効)になったあとに、ツールバーのアイコンをクリックしても「バックグラウンドの関数を実行!」だけが実行されます。このように初回起動時の処理を関数にまとめてイベントリスナーに登録することで、イベントページを呼び出した時に連動して実行されなくなります。このonInstalledとonStartupの組み合わせはよく使うのでセットで覚えておくと便利です。1つ、はまりやすいポイントである「データの渡し方」も見ていきます。メッセージパッシングの仕組みと、イベントページで実装するポイントバックグラウンドページとコンテンツスクリプトやアクションページとの間でデータを相互にやりとりする方法を解説します。合わせてはまりやすいイベントページでの実装方法を解説します。Passing(メッセージパッシング)という仕組みで実装します。object」を渡すことができます。メッセージの送信にはchrome.runtime.sendMessageか、chrome.tabs.sendMessageを使い、メッセージの受信は共にchrome.runtime.onMessage.addListenerを使う。runtime.sendMessageとtabs.sendMessageの使い分けですが、コンテンツスクリプトに送信するときだけtabs.sendMessageを使います。実際に作成してみます。8を作り、バージョンを0.8へ変更。["tabs"]}myscript.jsまずは一番単純な方法でメッセージを送信。runtime.sendMessageでmsmessageという名前のメッセージを送信します。});background.jsruntime.onMessage.addListenerでメッセージを受けるイベントリスナーを作成。)の部分は、メッセージの名前がmsmessageの場合は実行するという仕組みです。複数のメッセージがある場合はよく使う方法なのでセットで覚えておいてください。ページ」をクリックしてコンソールを表示。その状態でツールバーのアイコンをクリックしてください。するとバックグラウンドページ用のコンソールで「myscriptからのメッセージ!」とログが流れます。このように本来であれば、バックグラウンドページとコンテンツスクリプトなどはデータのやり取りができないところをやり取りが可能になります。バックグラウンドページからページアクション側へメッセージを送信する今度は逆にバックグラウンドページからページアクションへメッセージを送信してみます。実はイベントページとして動作しているバックグラウンドページからメッセージを取得するのは厄介です。9を作り、バージョンを0.9へ変更。["tabs"]}myscript.jschrome.runtime.onMessage.addListenerでイベントリスナーを登録。bgmessageという名前のメッセージの場合、コンソールに表示します。バックグラウンドページを呼び出すにはchrome.runtime.getBackgroundPageを使います。{})で実行のタイミングを指定しています。){backgroundPage.backgroundFunction();});});background.jsmyscriptから呼び出せるようにbackgroundFunction()という関数を作成してメッセージを送信しています。9のダウンロード機能拡張ページで読み込んでツールバーのアイコンを右クリックして、「ポップアップを検証」をクリック。すると「バックグラウンドページからのメッセージ!」とコンソールログに流れます。これでバックグラウンドページのメッセージをブラウザアクションで表示することができました。組み合わせれば、コンテンツスクリプトから動的にデータを渡して、バックグラウンドページで処理、ポップアップで受け取って表示を変えるといった複雑な処理もできるようになります。チャンネルを作成して双方向の通信を行うsendMessageやonMessageは一方通行でしたが、チャンネルを作成して永続的な通信を行うこともできます。Passingの公式ドキュメント」で解説されているのですが、サンプルを見ただけだと理解しにくいと思うので段階を追って解説します。Passingの公式ドキュメントまず、チャンネルを作ってmyscript.jsからバックグラウンドページにメッセージを送る方法を理解します。10を作り、バージョンを1.0へ変更。ブラウザアクションとバックグラウンドページを利用します。false}}myscript.jsbackground.jsへメッセージを送ります。chrome.runtime.connectでhogeChannelという名前のチャンネルを作成してportに代入します。そのチャンネルに対してpostMessageでメッセージを送信します。"myscript.jsからのメッセージ!"});background.jsまずはmyscript.jsで送信されたチャンネルをchrome.runtime.onConnect.addListenerで待ち受けます。コールバックで引数にチャンネルが入っているので名前を確認して分岐します。続いてチャンネルに入ったmessageで分岐してメッセージを受けます。"myscript.jsからのメッセージ!"){console.log(msg.msMessage);}});}});機能拡張を読み込んでバックグラウンドページのコンソールを開いた状態で、ツールバーのアイコンをクリックしてmyscript.jsを実行します。するとコンソールログに「myscript.jsからのメッセージ!」とログが流れます。これでチャンネルとメッセージの二段階で設定してメッセージの送受信を行う流れを理解していただけたと思います。これだけだとただ面倒になっただけですが、Connectを使うとバックグラウンドページからの応答をmyscript.js側で受信することができます。myscript.jsport.onMessage.addListenerを追加して応答のメッセージを待ち受けます。{console.log(msg.answer);});background.jsport.postMessageでメッセージを受信したことを作成したチャンネル宛に返します。10のダウンロード再び機能拡張をリロードして、今度はツールバーのアイコンを右クリックして「ポップアップを検証」をクリックします。するとバックグラウンドページからの応答である「メッセージ届いたよ!」がログに流れます。myscript.js側ではチャンネルの分岐をしていないこともポイントです。上で作成したportのチャンネルに対する応答は、port.onMessage.addListenerとすることで限定して受信することができます。このようにConnectを利用すれば、バックグラウンドページやコンテンツスクリプトの特性を活かしつつ、メッセージを相互に送受信することができます。「では、早速グローバル変数なんかを登録して...」となると思いますが、メッセージパッシングはそういった用途には向いていません。イベントリスナーを実行し続けるのでいつまで経っても(無効)になりません。変数を取得するたびにバックグラウンドページが実行されるため、イベントページのメリットである、終了してメモリを開放することができなくなります。単純に変数を渡すにはもっと良い方法が用意されています。ストレージを利用して変数をやり取りする名前から分かる通りメモリ上にデータを保存するstorageのAPIです。APIなら保存したデータを相互にやり取りできます。値には文字列だけでなく、オブジェクトなんかも渡せます。セットしておいた値を取得するだけならバックグラウンドページを呼び出す必要もないため、イベントページとの相性も良くメモリ上に展開されるので速度も高速です。12を作り、バージョンを1.2へ変更。公式ドキュメントにある通り、permissionsにstorageを追加。["storage"]}background.jschrome.storage.local.setで名前と値をセット。"fooVal"});myscript.jschrome.storage.local.getでオブジェクトを取得します。引数はnullにすると全てのストレージオブジェクトを取得できます。コールバックで処理も行えるのでコードの見通し良いですね。12のダウンロード機能拡張ページで読み込んだら(無効)の表示まで十数秒待ち、ポップアップを右クリックし「ポップアップを検証」をクリック。バックグラウンドページが(無効)のまま、変数が取得できているのがわかると思います。APIを利用するのが最も簡単です。APIには他の機能拡張とデータのやり取りを行う機能もあるので気になる方は公式ドキュメントをご覧ください。11を作り、バージョンを1.1へ変更。公式ドキュメントにある通り、permissionsにalarmsを追加。長時間の時間差を試すだけなのでバックグラウンドページのみ。["alarms"]}background.jschrome.alarms.createでmyAlarmという名前のdelay時間が1分のアラームを作成。chrome.alarms.onAlarm.addListenerでアラームのイベントリスナーを作り、アラームネームで分岐しています。一応解説したメッセージも合わせて実行できるということでsendMessageを使ってアラート表示。11のダウンロード機能拡張ページで読み込んみ、しばらく待つと(無効)表示になると思います。その後、1分経つとアラートが表示され、再びバックグラウンドページが有効になります。ちなみにアラートでバックグラウンドページ全体が読み込まれるので再び1分経つとアラートが表示されます。普通はchrome.alarms.createのalarmInfoに第2引数としてperiodInMinutesを指定してインターバルを指定したり、runtime.onInstalled等で一度だけ実行という形になると思います。解説しましたが、バックグラウンドページ(イベントページ)、コンテンツスクリプト、ページアクションといった異なる空間でも関数や変数などのやり取りができることが理解していただけたと思います。イベントページは少し癖がありますが、Chrome全体のメモリ消費が抑えられるというユーザーにとって大きなメリットがあるので、積極的に取り入れていきましょう。8」という名前のディレクトリでbackgroundPage.postGoikenurlを使うと以下のエラーが出て動作しません。extensions::uncaught_exception_handler:8何らかのChrome側のエラーと思われます。絶対に動作するはずのスクリプトが動作しない。いった場合にはディレクトリ名を変更するのもいいかもしれません。次の投稿では「コンテンツスクリプト」について解説します。PM書かれている流れとして、以下の方が正しいのではないでしょうか。PMご指摘ありがとうございます。確かにその通りですね。修正させていただきました。コメントをキャンセルコメントは認証制のため、すぐには反映されません。sukegra」をご利用ください。Share)Linuxの基本の基本。Share)他に運営しているサイトGOいけん世間を騒がせている、あの話題について言いたい。そんなあなたの「ご意見」を世に問うサイト。SUKEGRA「スケグラ」は日本語でプログラミングの悩みを解決するQ&Aサイトです。アクアリウムWikiアクアリウムに関することをみんなでまとめるWikiです。