Visit Mozilla.org

XPCNativeWrapper

出典: MDC


XPCNativeWrapper は、特権コードから安全にアクセスするために、オブジェクトをラップする方法です。 これは、全てのバージョンの Firefox で利用できますが、Firefox 1.5 (Gecko 1.8) からいくつかの挙動が変更されています。 このページでは、Firefox の 1.5 以降の XPCNativeWrapper を前提に記述しています。 Firefox のバージョンが 1.5 までの場合についての情報は、MozillaZine ナリッジベースの XPCNativeWrapper のエントリーを参照してください。

目次

[編集] XPCNativeWrapper は何をするのか

XPCNativeWrapper は、ラップしたオブジェクトのメソッドとプロパティへのアクセスを制限します。 XPCNativeWrapper を通した場合、そのプロパティとメソッドへのアクセスは、IDL で定義されているか、DOM レベル 0 で定義されているものに限定されます。 (ただし、いくつかの DOM レベル 0 のプロパティとメソッドは、XPCNativeWrapper 上では動作しません) 特に、JavaScript によって、プロパティを追加した場合や、 __defineGetter____defineSetter__ によって、getter と setter を定義した場合には、 そのオブジェクトに対する XPCNativeWrapper で公開されるようにはなりません。 この制約によって、そのオブジェクトに対して IDL で定義されている安全なメソッドに確実にアクセスできるようにします。

コードの記述にかかる前に、既知の問題のセクションを読んで確認するようにしてください。 Firefox リリース 1.5.0.x を対象にコードを対象にする場合は、特にそうです。

[編集] XPCNativeWrapper の種類

Firefox 1.5 の XPCNativeWrapper には、3 種類の異なる型があります。 3 つの型は全て、安全でない可能性があるオブジェクトをラップし、それらのプロパティやメソッドへの安全なアクセス方法を提供します。

3 種類の XPCNativeWrapper の違いは、XPCNativeWrapper のラッパーが持つことができる 2 つの特性から決定されます。 XPCNativeWrapper は、明示的 (explicit) (または 暗黙的 (implicit)) にでき、かつ 徹底的 (deep) (または 表面的 (shallow)) に適用することができます。 この作成されたラッパーの種類は、以下に示す作成されたときの方法により決定されます。

作成元 明示的/暗黙的 徹底的/表面的
保護されたスクリプトが信頼できないオブジェクトにアクセスするとき 暗黙的 徹底的
文字列引数とともに、コンストラクタが呼び出されたとき 明示的 表面的
文字列以外の引数とともに、コンストラクタが呼び出されたとき 明示的 徹底的

[編集] 「明示的」か「暗黙的」か

XPCNativeWrapper の挙動が明示的 (explicit) か暗黙的 (implicit) かの違いは、 保護されていないスクリプトからの、暗黙的な XPCNativeWrapper 上のプロパティへのアクセスは、安全ではないという点です。 このときのプロパティへのアクセスは、XPCNativeWrapperwrappedJSObject を通して転送されます。

このことは、保護されていないスクリプトでは、 暗黙の XPCNativeWrapper によって想定外のコードが渡されることで、バグが発生することを心配する必要はないことを意味します。 また、そういったスクリプトからは、安全ではないオブジェクトへアクセスする場合は、用心する必要があることも意味します。

明示的な XPCNativeWrapper 上のプロパティにアクセスする場合は、呼び出し元が保護されているか否かにかかわらず安全です。

[編集] 「徹底的」か「表面的」か

XPCNativeWrapper の挙動が、徹底的 (deep) であるか表面的 (shallow) であるかの違いは、 徹底的なラッパーの場合は、プロパティのアクセスやメソッドの呼び出しで得られた返り値にも、XPCNativeWrapper が作成されてラップされるという点になります。 このとき、プロパティをアクセスされた XPCNativeWrapper が明示的であった場合には、 新規の XPCNativeWrapper も、徹底的でかつ、明示的になります。 これと比較して、表面的なラッパーの場合は、プロパティのアクセスやメソッドの呼び出しで得られた返り値は、安全でないオブジェクトである可能性があります。

例えば、同一のウィンドウオブジェクトに対して、3 つの XPCNativeWrapper のインスタンスが与えられ、 それらを deepExplicitWindowdeepImplicitWindowshallowWindow とした場合:

