JavaScript XPCOM コンポーネントの状況

第 2 草稿

はじめに

私は、 XPJS Components Proposal の中で、 JavaScript を使って xpcom コンポーネントとサービスを実装するシステムの構築を提案しました。他の xpcom コンポーネントは、このコンポーネントを透過的に使うことができます。

その後、 Mike Shaver は、このシステムが動作するようにしてくれました。 このドキュメントでは、その作業の状態と現在の実装におけるシステムの制限を示すことにします。

実装

当然のことながら、システムは、全面的に XPConnect をベースにしています。目玉は、システムにおいて、コンポーネントのインスタンス化に xpcom を使うようにしたことです。 以前は、組み込みのコンポーネントローダーが存在していました。このローダーは、すべてのコンポーネントが C/C++ で実装されていて、そして DLL からロードされたか、または実行時に(非永続的に)登録されたファクトリーインスタンスから作られたと仮定していました。 Mike は、このコードを切替え可能なコンポーネントローダーシステムに組み立て直しました。 その後、彼は DLL からロードするネイティブなコンポーネント用のローダーを作成しました。また、 .jsファイルからロードする JavaScript コンポーネント用のローダーを作成しました。 これは拡張可能なシステムであり、将来、他の人が追加のローダーを書いてくれることを期待できます。

JS コンポーネントローダーの実装は、 /js/src/xpconnect/loader/ にあります。 私の知る限り、 Mike Shaver は、その使い方について詳細な文書を書いていません。 簡単に言うと、JS コンポーネントローダーは、自動登録時に、コンポーネントとして登録して使用するために、 'components' ディレクトリにあるすべての .js ファイルをロードしようとします。 つまり、DLL のネイティブコンポーネントがあるのと同じディレクトリーです。 アプリケーションが起動するたびにこれらのファイルを探すのか、それとも自動登録時だけに探すのかは、良く分かりません。[shaver がはっきりさせてくれるのかな ?]

私が知っている最良のドキュメントは、サンプルにあるxpcom/sample/nsSample.js です。 ただ、これがサンプルであり、同時にちょっとしたテストベッドであるのは意味がないと思います。 この中には、不要なデバッグコードがいくつかあります。そして、定型的なコードは、もう少し再構成し、カスタマイズ可能な部分は変数として分離するべきだと思います。そうすれば、将来の JS コンポーネント実装者は、単に共通のブロックをコピーして、自分自身の progid と iid などの文字列を埋めるだけでよくなります。 それでもやはり、このサンプルは現に使えるものであり、サンプルにあるコメントを読めば、どのようにシステムを使えば良いのかが理解できます。

この他のシステムの進化についての情報元として(ハードコアな好奇心のために)、 xpcom ニュースグループ があります。JS components、component loader といったキーワードで題名を探してください。

JS コンポーネントの適切な例としては、 components ディレクトリーにある .js ファイルを挙げることができます。 現在のところ、例えば、サイドバーコンポーネントがあります。Mike Shaver は、 JS を使って、カテゴリーマネージャを実装しました。もっともこれは、後で C++ を使って再実装されています。 別に JS での実装が実用的でなかったわけではなく、ただ単に XPCOM のコアサービスが JavaScript と XPConnect を必要とするのは良くない、と判断されたためでした。 これは、ブラウザ以外の用途の埋め込みの場合、これ以外には JavaScript を必要としないので、 xpcom の有用性を損なってしまう恐れがあるためです。

実装の公開

2000 年 8 月 1 日まで、JS コンポーネントには、重大な問題がありました... JS コードから JS コンポーネントを使った場合、他の xpcom コンポーネントとは見え方が違っていたのです。 この問題は、xpconnect が JS コードから、実装の JS コンポーネントの JSObject へ直接アクセスすることを許していたことが元になっています。 つまり、呼び出し側は、宣言されたインタフェースに限定されず、すべてのメソッドとプロパティにアクセスすることができた、ということです。 これは同時に、他の xpconnect 基盤が実装公開された JS コンポーネントを任意の xpcom インタフェースとして認識できないということも意味します。 これらの問題の議論については、ここを見てください。

