f8g

Firefox3でWSHプロトコル

Firefox3のXPCOMUtilsを使うと、短いコードで独自プロトコルを登録できるんですね。

XPCOMUtilsを使わない方法との比較

使わない方法は以下のリンクを参考に。

QueryInterfaceをあんまり書かなくてよくなった

XPCOMUtils.generateQIが作ってくれます。

ProtocolFactoryを書かなくてよくなった

勝手に作ってくれます。自分で定義したい場合は、_xpcom_factoryに書きます。

TestModuleを書かなくてよくなった

XPCOMUtils.generateModuleが勝手にやってくれます。

オレオレプロトコルを書く

以下の必須っぽいものを実装していきます。

classDescription, classID, contractID

XPCOMUtils.generateModuleの中で参照されるものです。全部揃ってないと登録されません。contractIDは、"@mozilla.org/network/protocol;1?name=" + scheme みたいなものになります。

QueryInterface

XPCOMUtils.generateQI([Ci.nsIProtocolHandler])

scheme

登録するスキーム。

newChannel

実際の動作の中身です。ちゃんとnsIChannelを返すようにしないと、Firefoxが落ちるようです。

その他

defaultPort, protocolFlags, allowPort, newURIはコピー・アンド・ペーストで。

コード

Oretocolの中身を変えればいいようにしています。WSHを実行するのにTomblooを使用。「wsh:return 1」と書かないといけないのがちょっとダサい。

const Ci = Components.interfaces;
const Cc = Components.classes;

Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");

var Oretocol = {
	scheme     : "wsh",
	description: "WSH Protocol",
	id         : Components.ID("F32C80B3-404B-4ce0-8590-B9DC25BDFA9A"),
	newChannel : function(aURI){
		var T   = Components.classes['@brasil.to/tombloo-service;1'].getService().wrappedJSObject;
		var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
		var que = decodeURIComponent(aURI.spec).substr(Oretocol.scheme.length + 1);

		eval("var fun = function(){" + que + "}");
		T.log(T.executeWSH(fun));

		return ios.newChannel("javascript:alert((" + fun.toSource() + ")())", null, null);
	}
};

function Protocol(){}

Protocol.prototype = {
	classDescription: Oretocol.description,
	contractID      : Oretocol.contractID,
	classID         : Oretocol.id,
	contractID      : "@mozilla.org/network/protocol;1?name=" + Oretocol.scheme,
	QueryInterface  : XPCOMUtils.generateQI([Ci.nsIProtocolHandler]),

	scheme       : Oretocol.scheme,
	defaultPort  : -1,
	protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE | Ci.nsIProtocolHandler.URI_NOAUTH,

	allowPort: function(port, scheme){
		return false;
	},

	newURI: function(spec, charset, baseURI){
		var uri = Cc["@mozilla.org/network/simple-uri;1"].createInstance(Ci.nsIURI);
		uri.spec = spec;
		return uri;
	},

	newChannel: Oretocol.newChannel
}

function NSGetModule(compMgr, fileSpec){
	return XPCOMUtils.generateModule([Protocol]);
}

そういえば、ios.newChannel("javascript:なんとか", null, null)が全然動かなかったような。

インストール

Latest topics > Firefoxで独自プロトコルを定義する方法 - outsider reflex」の一番下の記述のとおり。ただし、Profilesディレクトリ(WindowsならC\Documents and Settings\ユーザ名\Application Data\Mozilla\Firefox\Profiles\なんたらかんたら)にあるcompreg.datというファイルを削除してから再起動しないと読み込まれないかもしれません。

「about:何とか」も簡単に作れる

Firefoxをインストールしたところのcomponentsディレクトリ(C:\Program Files\Mozilla Firefox\components)にある「aboutなんとか.js」を参考に。

追記

このままだと、どこからでもオレオレプロトコルを実行できてしまい、外部からのヤバイコードも実行してしまいますね。以下のように変更すれば多分大丈夫です。

	protocolFlags: Ci.nsIProtocolHandler.URI_NORELATIVE
	             | Ci.nsIProtocolHandler.URI_NOAUTH
	             | Ci.nsIProtocolHandler.URI_DANGEROUS_TO_LOAD,

nsIProtocolHandler.URI_DANGEROUS_TO_LOAD を設定すると特権的な場所からのみ実行できるようになります。実行制限は、
URI_DANGEROUS_TO_LOAD < URI_IS_UI_RESOURCE < URI_LOADABLE_BY_ANYONE
って感じだそうで。
参考: nsIProtocolHandler - Mozilla | MDN