var doc1 = deepExplicitWindow.document;
// これにより doc1 は、文書オブジェクトに対する
// 徹底的かつ明示的な XPCNativeWrapper になります。
// doc1.open() にアクセスしたとしても安全です。
var doc2 = deepImplicitWindow.document;
// 呼び出し元に xpcnativewrappers=yes が設定されている場合、
// doc2 は、文書オブジェクトに対する徹底的で暗黙的な
// XPCNativeWrapper になります。そうでない場合は、
// プロパティへのアクセスは、単に安全でないウィンドウオブジェクトに
// 直接渡されるため、doc2 の文書オブジェクトも、安全ではありません。
// var doc3 = shallowWindow.document;
// この doc3 の文書オブジェクトは安全ではありません。

[編集] XPCNativeWrapper オブジェクトの生成

XPCNativeWrapper オブジェクトの生成には、3 つの異なる方法があり、 それぞれ、前述の 3 つの型に対応しています。

[編集] 保護されたスクリプトから安全でないオブジェクトにアクセスする

保護されたスクリプトが、信頼できないオブジェクトにアクセスするときは、常に暗黙で、徹底的XPCNativeWrapper が使用されます。 保護されたスクリプトから、この XPCNativeWrapper のプロパティにアクセスする場合は安全です。

この方法で作成されたラッパーは、オブジェクトをラップしている間は、ラップするオブジェクトに紐付くことになるため、 1 つの処理の中で、同じオブジェクトに 2 回アクセスした場合は、同じ XPCNativeWrapper が取得されることになります。

[編集] 保護されたスクリプトとは ?

Firefox の バージョン 1.5 から 1.5.0.5 までは、スクリプトが保護されるか否かは、その URI だけが基準なっています。 スクリプトの URI が、保護対象であることを示す既知の接頭辞で始まる場合のみ保護され、スクリプトが URI を利用して読み込まれない場合 (例: JavaScript で実装されたコンポーネント) は、保護されません。 Firefox 1.5 では、保護対象を示す接頭辞は、Chrome レジストリで決定されます。

デフォルトでは、全てのコンテントパッケージが保護されます。 これは、(任意のパッケージで)「chrome://<package name>/content/」で始まる全ての URI が保護されるということを意味しています。 個々のパッケージでは、chrome マニフェストファイルのフラグを使用して変更することが可能です。

Firefox 1.5.0.6 からは、JavaScript で実装されたコンポーネントは、保護されたスクリプトになります。 このため、スクリプトは保護対象の接頭辞で始まる URI から読み込まれる場合も、JavaScript で実装されたコンポーネントの場合も、両方とも保護されることになります。

[編集] 信頼できないオブジェクトとは ?

全てのオブジェクトは、信頼できるか、信頼できないかのどちらかになります。 オブジェクトが信頼できるのは、以下のいずれかに該当する場合です。

  1. 親 (JavaScript の __parent__ プロパティ) が、信頼できるオブジェクトの場合
  2. JavaScript コンポーネントの「root scope object」である場合
  3. 信頼できるウィンドウのウィンドウオブジェクトである場合

ウィンドウにある全ての DOM オブジェクトは、そのウィンドウオブジェクトを __parent__ チェインの中に含むことになります。 このため、ウィンドウが信頼できる場合は そのウィンドウの DOM オブジェクトも信頼できます。 かつ、ウィンドウ中の DOM オブジェクトが信頼できるのは、そのウィンドウオブジェクトが信頼できる場合のみになります。

[編集] 信頼できるウィンドウとは ?

ウィンドウが信頼できるかどうかは、そのコンテナに依存します。 ウィンドウが信頼できるのは、以下のいずれかに該当する場合です。

  1. トップレベルウィンドウ (例: <xul:window><xul:dialog>、コマンドラインから URI に -chrome フラグを指定) の場合
  2. 親が信頼でき、以下の 3 つのどれかに該当する場合
    1. <xul:iframe> または <xul:browser> を使用して読み込まれたものではない
    2. <xul:iframe> または <xul:browser> が読み込む際に type 属性を指定していない
    3. <xul:iframe> または <xul:browser> が読み込む際に指定された type 属性の値が「content」ではなく、かつ「content-」で始まるものではない。

ウィンドウが信頼できるかどうかは、ウィンドウに読み込まれた URI に依存するものではないことに注意してください。 したがって、以下に示す例が、既に信頼されているウィンドウの文書で使用された場合は、信頼できるウィンドウを作成します。

  • <xul:browser>
  • <xul:browser type="chrome">
  • <xul:browser type="rabid_dog">
  • <xul:iframe type="foofy">
  • <html:iframe>
  • <html:iframe type="content">

