メッセージマネージャ
メッセージマネージャは、chrome スクリプトがコンテンツ領域の内容に触るための方法を提供する、メッセージ受け渡し用のフレームワークです。Firefox 4 においては、chrome スクリプトは XUL の <browser> 要素のプロパティを介してコンテンツ領域の内容や docshell に直接アクセスすることができます。しかしながら、Mobile Firefox 4 および将来のバージョンの Firefox では、コンテンツ領域は chrome とは別のプロセスで実行されるようになり、そのような直接のアクセスは不可能になるでしょう。その代わりに、chrome スクリプトは、コンテンツ領域に同期的に触れなくてはならないコードを別のスクリプト(コンテントスクリプト)に分割して、それらのスクリプトをメッセージマネージャを使って非同期に実行しなくてはなりません。メッセージマネージャは Firefox 4 で既に利用可能ですので、Firefox がコンテンツ領域のプロセス分離型のモデルに移行する前に、Firefox 自身のコードや拡張機能で新しい API をすぐに使い始める事ができます。
メッセージ
メッセージマネージャを介して受け渡されるメッセージは、1つの名前と任意のデータ(省略可)を持ちます。メッセージの送信は非常に単純です:
manager.sendAsyncMessage("メッセージ名", {"foo": 2});
データはメッセージの中では(JSON 文字列形式に)シリアライズされます。これは、メッセージを経由してオブジェクトの参照を送信する事はできないという事を意味します。
メッセージが chrome 領域からコンテンツ領域に送信される時には、chrome スクリプトにはコンテンツ領域の処理をブロックする事が許されていないため、必ず非同期にメッセージを送らなくてはなりません。しかしながら、コンテントスクリプトは chrome スクリプトに同期的にメッセージを送信して、レスポンスを待つ事ができます:
var response = manager.sendSyncMessage("メッセージ名", {"foo": 1});
レスポンスは各リスナが返した値の配列として得られます。
メッセージリスナ
スクリプトはメッセージマネージャに対してメッセージリスナを追加する事ができます:
function listenerFunction(cx) { var messageName = cx.name; var sync = cx.sync; // メッセージが同期的に送信されており、レスポンスが期待されている場合、true になる var data = cx.json; } manager.addMessageListener(listenerFunction); manager.removeMessageListener(listenerFunction);
browser.messageManager
1つの XUL browser
要素につき1つのメッセージマネージャが存在します。ある browser のメッセージマネージャを取得するには、単に browser.messageManager
と書いて下さい。
window.messageManager
あるウィンドウ内に読み込まれているすべての <browser> からのメッセージを受信するために、chrome ウィンドウでは window.messageManager という特別なプロパティも利用できます。メッセージはまず最初に <browser>.messageManager に送られ、次に window.messageManager に送られます。ウィンドウの messageManager に対してのメッセージ送信はできない事に注意してください。messageManager はメッセージ受信用のリスナを登録するためにのみ利用できます。コンテンツ領域のプロセスからメッセージを受信した時には、browser.messageManager のリスナがまず最初に呼ばれ、続いてウィンドウの messageManager のリスナが呼ばれます。複数のメッセージマネージャが1つの同期的なメッセージを監視している場合には、コンテントスクリプトに対してはすべてのメッセージマネージャのリスナの戻り値が1つの配列にまとめられて返されます。(例: ["browser.messageManagerからの戻り値1", "browser.messageManagerからの戻り値2", "window.messageManagerからの戻り値1", "window.messageManagerからの戻り値2"])
コンテントスクリプト
メッセージを使うためには、メッセージを送信または受信して browser に読み込まれた DOM に対して働く「コンテントスクリプト」をインストールしなければなりません。chrome プロセスで動作しているコードは、ある <browser> に何らかのページが読み込まれる前に、browser.messageManager.loadFrameScript() を使って任意のスクリプトをコンテンツ領域のプロセスに注入できます。その後、注入されたスクリプトはコンテンツ領域のプロセスに対してリスナを追加し、chrome プロセスに対して結果のメッセージを送り返す事ができます。
コンテントスクリプトの中では以下のグローバル変数を利用できます:
- content - その browser に読み込まれたページの DOM window。
- docShell - その browser に紐付けられた nsIDocShell。
- addMessageListener
- removeMessageListener
- sendAsyncMessage
- sendSyncMessage
- dump
簡単な例
この単純な例では、HTML の <a>
要素の上で行われたすべてのクリック操作を chrome に転送しています。これは単純化された例で、子孫の要素に対しては期待通りに動作しませんが、しかしメッセージの仕組みがどのように動作するのかを理解する手助けにはなるでしょう。
コンテントスクリプト
このコードは、クリックイベントを chrome プロセスに転送するイベントリスナを準備するために、コンテンツ領域のプロセスで実行されます。
addEventListener("click", function(e) { if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement && sendSyncMessage("linkclick", { href : e.target.href })[0].cancel) { e.preventDefault(); } }, false);
chrome スクリプト
このコードは、コンテントスクリプトを読み込んで、クリックイベントを受け取るために、chrome の領域で実行されます。
browser.messageManager.addMessageListener("linkclick", function(m) { return { cancel: !confirm(m.json.href + "を読み込みますか?") }; } ); browser.messageManager.loadFrameScript("chrome://myextension/content/checklinks.js", true);
参考
- Content Process Event Handlers (デザインドキュメント)