このセクションでは、Mozilla が使用しているオブジェクトシステムである XPCOM (Cross-platform Component Object Model) の概要を見ることにしましょう。
XUL を使うことによって、複雑なユーザーインターフェースを組み立てることができます。また、インターフェースを更新し、タスクを実行するスクリプトを貼り付けることもできます。しかし、直接 JavaScript を使うだけでは行なうことのできないことも数多くあります。例えば、メールアプリケーションを作りたい場合、メールの送受信をするため、メールサーバーに接続するスクリプトを書く必要があります。JavaScript には、そうしたことを行なう能力がありません。
これを処理する唯一の方法は、メールを取得するネイティブコードを書くことでしょう。また、スクリプトからそのネイティブコードを簡単に呼び出すことができなくてはなりません。Mozilla には、こうした方法が用意されています。XPCOM (Cross-platform Component Object Model) の使用です。
Mozilla は、それぞれ決まったタスクを実行するコンポーネントの集合から組み立てられています。例えば、メニューやボタン、要素それぞれのためのコンポーネントがあります。そうしたコンポーネントは、インターフェースと呼ばれる多数の定義から組み立てられています。
Mozilla のインターフェースとは、コンポーネントが実装する機能セットの定義です。コンポーネントとは、様々なことを行なう Mozilla 内のコードを実装するものです。個々のコンポーネントは、インターフェースによって記述された機能を実装します。一つのコンポーネントが複数のインターフェースを実装する場合もあります。そして逆に、複数のコンポーネントが同じインターフェースを実装する場合もあります。
ファイルコンポーネントを例にしましょう。プロパティーとファイルを使って実行できる機能を記述するインターフェースを作る必要があります。ファイルには、名前、更新日付、サイズのためのプロパティーが必要です。また、機能には、ファイルの移動、コピー、削除が含まれます。
File インターフェースには、ファイルの特性だけを記述します。それを実装するものではありません。File インターフェースの実装は、コンポーネントに任されます。コンポーネントは、ファイルの名前、日付、サイズの検索ができるコードが必要です。また、ファイルをコピーしたり、名前の変更を行なうコードもいるでしょう。
コンポーネントがインターフェースを正しく実装している限り、コンポーネントがインターフェースをどのように実装しているかは問題ではありません。もちろん、プラットフォームごとに異なった実装が必要です。ファイルコンポーネントの Windows バージョンと Macintosh バージョンは明らかに異なります。しかし、それらはいずれも、同じインターフェースを実装しています。このため、インターフェースから知ることのできる機能を使ってアクセスすることにより、そのコンポーネントを使うことができるのです。
Mozilla では、インターフェースは 'nsI' で始まります。そのため、それは簡単にインターフェースであると分かります。例えば、nsIAddressBook はアドレスブックを使ってやり取りするためのインターフェースで、nsISound はファイルを再生するために、また、nsILocalFile はファイルのために使われます。
XPCOM コンポーネントは、多くの場合、ネイティブに実装されます。これは、一般的に言えば、JavaScript だけではできないことを行なうということです。しかし、それらを呼び出す方法はあります。それについては、すぐ後で見ることにしましょう。インターフェースによって記述されコンポーネントが実装している機能のどれでも呼び出すことができます。例えば、あるコンポーネントがある場合、それが nsISound を実装しているかチェックし、実装している場合は、それを使ってサウンドの再生ができます。
スクリプトから XPCOM を呼び出すプロセスは、XPConnect と呼ばれます。これは、スクリプトオブジェクトをネイティブオブジェクトに翻訳する層を指します。
XPCOM コンポーネントを呼び出すには、3 つのステップを踏みます。
最初の 2 つのステップが終われば、最後のステップは必要に応じて何度でも繰り返すことができます。ファイル名の変更がしたいとします。これには、nsILocalFile インターフェースを使うことができます。最初のステップで、ファイルコンポーネントを取得します。次に、ファイルコンポーネントの問い合わせを行ない、そこから nsILocalFile インターフェースを実装する部分を取得します。最後に、インターフェースが提供する関数を呼び出します。このインターフェースは、一つのファイルを表現するのに使われます。
インターフェースは常に 'nsI' で始まる名前であることは既に見ました。しかし、コンポーネントは、URI シンタックスを使って参照されます。Mozilla は、利用可能なコンポーネントすべてのリストをそれ自身のレジストリーに保存しています。ユーザーは、必要に応じて新しいコンポーネントのインストールができます。それはプラグインとよく似た働きをします。
Mozilla は、ファイルコンポーネント、つまり、nsILocalFile を実装したコンポーネントを提供しています。このコンポーネントは、'@mozilla.org/file/local;1' を使って参照できます。コンポーネント: URI の構成によって、コンポーネントを指定します。その他のコンポーネントも同様な方法で参照できます。
コンポーネントの URI を使って、コンポーネントが取得できます。JavaScript を使う場合、次のような JavaScript コードを使ってコンポーネントが取得できます。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); |
ファイルコンポーネントが検索され、aFile 変数に保存されます。上の例の Components は、コンポーネントに関連する幾つかの関数を提供する一般的なオブジェクトを参照します。ここで、classes プロパティーからコンポーネントを取得します。classes プロパティーは、利用可能なコンポーネントすべての配列です。別のコンポーネントを取得するには、使いたいコンポーネントの URI で角カッコ内の URI を置き換えるだけです。最後に、インスタンスが createInstance 関数によって作られます。
しかし、この時点では、ファイルコンポーネント自身の参照ができたにすぎません。その関数を呼び出すには、そのインターフェースの一つを取得しなければなりません。この場合は、nsILocalFile です。2 行目に次のコードを追加します。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); var aLocalFile = aFile.QueryInterface(Components.interfaces.nsILocalFile); |
QueryInterface は、コンポーネントすべてで提供される関数で、これを使えば、そのコンポーネントから指定のインターフェースを取得することができます。この関数はパラメータを一つ取ります。取得したいインターフェースです。Components オブジェクトの interfaces プロパティーには、利用可能なインターフェースすべてのリストがあります。ここでは、nsILocalFile インターフェースを使い、それをパラメータとして QueryInterface に渡します。その結果、aLocalFile には、コンポーネント中の nsILocalFile インターフェースを実装する部分への参照が設定されます。
上の JavaScript の 2 行は、どのコンポーネントのどのインターフェースを取得するのにも使えます。使いたいコンポーネントの名前でコンポーネント名を置き換え、インターフェースの名前を変えるだけです。もちろん、どんな変数名でも使えます。例えば、サウンドインターフェースを取得するには次のようにします。
var sound = Components.classes["@mozilla.org/sound;1"].createInstance(); var nsisound = sound.QueryInterface(Components.interfaces.nsISound); |
XPCOM インターフェースは、他のインターフェースを継承することができます。他のインターフェースを継承したインターフェースは、それ自身の関数とともに継承したインターフェースすべての関数をもちます。すべてのインターフェースは、別のインターフェースを継承します。トップレベルのインターフェースは nsISupports と呼ばれます。すべてのインターフェースはこのインターフェースの子孫です。そこには JavaScript に提供される関数が一つあります。これが既に見た QueryInterface です。インターフェース nsISupports はすべてのコンポーネントで実装されるので、関数 QueryInterface はどのコンポーネントでも利用できます。
コンポーネントの幾つかが同じインターフェースを実装する場合があります。多くの場合、それらはオリジナルのサブクラスになりますが、必ずそうだという訳ではありません。コンポーネントはどれも、nsILocalFile の機能を実装するかもしれません。また、一つのコンポーネントが幾つかのインターフェースを実装するかもしれません。関数を呼び出すために、インターフェースを取得するのに 2 つのステップを踏むのはこうした理由からです。
しかし、これらの行はしばしば一緒に使われるので、ショートカットが使えます。
var aLocalFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile); |
これは、2 行の場合と同じことをしますが、1 行で済みます。これを使うと、インスタンスを作り、それにインターフェースの問い合わせをするのを 2 つの別ステップで行なう必要がなくなります。
あるオブジェクトで QueryInterface を呼び出したけれどもそのオブジェクトが必要とするインターフェースをサポートしていなかった場合、null が返されます。次のように、null ではない値が返されたか常に確かめるべきです。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(); if (!aFile) return false; aLocalFile=aFile.QueryInterface(Components.interfaces.nsILocalFile); if (!aLocalFile) return false; |
nsILocalFile インターフェースをもつコンポーネントを参照するオブジェクトができたので、それを使って nsILocalFile の関数を呼び出すことができます。ファイル名の変更がしたいとします。そのためには、2 つのことをしなければなりません。まず、ファイルコンポーネントにどのファイルの名前を変更するのか指示する必要があります。次に、ファイル名の変更をします。nsILocalFile は一つのファイルを指定するのに使われます。汎用目的のファイル操作オブジェクトではないことに注意して下さい。
下のテーブルは nsILocalFile インターフェースのプロパティーとメソッドの幾つかを示しています。
| initWithPath | このメソッドは、nsILocalFile のパスとファイル名を初期化するのに使われる。最初のパラメータは、/usr/local/mozilla などのファイルパスにする。 |
| leafName | ディレクトリー部のないファイル名。 |
| fileSize | ファイルのサイズ。 |
| isDirectory() | nsILocalFile がディレクトリーを表している場合、真を返す。 |
| delete(recursive) | ファイルを削除する。recursive パラメータが真の場合、ディレクトリーとそこにあるファイルすべて、サブディレクトリーも削除される。 |
| copyTo(nsILocalFile dir,newname) | ファイルを別のディレクトリーにコピーする。オプションで、ファイル名を変更する。 |
| moveTo(nsILocalFile dir,newname) | ファイルを別のディレクトリーに移動する。あるいはファイル名を変更する。 |
ファイルを削除するには、ファイルを nsILocalFile に割り当てる必要があります。どのファイルを削除するのか指示するために、initWithPath メソッドを呼び出します。このプロパティーにファイルのパスを割り当てるだけです。次に、delete 関数を呼び出します。これはパラメータを一つ取ります。新しいファイル名です。(訳注: 原文は It takes one parameter which is the new filename. です。この関数の引数は、再帰的な削除を行なうかどうかを指示するもののはずですから、これは誤りです。) 下のコードはこれら 2 つのステップの例を示しています。
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance();
var aLocalFile = aFile.QueryInterface(Components.interfaces.nsILocalFile);
if (!aLocalFile) return false;
aLocalFile.initWithPath("/mozilla/testfile.txt");
aLocalFile.delete(false);
|
このコードは、/mozilla/testfile.txt ファイルを受け取り、それを削除します。このコードをイベントハンドラーに追加して、この例を試してみて下さい。ファイル名は、既にある削除したいファイルにします。
上の関数テーブルには、copyTo と moveTo という 2 つの関数があります。これら 2 つの関数は、それぞれ、ファイルをコピーしたり、削除したりするのに使えます。それらはコピーや移動するディレクトリーを表す文字列パラメータを取るのではなく、nsILocalFile を取ることに注意して下さい。これは、ファイルコンポーネントが 2 つ必要だということです。下の例は、ファイルをコピーする方法を示しています。
// コピーするファイルのためのコンポーネントを取得する
var aFile = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
if (!aFile) return false;
// コピー先のディレクトリーのためのコンポーネントを取得する
var aDir = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
if (!aDir) return false;
// 次に、ファイルコンポーネントに URL を割り当てる
aFile.initWithPath("/mozilla/testfile.txt");
aDir.initWithPath("/etc/");
// 最後に、ファイル名を変更せずに、ファイルをコピーする
aFile.copyTo(aDir,null);
|
XPCOM コンポーネントの中には、サービスと呼ばれる特殊なコンポーネントがあります。それらのインスタンスは作りません。一つだけ存在すべきだからです。サービスは、グローバルデータの取得や設定、他のオブジェクトに対する操作の実行を行なう一般的な機能を提供します。createInstance を呼び出す代わりに、サービスコンポーネントへの参照を取得するため getService を呼び出します。それ以外は、サービスは他のコンポーネントとさして変わりません。
Mozilla が提供するこうしたサービスの一つに、ブックマークサービスがあります。これによって、ブックマークをユーザーの現在のブックマークリストに追加することができます。例を以下に示します。
var bmarks = Components.classes["@mozilla.org/browser/bookmarks-service;1"].getService();
bmarks = bmarks.QueryInterface(Components.interfaces.nsIBookmarksService);
bmarks.AddBookmark("http://www.mozilla.org","Mozilla");
|
まず、コンポーネント "@mozilla.org/browser/bookmarks-service;1" が検索され、そのサービスが変数 bmarks に置かれます。nsIBookmarksService インターフェースを取得するため、QueryInterface を使います。このインターフェースが提供する関数 AddBookmark は、ブックマークを追加するのに使われます。この関数が取る 2 つのパラメータは、ブックマークの URL とそのタイトルです。
(進む) 次は、Mozilla が提供する使用可能なインターフェースの幾つかを見ることにしましょう。