長期的な対策としては、この問題は、この目的に特化した第 3 の xpconnect ラッパーにより修正されるでしょう。 短期的な対策として、ネイティブコードから JS コードに対して JS コンポーネントが渡される時に、JS コンポーネントの '二重ラップ' を行うことにしました。 つまり、 JS コンポーネントが作成されて、ネイティブコードに渡される時、 xpconnect はネイティブコードが使うためのラッパーを作ります。そして、そのラッパーが後で JS コードに渡される時、 xpconnect は、そのラッパーの周りに JS から使うためのラッパーを作ります。 これにより、JS コンポーネントはネイティブな xpcom コンポーネントとほとんど区別がつかなくなりました。 ただし、一つ違いがあります。

まれに、JS コードの呼び出しから、実装している JS コンポーネントの JSObjectに実際にアクセスする必要がある可能性があります。 このため、 xpcom コンポーネントの回りの xpconnect ラッパーは、現在 wrappedJSObject というプロパティをサポートしています。 これにより、 JS コードでは、以下のようなことができます...

// 'foo' は、JS で実装されている可能性がある xpcom オブジェクトです。
var underlyingObject = foo.wrappedJSObject;
if(underlyingObject) {
   underlyingObject.somePropertyThatIsNotPartOfTheInterface = 'bar';    
}
もし 'foo' が JS で実装されていない場合、foo.wrappedJSObjectは、undefined を返します。 また、'foo' が JSで実装されている場合でも、これは実装の JSObjectが 'wrappedJSObject' というプロパティを持っている場合だけ成功します(このプロパティには、たいてい JSObject 自身の this が設定されています)。 つまり、実装の JSObject を明示したい JS コンポーネントだけがこのように実装を公開することができます。 これにより、それらのオブジェクトが他の JSObject を返すことも(望めば)できるようになります。 XPConnect は、セキュリティシステムの使用もサポートしており、この動作を続ける前に特別なチェックを行うことができます。 ただし、今のところ、すべての XPConnect アクティビティーは UniversalXPConnect '超特権'を持っているものとして、チェックされています。(訳者注: 原文あやまりか ? )

バグ

  • 24688 - ラップされた JS での実行時エラーが分からない。
    今は、JS コンソールサービスがあるので、実行時エラーをそこに送ることができます。 Mike McCabe は、このバグを修正するパッチを持っています。現在、JS コンポーネントでの JS エラーは、xpconnect の層でつかまえられ、デバッグビルドにおけるネイティブコンソールに表示されます。ただし、エラーは、リリースビルドでは落とされます。 これは、 JS コンポーネントの開発とテストを遅らせることになります。
  • 25180 - xpconnect で、JS -> JS の通信のためのラッパーが必要。
    私は、暫定的解決策を(実装して)チェックインしました。これは、JS コンポーネントが JS コードに渡された時に JS コンポーネントを '二重ラップ' するものです。 これは、特に効率的というわけではありません。しかし、以前の方法のように 単純に JSObject をそのまま見せるよりは良い方法です
  • 27867 - xpconnect が DOM オブジェクトをうまく扱えない場合がある。
    JS コンポーネントを使って行いたいことの一つとして、DOMやその他のユーザインタフェースの操作があります。 JS コンポーネントと DOM オブジェクトの間のやり取りは、ほとんどの場合可能です。 ただし、現在のシステムでは、いくつかの場合が全くサポートされていません。 このパスを通るような処理をすると、やりたいことがある時点でできなくなることがあり得ます。 最終的には、DOM システムが xpconnect を使うように実装しなおして、これらの制限がなくなるようにする予定です。

    我々は、DOM ラッパーと xpconnect ラッパーの間のやり取りを改善しましたが、しかし、まだ、"ある場所から他の場所へ進めない"場合があります。

    ある DOM コードをメイン UI スレッド以外のスレッドで動作させないようにも気をつけなければいけません。 JS コンポーネントは、一般的にどのスレッド上で動作するかを制御できません。

結論と推奨

JavaScrip コンポーネントの未来は明るいと思います。気をつけなければいけない制限、特に DOM を扱う際の制限がありますが、JS コンポーネントは、いろいろなタスクに適しています。

ドキュメントの履歴...