May 16, 2007

Webページ内のスクリプトからchrome内に処理を渡す

分割ブラウザに、Webページ内のスクリプトから任意の方向に表示領域を分割するAPIを加えた。

今回、最終的に「スクリプトを実行しているページのドキュメントのルート要素に属性値を設定することで挙動を制御する」というあんまりスマートでない仕様に落ち着いたんだけど、これは結構苦肉の策というか、本来やりたかったものとは違う形のソリューションになってしまっている。

そもそもなんでこういう機能(?)を加えたかというと、Stylishという拡張機能の作者さんから「そういうAPIを作ったらどうかね」という意見を頂いたからだ。

Stylishはuserstyles.orgというユーザースタイルシートの投稿サイトと連係して動作するようになっていて、現在見ているページで使えるユーザースタイルシートを表示して、ボタン一発でそれをインストールできるようになっている。要は、そういうAPIがSplit Browserにもあった方がいいんじゃないのか、という話。

まず、内容領域のウィンドウに独自の関数なりメソッドなりを定義する という方法を思いついたけど、これは論外だ。セキュリティホールの温床になりかねないから、よっぽどどうしようもない時以外、この方法は採りたくない。

次に、Stylishがそうしているように独自のイベントを定義して、そのイベントが発行されたらSplit Browserでハンドリングする、という方法を思いついた。でもこれは実験してみたらうまく動かなかった。

var newEvent = document.createEvent('Events');
newEvent.initEvent('SubBrowserAddRequestFromInternal', true, true);
newEvent.targetPosition = 2;
newEvent.targetURI = 'http://www.google.com/';
document.dispatchEvent(newEvent);

こんな感じのスクリプトを書いたテストページを用意してみたんだけど、どういうワケか、こうして発行した独自のイベントは、Chromeウィンドウ内のイベントターゲットにリスナを設定する限り、バブリングフェーズでもキャプチャリングフェーズでも、Chromeウィンドウからはハンドリングすることができなかった。何故だ! clickとかのイベントは問題なくハンドリングできてたのに!!

Stylishの場合、ページ内に特定のidのボタンが存在している場合にのみ、そのページのdocumentをターゲットとしてChromeからイベントハンドラを登録してやる、という方法を採っていた。これなら問題なくイベントを捕捉できるっぽいんだけど、Split Browserでこれをすべてのページに対してやるというのは現実的ではない気がする。

次に、window.open()に渡すURIで制御できないか? と言うことを思いついた。Firefox 1.5以降ではnsIBrowserDOMWindowという仕組みによって、新規ウィンドウに開かれるはずの要求を代わりにタブで開くといった処理が可能になってるんだけど、そこに割り込んで、URIの末尾に ?-moz-split-browser-to=TOP のような引数が付いていればタブを開かずにブラウズ領域を分割する、といったことができるようになるはずだと考えたわけだ。

でもこれも実験してみるとうまく動かなかった。ページ内のスクリプトでwindow.open()を実行した時には、nsBrowserAccess.prototype.openURI()には開こうとしているURIが渡されないのだ。Firefox 2に至るまでのどこかの時点でそういう仕様に変更されたらしい。 window.open()の実装その実体nsIWindowWatcherの実装その実体nsIWindowProviderの、ウィンドウを開いていいかどうか確認する処理 と辿って、ハッキリとこの時nsIBrowserDOMWindowのopenURI()には空のURIを渡すと書いてある箇所にぶち当たってしまったので、これはもう、僕にはどうにもできない世界の話だ。

ただ、openURI()には引数として、そのURIを新規ウィンドウで開こうとしたスクリプトが実行されているウィンドウが渡される。なので、そのウィンドウのdocument.documentElementに属性が設定されているかどうかを見て、タブを開く代わりにブラウズ領域を分割するようにはできた。

……というのが、今回の仕様に落ち着くまでの経緯。なんだかとっても遠回りをした気がする。

エントリを編集します。

wikieditish message: Ready to edit this entry.











拡張機能