また、以下の例は信頼できるウィンドウを作成しません。

  • <xul:browser type="content">
  • <xul:iframe type="content-primary">

補足すれば、信頼できないウィンドウの任意の子ウィンドウは、自動的に信頼できないウィンドウになります。

[編集] スクリプトがオブジェクトにアクセスした際に起こることは ?

以下のテーブルは、スクリプトがオブジェクトにアクセスした際に起こることと、ラッパーが介在する方法について説明します。

スクリプト オブジェクト 効果
保護されている 信頼できる ラッパーは生成されないため、スクリプトからはオブジェクトに完全にアクセス可能
保護されている 信頼できない 暗黙徹底的XPCNativeWrapper が生成される
保護されない 信頼できる ラッパーは作成されず、保護 + 信頼の場合と同様になる
保護されない 信頼できない ラッパーは作成されず、保護 + 信頼の場合と同様になる

[編集] XPCNativeWrapper のコンストラクタを文字列引数で呼び出す

例:

var contentWinWrapper = new XPCNativeWrapper(content,
                                             "document");

この例は、明示的表面的XPCNativeWrapper を作成します。 この構文は、Firefox 1.5 より前のバージョンとの互換性を維持するために用意されています。 contentWinWrapper オブジェクトの全てのプロパティにわたり、安全にアクセスすることは可能ですが、これらのプロパティの戻り値は (Firefox 1.5 までと同様に) 安全にアクセスすることはできません。 このため、この XPCNativeWrapper表面的ということになります。 したがって、コンテントの文書タイトルと、現在の選択を比較するためには、以下のようにする必要があります。

var winWrapper = new XPCNativeWrapper(content, "document",
                                      "getSelection()");
var docWrapper = new XPCNativeWrapper(winWrapper.document,
                                      "title");
return docWrapper.title == winWrapper.getSelection();

これは、Firefox 1.5 までのバージョンの場合と同様になります。 この "getSelection()" 引数は、厳密にはここでは必要ないことに注意してください。 もし、このコードを Firefox 1.5 より前のバージョンに使用するつもりがないのであれば、削除してもかまいません。 Firefox 1.5 以降で、この型の XPCNativeWrapper を作成する場合は、 ラップするオブジェクトのあとに置く引数は、文字列引数が 1 つだけ必要になります。

[編集] XPCNativeWrapper のコンストラクタを文字列引数なしで呼び出す

例:

var contentWinWrapper = new XPCNativeWrapper(content);

この例は、明示的で、徹底的XPCNativeWrapper を作成します。 この XPCNativeWrapper のプロパティにアクセスした場合は安全で、かつ戻り値も明示的で、徹底的XPCNativeWrapper オブジェクトでラップされることになります。

[編集] XPCNativeWrapper の "expando" プロパティを on にする

XPCNativeWrapper オブジェクトに対して、"expando" プロパティ (IDL で定義されたプロパティと関係しない名前を持つプロパティ) を設定することが可能です。 設定した場合、chrome では、これらの expando プロパティを参照できますが、コンテントからは参照できません。 chrome から expando プロパティを設定し、それをコンテントから読み込めるようにする安全な方法は存在しません。

[編集] XPCNativeWrapper の寿命

明示的な XPCNativeWrapper オブジェクトは、参照されている間存在することになります。 同じ信頼できない可能性があるオブジェクトに対して、新規に明示的な XPCNativeWrapper を作成した場合、新規のラッパーオブジェクトが作成されます。 このことは、"expando" プロパティの設定を行う場合には、気をつけておく必要があります。

また、暗黙の XPCNativeWrapper オブジェクトは、ラップしているオブジェクトと同じ寿命を持っています。

[編集] 安全でないプロパティへのアクセス

もし、何らかの利用によってプロパティへの安全でないアクセスが必要になった場合、ラッパーの wrappedJSObject プロパティを経由することで行うことができます。 例えば、docWrapper が、doc のラッパーである場合、

docWrapper.wrappedJSObject.prop

は、以下と同じです。

doc.prop

[編集] 既知のバグ

