JavaScript で XTF の実装2005年09月02日 05時31分

この間は JavaScript で XPCOM コンポーネントを作ったので、今回はその発展として XTF の実装に挑戦。XTF (eXtensible Tag Framework) っていうのは XPCOM を用いて新しい XML 要素を実装できるというもので、IE でいうところの Element Behavior (日本語での要約記事) に似てる。Gecko 1.8 (Firefox 1.5) からのサポートなので現時点で試すためには nightly ビルドを使う必要あり。

といってもどんな要素を作ったものかうまく思い浮かばないのでとりあえずカレンダーを作ってみることにする。<calendar xmlns="http://www.ne.jp/asahi/nanto/moon/ns/calendar" /> とすることで今月のカレンダーが表示されるというもの。

ファクトリの作成

XTF ではまず特定の名前空間に対応する XPCOM コンポーネントをひとつ作り、要素の生成はそのコンポーネントが受け持つという形になる。デザインパターンでいうところの Factory Method ってところか。で、そのコンポーネントが実装しなくてはいけないのが nsIXTFElementFactory インターフェース

interface nsIXTFElementFactory : nsISupports
{
  nsIXTFElement createElement(in AString tagName);
};

メンバは createElement メソッドのみ。というわけでサクッと実装。

function nntCalendarFactory() {}

nntCalendarFactory.prototype = {
  createElement: function (tagName)
  {
    switch (tagName) {
      case "calendar":
        return new nntCalendarElement();

      default:
        throw Components.results.NS_ERROR_FAILURE;
    }
  },

  QueryInterface: function (iid)
  {
    if (iid.equals(Components.interfaces.nsIXTFElementFactory) ||
        iid.equals(Components.interfaces.nsISupports))
      return this;

    Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
    return null;
  }
};

nntCalendarElement っていうのが実際の要素に当たるオブジェクト。その解説は後回しにしてとりあえずこれを登録してみる。

const XMLNS_NNT_CALENDAR = "http://www.ne.jp/asahi/nanto/moon/ns/calendar";

var nntCalendarFactoryModule = {
  _description: "XTF Calendar Factory",
  _classId:     Components.ID("{1abb1e29-c7a7-45c7-8670-ba8919f0ae6d}"),
  _contractId:  "@mozilla.org/xtf/element-factory;1?namespace="
                  + XMLNS_NNT_CALENDAR,

  ...

  registerSelf: function (compMgr, fileSpec, location, type)
  {
    ...
    compMgr.registerFactoryLocation(this._classId,
                                    this._description,
                                    this._contractId,
                                    fileSpec,
                                    location,
                                    type);
  },

  ...
};

function NSGetModule(compMgr, fileSpec)
{
  return nntCalendarFactoryModule;
}

"@mozilla.org/xtf/element-factory;1?namespace=NamespaceURI" という Contract ID で Factory を登録することによって、<elementName xmlns="NamespaceURI"> という要素が現れたときに Factory.createElement("elementName") が呼び出されるようになるらしい。CID (Class ID?) は適宜生成

要素オブジェクトの作成

次は実際に要素を表すオブジェクトの作成。これは nsIXTFElement インターフェースを実装しなくてはいけない。

function nntCalendarElement() {}

nntCalendarElement.prototype = {
  // nsIXTFElement の実装
  elementType: Components.interfaces.nsIXTFElement.ELEMENT_TYPE_XML_VISUAL,
  isAttributeHandler: false,
  getScriptingInterfaces: function (count)
  {
    var interfaces = [Components.interfaces.nsIDOMElement,
                      Components.interfaces.nsIDOM3Node,
                      Components.interfaces.nsIDOMEventTarget];

    count.value = interfaces.length;
    return interfaces;
  },

  // nsIXTFVisual (nsIXTFXMLVisual の派生元) の実装
  visualContent: null,
  insertionPoint: null,
  applyDocumentStyleSheets: true,

  // nsIXTFXMLVisual の実装
  onCreated: function (/* nsIXTFXMLVisualWrapper */ wrapper)
  {
    ...

    var builder = Components.classes["@mozilla.org/xtf/xml-contentbuilder;1"]
                            .createInstance(
                              Components.interfaces.nsIXMLContentBuilder);
    builder.setElementNamespace("http://www.w3.org/1999/xhtml");

    builder.beginElement("table");
      ...
    builder.endElement(); // table

    this.visualContent = builder.root;
  },

  // nsISupports の実装
  QueryInterface: function (iid)
  {
    if (iid.equals(Components.interfaces.nsIXTFXMLVisual) ||
        iid.equals(Components.interfaces.nsIXTFVisual) ||
        iid.equals(Components.interfaces.nsIXTFElement) ||
        iid.equals(Components.interfaces.nsISupports))
      return this;

    Components.returnCode = Components.results.NS_ERROR_NO_INTERFACE;
    return null;
  }
};

