クロージャを使ってISleipnirにアクセスする

8日の記事でIHTMLWindow2にISleipnirを渡すのは危ないよってなことを

ISleipnirを渡すSeaHorseスクリプトを不特定多数のサイトで使うのはだめですね。いたずらでいきなり全タブを閉じるとかされたりするかも。

griffin-stewieの日記 - セキュリティ

の前半ISleipnirを不特定のサイトに渡すのはまずいという程度の意識でさらっと書いたのだけど、いたずらでいきなり全タブを閉じるくらいだとIAPIでも出来てしまう。個人的にはISleipnir渡さなくてもIDatabaseとIAPI渡せればほぼ困ることは無いと思ってたがIAPIも渡せないとなるとちょっと困るかもしれない。*1
引用元にあるようにクロージャを使ってSeaHorse側から追加した関数からだけISleipnirにアクセスできるようにする。クロージャの使い方に関してははてなのオートリンク先やwikipediaの例がわかりやすい。"関数を返す関数"は面白いけど使い道が長らくわからなかったけどこう使うのね。TridentのスクリプトでattachEventの関数に引数を渡せないのもこれ使えばすっきり解決できそう。
本題の、ISleipnirをSeaHorseで追加した関数からだけアクセスできるようにするには、

_window.tempnir = sleipnir;
_window.execScript("setStatus = (" + setStatus + ")();", "JavaScript");
_window.setTimeout("setStatus('Sleipnir')", 1000);
_window.tempnir = null;

function setStatus()
{
	var sleipnir = tempnir;
	return function(arg)//関数本体。ここの中身がページ内でsetStatusになる。
	{
		sleipnir.Status = arg;
	};
}

一時的にIHTMLWindow2のtempnirにISleipnirを渡しておき、execScriptでクロージャ内のsleipnirにコピーしたあとで開放する。ページ内の他のスクリプトからはsleipnirは見えないがsetStatusからはsleipnirをISleipnirとして扱え、setStatusはページ内のスクリプトとして振舞う。
今回は必然性があってexecScriptを使ったが、他のスクリプトでもスクリプト要素を付加するよりこっちの方がすっきりするかもしれない。

*1:ISleipnirが渡せることによる利点はRunScriptくらいだと思う。独自右クリックメニューを自前で実装するとかやら無い限り特に必要ない気がする。