バージョンが 1.5.0.x のものに含まれる XPCNativeWrapper には、2 つの既知のバグがあります。

  1. Firefox のバージョン 1.5 から 1.5.0.4 には bug 337095 があり、 これによっていくつかの状況で保護されたスクリプトに対してラッパーが作成されないことがあります。 特に保護されたスクリプトがプロパティのアクセスや関数の呼び出しを行って信頼できないオブジェクトが戻される場合、ラッパーが生成されますが、 保護されたスクリプトにある関数が C++ から呼び出され、信頼できないオブジェクトが引数として関数に渡された場合、ラッパーは作成されません。 関数がこの方法で呼び出されることが想定される場合には、自身でラッピングする必要があります。 このバグは Firefox 1.5.0.5 以降で解消されています。
  2. Firefox のバージョン 1.5 から 1.5.0.5 には bug 345991 があり、これによって JavaScript で記述されたコンポーネントが保護されたスクリプトになりません。 このバグは Firefox 1.5.0.6 以降で解消されています。

[編集] XPCNativeWrapper の制限事項

XPCNativeWrapper を使用する場合、いくつかの一般的に使用されるプロパティやコーディングスタイルが使用できない場合があります。 特に注意すべき点を列挙します。

  1. DOM ノードや Window オブジェクトに設定された XPCNativeWrapper の、on* プロパティを設定したり読み込もうとすると例外が送出されます。 (addEventListener を代わりに使用し、もし以前に "return false;" を使用していた場合には、作成したハンドラの中で "event.preventDefault();" を行うようにしてください)
  2. ウィンドウ名によるフレームアクセス (例: window.frameName) は、XPCNativeWrapper 上では動作しません。
  3. document に設定された XPCNativeWrapper では document.all は動作しません。
  4. HTML の document に設定された XPCNativeWrapper では、名前付きのアイテムを名前でアクセスすることができません。 例えば、doc という HTML 文書 があり、そこに <form name="foo"> があって、docWrapper がこの文書に対するラッパーである場合、doc.foo は、HTMLFormElement になりますが、docWrapper.fooundefined になってしまいます。 ラッパーを利用する場合は、代わりにコードとして docWrapper.forms.namedItem("foo") を使用すればうまくいきます。
  5. HTML の document に設定された XPCNativeWrapper では、id によるノードアクセスは動作しません。 代わりに getElementById を使う必要があります。
  6. HTML の form に設定された XPCNativeWrapper では、名前による入力項目へのアクセスは動作しません。 この場合は、コードとして form.elements.namedItem("inputname") を使用すればうまくいきます。
  7. HTMLCollection に設定された XPCNativeWrapper では、名前による要素のアクセスが動作しません。 この場合は、コードとして namedItem() メソッドを使用する必要があります。 なお namedItem は、(例えばラジオボタンのように) フォームの中に同じ名前の要素が複数あったとしても、その名前に該当する最初の入力要素だけを戻すことに注意してください。
  8. 関連するノードに設定された XPCNativeWrapper を通しての NPAPI プラグインによって実装されたメソッドの呼び出しは動作しません。
  9. 関連するノードに設定された XPCNativeWrapper を通しての NPAPI プラグインによって実装されたプロパティの取得や設定は動作しません。
  10. ノードに設定された XPCNativeWrapper を通して、そのノードに結びつけられた XBL バインディングによって実装されたメソッドの呼び出しは動作しません。
  11. ノードに設定された XPCNativeWrapper を通して、そのノードに結びつけられた XBL バインディングによって実装されたプロパティの設定や取得は動作しません。
  12. "for (var p in wrapper)" によって、XPCNativeWrapper のプロパティの列挙を行った場合、IDL で定義されたプロパティは列挙されません。
  13. Object.prototype は、XPCNativeWrapper の prototype チェインに含まれません。 そのため、XPCNativeWrapper では、いくつかの Object.prototype プロパティが undefined になります。 (正確には、__proto__, __parent__, __count__, toSource, toLocaleString, valueOf, watch, unwatch, hasOwnProperty, isPrototypeOf, propertyIsEnumerable, __defineGetter__, __defineSetter__, __lookupGetter__, and __lookupSetter__ が該当します)
  14. 古い XPCNativeWrapper の実装に存在した importXPCNative メソッドは現在ではサポートされていません。
  15. XPCNativeWrapper を通して、(Function のような) 標準クラスへのアクセスは動作しません。 特定のウィンドウの親を伴って、関数やオブジェクトを生成したい場合は、そのウィンドウの eval 関数を使用してください。

Avoid Common Pitfalls in Greasemonkey には、これらの制限事項のいくつかについての詳細な説明があります (Greasemonkey スクリプトから利用する観点です)。