問題

拡張機能やユーザスクリプトでは、 設定値の取得やファイル操作は COM (XPCOM) でバイナリを呼び出して実装することができる。 しかし、この呼び出し文が長い。 ドキュメントのサンプルコードでも通常2行に分けて書いてある位である。 そのような長い文がコードの中ほどに現れてきて読みずらい。というか格好悪い。

さて、今回は、ScrapBookのコードの一部分(Gomita, 2006)を例題としてみようと思う。これは、一般的な、読みやすいコーディングで書かれているものである。

......

function SB_pasteClipboardURL()
{
	try {
		var myClip  = Components.classes['@mozilla.org/widget/clipboard;1'].createInstance(Components.interfaces.nsIClipboard);
		var myTrans = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
		myTrans.addDataFlavor("text/unicode");
		myClip.getData(myTrans, myClip.kGlobalClipboard);
		// データを取得
		var str = new Object();
		var len = new Object();
		myTrans.getTransferData("text/unicode", str, len);
		// 文字列へ変換
		if ( str ) {
			str = str.value.QueryInterface(Components.interfaces.nsISupportsString);
			if ( str.toString().match(/^(http|https|file):\/\//) ) SBURL.value = str + "\n";
		}
	} catch(ex) {	// quiet warning
	}
}

......

2行の意味は、

Components
xpcomの動作に関係するものを集めた組み込みオブジェクトのこと。
classes
xpcomのクラスのリスト。後ろにブランケット([ ])で囲ってあるのは、classes.(@mozila.org/...)と等価だが、点やスラッシュを使うための措置。 ちなみに、単に'class'と呼ばないのは、英語の問題だけでなく、classが予約語で使えないからだと思う。
createInstance
読んで字の如く。後ろにインタフェースの引数をとるのは、同時にqueryInterfaceを実行するからである。

解決策

createInstanceに、適当な別名をつけて、ソースの先頭に置けばよい。

関数xpiClassを書くと、次のようになる。

var xpcomClass = function(name, itf){
 var obj = function() {
  return Components.classes[this.name].createInstance(this.theInterface);};
 obj.name = name;
 obj.theInterface = Components.interfaces[itf] || Components.interfaces.nsISupports;
 return obj;
};
//imports
var Clipboard = xpcomClass("@mozilla.org/widget/clipboard;1", "nsIClipboard");
var Transferable = xpcomClass("@mozilla.org/widget/transferable;1", "nsITransferable");

......

function SB_pasteClipboardURL()
{
	try {
		var myClip  = Clipboard();
		var myTrans = Transferable();
		myTrans.addDataFlavor("text/unicode");
		myClip.getData(myTrans, myClip.kGlobalClipboard);
			.
			.
			.
			.
......