X-0028 個々のフレームに対応するnsIDocShellのオブジェクトを取得する

nsIDocShellとは

Document Shellは「Webページのウィンドウ」それぞれに対応する「仮想的なWebブラウザ」と呼べるようなもので、全てのウィンドウ・フレームについて存在しています。それぞれのウィンドウ・フレームごとの「次に進む」「前に戻る」「ページ内を検索」といった機能は、全てこのDocument Shellのメソッドで(正確には、Document ShellからさらにnsIWebNavigationインターフェースを経由します)提供されています……多分。

このDocument ShellはXPCOMにおいてnsIDocShellというインターフェース名で振る舞いが定義されています。XULのツールキットの中ではbrowserおよびtabbrowserウィジェットのdocShellプロパティがnsIDocShellのインスタンスとしてアクセス可能で、例えば現在Navigator/Browserで閲覧中のWebページのDocument Shellは gBrowser.docShell で取得できます。

前述したとおり、Document ShellはWebブラウザの機能の中枢を担っています。これを直接操作すれば、POSTメソッドで任意の内容を送信したり、ページ中を任意の文字列で検索したりということが可能になります。日本語でのインクリメンタルサーチを実現するXUL/Migemoもその応用例の一つと言えます。

Document Shellの利用

一番外側のフレームのDocument Shellにしかアクセスできない?

ところが、browserのdocShellプロパティが参照しているのはあくまでトップレベルのフレーム(のwindowオブジェクト。Webページから見た時のwindow.top。)に対応するDocument Shellです。フレーム分けされたページやインラインフレームで取り込まれているページのフレームそれぞれに対応するものではないので、これでは、先述したような「応用」はトップレベルのフレームに対してしか利用できません。

では、個々のフレームに対応するDocument Shellにアクセスするのは不可能なのでしょうか。いえいえ、そんなことはありません。以下のやり方を使えば、実際に個々のフレームのDocument Shellにアクセスできます。

Document Shellのツリー構造を利用する

フレームはツリー構造になっていて、parentプロパティやframesプロパティでそれぞれのフレームを辿っていくことができます。実はDocument Shellもそれと同じようなツリー構造を持っていて、子フレームに対応するDocument Shellから親フレームのDocument Shellを、親フレームのDocument Shellから子フレームのDocument Shellを辿っていくことができます。

ここで使用するのがnsIDocShellTreeItemインターフェースのnsIDocShellTreeItem.parentnsIDocShellTreeNodeインターフェースのnsIDocShellTreeNode.getChildAt()です。以下に例を示します。

var docShell         = gBrowser.docShell;
var docShellTreeNode = docShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);

var forFirstFrame  = docShellTreeNode.getChildAt(0)
                       .QueryInterface(Components.interfaces.nsIDocShell);
var forSecondFrame = docShellTreeNode.getChildAt(1)
                       .QueryInterface(Components.interfaces.nsIDocShell);

var current        = forFirstFrame;
var forParentFrame = current
                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                       .parent
                       .QueryInterface(Components.interfaces.nsIDocShell);

windowから直接Document Shellを取得する

先の例とは別のアプローチで、フレームに対応するDocument Shellをフレームから取得するやり方もあります。先の例がトップダウン方式なら、こちらはボトムアップ方式といえますね。以下の例を見て下さい。

var win = document.commandDispatcher.focusedWindow; // 現在アクティブなフレーム
// var win = gBrowser.contentWindow; // トップレベルのフレーム
// var win = gBrowser.contentWindow.frames[1]; // 二つめのフレーム
var docShell = win
	.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
	.getInterface(Components.interfaces.nsIWebNavigation)
	.QueryInterface(Components.interfaces.nsIDocShell);

ここではnsIWebNavigationインターフェースがDocument Shellとフレームを繋ぐ架け橋のような役割を果たしています。