まずは elementType にそれがどういう種類の要素か指定する。指定できる値は以下のとおり。指定した値によって追加のインターフェースを実装する必要あり。

elementType プロパティに指定できる値
説明 インターフェース
ELEMENT_TYPE_GENERIC_ELEMENT 表示には反映されない要素を表すみたい。HTML での link 要素とか script 要素とかみたいなものか。 nsIXTFGenericElement
ELEMENT_TYPE_SVG_VISUAL SVG を使って表示する要素を表すみたい。 nsIXTFSVGVisual
ELEMENT_TYPE_XML_VISUAL 何らかの内容を表示する一般的な要素。とりあえず SVG を使わないのならこれを指定しておけばいいのか? nsIXTFXMLVisual
ELEMENT_TYPE_XUL_VISUAL XUL 要素と同等の要素? nsIXTFXULVisual
ELEMENT_TYPE_BINDABLE XBL によるバインディングが可能な要素? nsIXTFBindableElement

isAttributeHandler はとりあえず false にしておけばよさそう。true にすると nsIXTFAttributeHandler インターフェースも実装しなくてはいけないから面倒くさそう。

getScriptingInterfacesnsIDOMElement とかを返しておかないと自動的に nsIDOMElement だと認識されない。つまり document.getElementsByTagName("calendar")[0].nodeType とかやっても undefined になる。

void getScriptingInterfaces(out unsigned long count,
                            [array, size_is(count), retval] out nsIIDPtr array);

IDL を見ると out だなんて使われているのでどう実装したらいいものか悩んだが、FAQ によると out の引数にはオブジェクトが渡されるからその value プロパティに値をセットすればいいそうな。配列のほうは JavaScript の配列を自動的に変換してくれる。 retval がついているのは返り値として指定すればいいということみたい。

ここでは elementTypeELEMENT_TYPE_XML_VISUAL を指定していたので nsIXTFXMLVisual インターフェースも実装しなくてはいけない。が、その前にその派生元である nsIXTFVisual インターフェースを実装。まずは visualContent 、ここには実際に表示に使われる要素を指定する。今回は onCreated() の呼び出しの中でセットするが、それ以降は指定される要素自体を変更してはいけない。要素の属性や子要素の変更ならできる。

insertionPoint には visualContent に指定した要素か、その子孫要素を指定する。appendChild とかの実際の適用先がここに指定された要素になる。null を指定しておけば子要素を挿入しても表示されないらしい。

applyDocumentStyleSheets は多分読んで字のごとく。製作者スタイルシートを適用するかどうかだと思う。

そして nsIXTFXMLVisual インターフェースの実装。といっても onCreated メソッドしかない。要素の作成時に呼び出される。今回はここでカレンダーを表す表を作成、visualContent にセットしている。

要素の作成には XMLContentBuilder を使用。最初に名前空間 URI をセットしてから beginElement(tagName)endElement()attrib(name, value)textNode(text) などを使って内容を構築していく。

サンプル

サンプルも作成。getScriptingInterfacesnsIDOMEventTarget を返しておけば、addEventListener でイベントハンドラを登録することもできる。スクリーンショットあり。

パッケージング

そもそも Firefox 1.5 にしか対応していないのでパッケージングも非常に楽。1.0 のことなど考えず chrome.manifest が使えたりする。XPIファイル及びファイル構成は以下のとおり。XPI ファイルをインストールすることで calendar 要素が使用できるようになる。

参考文献

コメント

コメントをどうぞ

※メールアドレスとURLの入力は必須ではありません。 入力されたメールアドレスは記事に反映されず、ブログの管理者のみが参照できます。

※投稿には管理者が設定した質問に答える必要があります。

名前:
メールアドレス:
URL:
次の質問に答えてください:
「ハイパーテキストマークアップ言語」をアルファベット4文字でいうと?

コメント:

トラックバック

このエントリのトラックバックURL: http://nanto.asablo.jp/blog/2005/09/02/60454/tb

_ Days on the Moon - 2006年10月09日 12時34分

気づいたら XTF のインターフェースが大幅に変更されていた。nsIXTFVisual インターフェースとそれに関連する部分が削除されている。全体的な構成はかなりすっきりしたが、前に書いた XTF