nsIWindowWatcher::openWindow()で複数の引数をウィンドウに渡すには?宣伝。日経LinuxにてLinuxの基礎?を紹介する漫画「シス管系女子」を連載させていただいています。2010nsIWindowWatcherのopenWindow()って何ですかアドオンで新しいChromeウィンドウ(特権付きのウィンドウ、XPCOMとか自由に使えるやつ。ブラウザウィンドウ等、Firefoxのアプリケーションとしてのウィンドウはだいたいそう。)を開く方法はいくつかある。window.openDialog()を使うnsIWindowWatcherのopenWindow()を使う普通にXULドキュメントの中で読み込まれてるスクリプトからやるのなら、1の方法でいい。JavaScriptコードモジュールやXPCOMコンポーネントのスクリプトのように、グローバルオブジェクトがDOMWindowじゃない場面ではこの方法が使えない。Firefoxが起動した直後でまだブラウザウィンドウすら開かれていないという場面では、nsIWindowMediatorのgetMostRecentWindow()でDOMWindowを取得してそのメソッドを呼ぶ、というようなこともできない。いう時は2の方法を使わないといけない。window.open()と同じだ。URL(文字列)ウィンドウ名(大抵は'_blank'決めうち)ウィンドウの挙動の指定('chrome,all,dialog=no'とか'chrome,all,modal'とかそういうの)window.openDialog()の場合はこの後に続いてさらに引数を指定することができて、第4引数以降に渡した物がそのまま、開かれたウィンドウの中でwindow.argumentsとして参照できるようになっている。Firefoxのブラウザウィンドウは、コマンドライン引数で渡されたURI等をこうやって受け取っている。これと同じことをnsIWindowWatcherのopenWindow()でやろうとして、うまくいかなかった。URL(文字列)ウィンドウ名(大抵は'_blank'決めうち)ウィンドウの挙動の指定ウィンドウに渡す引数となっていて、最初の引数が加わったことを除けば2~4はwindow.openDialog()の第1~第3引数と同じ。問題は、開かれるウィンドウに引数を渡す方法が違うということ。window.openDialog()と同じ感覚で引数を渡しても、期待通りに受け渡されない。arg1);Function.prototype.call()では引数を普通に並べてFunction.prototype.apply()では配列で渡す、という様式を真似てJavaScriptの配列を渡してみても、WindowWatcherは「なんですかコレ」って感じでこっちの意図を読み取ってはくれない。開かれたウィンドウの方でwindow.argumentsを見てみても、長さ1の配列になってて、その要素はなんだかよく分からないnsISupportsのオブジェクトになってる。引数を1つだけ渡す場合だとうまくいくことがあるというのは、このメソッドの第5引数の型がnsISupportsだからだ。nsISupportsをを実装してるオブジェクトならWindowWatcherはちゃんと受け取ってくれて、開かれた側のウィンドウでもwindow.arguments[0].QueryInterface()して使うことができる。やりたいことはあくまで、複数の引数をウィンドウに渡して、開かれたウィンドウの側でwindow.argumentsで配列の要素としてそれぞれを受け取ることなのですよ。いうか、開かれる側のウィンドウがそういう実装になっているため、どうしてもその様式で引数を渡さないといかんのです。nsISupportsArrayとnsISupportsなんちゃらで......XPCOMの世界で配列を扱わないといけない場面でたまーにnsISupportsArrayというのが出てくる。これは名前の通り配列のような性質を持つインターフェースで、openWindow()の第5引数はこのインターフェースを実装してるオブジェクトも受け付けるという風にIDL定義には書いてある。似たような感じでプリミティブ値に対応するようなXPCOMのインターフェースがいくつかあって、nsISupportsStringとかnsISupportsPRBoolとかnsISupportsPRUInt64とかそういうのがいっぱいある。これらのインスタンスをnsISupportsArrayに格納してopenWindow()に渡してやれば、どうやら、開かれた方のウィンドウでは対応するJavaScriptのプリミティブ値としてそれらを受け取れるらしい。こんな事真面目にやってらんないですよね。値の型に合わせてインターフェースを使い分けてインスタンスを作って......とか、めんどくさすぎる。XPCOMにはnsIVariantという便利な物があって、これのsetFromVariant()にJavaScriptの値を適当に渡してやると、あとはnsIVariant君がよろしくやってくれるのです。これを使わない手はない。array);これでめでたく、複数の引数をWindowWatcherからも渡せるようになりました、と。ハッシュは渡せなかったこれで一件落着と思ってたんだけど、この方法が使えない場合があることが分かった。nsIVariantはプリミティブ値でもXPCOMのオブジェクトでも何でも受け渡せる万能なヤツなんだけど、nsISupportsArrayと組み合わせてWindowWatcherに渡す時は、XPCOMのインターフェースを持ってないJavaScriptのネイティブのオブジェクトはこの方法では渡せなかった。});こうやって値を設定した物をnsISupportsArrayに渡しても、開かれたウィンドウの方ではよく分からんnsISupportsのオブジェクトになってしまって、元のオブジェクトが持ってた情報を取り出せなかった。回避方法として思いつくのはJSON.stringify()して渡して、受け取り側でJSON.parse()するnsIPropertyBagにするくらい。JSONにできない物は2の方法でやるしかないと思う。item.value;}という風にしてハッシュに戻す。まとめウィンドウ間の情報の引き渡しはマジ鬼門。あと、上に書いた諸々の処理をライブラリとしてまとめておいたので、同じような事をしようとしてる人は使うといいよ。});とすれば、開かれたウィンドウではwindow.arguments[0].wrappedJSObjectでanyObjectを参照できるようになるのではないでしょうか。titleなるほど、そういう解もありですね。自分はできるだけwrappedJSObjectは使わずに正規のAPIだけで完結させたいという立場(セキュリティモデルの変更等で使えなくなる可能性を恐れてます)なのと、そもそもの発端が、UxUでchrome://uxu/content/以下に置いていたファイルをJSコードモジュール化した時にテスト用のブラウザウィンドウを任意の引数を渡しつつ開く処理でwindow.openDialog()をそのままでは使えなくなってWindowWatcherに置き換えた所で嵌ってしまった、というところだったので、使いたくても使えず残念です。atの末尾に2018年6月21日時点の日本の首相のファミリーネーム(ローマ字で回答)を繋げて下さい。「noda」なら、「2010-09-01_windowwatcher-args.trackbacknoda」です。これは機械的なトラックバックスパムを防止するための措置です。