Firefox Developers Conference Summer 2007 での発表の補足も兼ねて、実際に既存の XPCOM コンポーネントを自作のものに置き換える手順を説明していきたいと思います。ちょうど Mozilla でプログラミング (XUL) スレッドで about:blank を置き換える話が出ていたので、それを題材にとってみましょう。
Firefox で about:foo を表示するとき、内部的にはコントラクト ID が "@mozilla.org/network/protocol/about;1?what=foo" である XPCOM コンポーネントが作成されます。このコンポーネントは nsIAboutModule インターフェースを実装している必要があり、その newChannel メソッドで得られたチャンネルの内容がブラウザに表示されます。チャンネルというのはデータストリームの源
であり、ある URI のページを Firefox で表示するときは、URI からチャンネルを作成、そのチャンネルからストリームを取得、ストリームの内容を解析して表示、という流れになります。
それでは早速コンポーネントの実装に入りましょう。実装するインターフェースは nsIAboutModule だけでいいので、新しくインターフェースを定義する (IDL を書く) 必要はありません。
function MyAboutBlank() {
}
MyAboutBlank.prototype = {
newChannel:
function MAB_newChannel(aURI) {
var content =
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>My About Blank</title>
</head>
<body>
<p>Hello, about:blank world!</p>
</body>
</html>;
var uriSpec = "data:application/xhtml+xml;charset=utf-8," +
encodeURI(content.toXMLString());
var ios = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var channel = ios.newChannel(uriSpec, null, null);
channel.originalURI = aURI;
return channel;
},
getURIFlags:
function MAB_getURIFlags(aURI) {
return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT;
},
QueryInterface:
function MAB_QueryInterface(aIID) {
if (aIID.equals(Ci.nsIAboutModule) ||
aIID.equals(Ci.nsISupports))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}
};
newChannel メソッドの役割は上で述べたとおりです。ここでは data URI からチャネルを作成していますが、http URI や chrome URI からチャンネルを作成することもできます。
getURIFlags メソッドでは、その about URI に適用されるフラグを返します。URI_SAFE_FOR_UNTRUSTED_CONTENT を返すことで、一般の Web ページからの読み込み (たとえば、<iframe src="about:blank"> といった使い方) が可能になります。
コンポーネントの登録は通常と代わりありません。クラス ID には新しく生成した UUID を指定します。ただし、コントラクト ID は既存のものを指定します。こうすることで、既存のコントラクト ID に関連付けられたコンポーネントを、自作のものに置換することができます。
const MAB_CLASSID = Components.ID("{80b51603-d8b8-4bcf-aa0b-644f6a196ffe}");
const MAB_CLASSNAME = "My About Blank";
const MAB_CONTRACTID = "@mozilla.org/network/protocol/about;1?what=blank";
var MyAboutBlankFactory = {
createInstance:
function MABF_createInstance(aOuter, aIID) {
if (aOuter)
throw Cr.NS_ERROR_NO_AGGREGATION;
return new MyAboutBlank().QueryInterface(aIID);
}
};
var MyAboutBlankModule = {
registerSelf:
function MABM_registerSelf(aCompMgr, aFile, aLocation, aType) {
var cr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
cr.registerFactoryLocation(MAB_CLASSID, MAB_CLASSNAME, MAB_CONTRACTID,
aFile, aLocation, aType);
},
getClassObject:
function MABM_getClassObject(aCompMgr, aCID, aIID) {
if (!aIID.equals(Ci.nsIFactory))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
if (!aCID.equals(MAB_CLASSID))
throw Cr.NS_ERROR_NO_INTERFACE;
return MyAboutBlankFactory;
},
...
};
セコメントをする