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
上のプロパティへのアクセスは、安全ではないという点です。
このときのプロパティへのアクセスは、XPCNativeWrapper
の wrappedJSObject
を通して転送されます。
このことは、保護されていないスクリプトでは、
暗黙の XPCNativeWrapper
によって想定外のコードが渡されることで、バグが発生することを心配する必要はないことを意味します。
また、そういったスクリプトからは、安全ではないオブジェクトへアクセスする場合は、用心する必要があることも意味します。
明示的な XPCNativeWrapper
上のプロパティにアクセスする場合は、呼び出し元が保護されているか否かにかかわらず安全です。
「徹底的」か「表面的」か
XPCNativeWrapper
の挙動が、徹底的 (deep)であるか表面的 (shallow)であるかの違いは、
徹底的なラッパーの場合は、プロパティのアクセスやメソッドの呼び出しで得られた返り値にも、XPCNativeWrapper
が作成されてラップされるという点になります。
このとき、プロパティをアクセスされた XPCNativeWrapper
が明示的であった場合には、
新規の XPCNativeWrapper
も、徹底的でかつ、明示的になります。
これと比較して、表面的なラッパーの場合は、プロパティのアクセスやメソッドの呼び出しで得られた返り値は、安全でないオブジェクトである可能性があります。
例えば、同一のウィンドウオブジェクトに対して、3 つの XPCNativeWrapper
のインスタンスが与えられ、
それらを deepExplicitWindow
、deepImplicitWindow
、shallowWindow
とした場合:
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 レジストリで決定されます。
デフォルトでは、全てのコンテントパッケージが保護されます。 これは、(任意のパッケージで)「<tt>chrome://<package name>/content/</tt>」で始まる全ての URI が保護されるということを意味しています。 個々のパッケージでは、chrome マニフェストファイルのフラグを使用して変更することが可能です。
Firefox 1.5.0.6 からは、JavaScript で実装されたコンポーネントは、保護されたスクリプトになります。 このため、スクリプトは保護対象の接頭辞で始まる URI から読み込まれる場合も、JavaScript で実装されたコンポーネントの場合も、両方とも保護されることになります。
信頼できないオブジェクトとは ?
全てのオブジェクトは、信頼できるか、信頼できないかのどちらかになります。 オブジェクトが信頼できるのは、以下のいずれかに該当する場合です。
- 親 (JavaScript の
__parent__
プロパティ) が、信頼できるオブジェクトの場合 - JavaScript コンポーネントの「root scope object」である場合
- 信頼できるウィンドウのウィンドウオブジェクトである場合
ウィンドウにある全ての DOM オブジェクトは、そのウィンドウオブジェクトを __parent__
チェインの中に含むことになります。
このため、ウィンドウが信頼できる場合は そのウィンドウの DOM オブジェクトも信頼できます。
かつ、ウィンドウ中の DOM オブジェクトが信頼できるのは、そのウィンドウオブジェクトが信頼できる場合のみになります。
信頼できるウィンドウとは ?
ウィンドウが信頼できるかどうかは、そのコンテナに依存します。 ウィンドウが信頼できるのは、以下のいずれかに該当する場合です。
- トップレベルウィンドウ (例:
<xul:window>
、<xul:dialog>
、コマンドラインから URI に <tt>-chrome</tt> フラグを指定) の場合 - 親が信頼でき、以下の 3 つのどれかに該当する場合
-
<xul:iframe>
または<xul:browser>
を使用して読み込まれたものではない -
<xul:iframe>
または<xul:browser>
が読み込む際に type 属性を指定していない -
<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 つの既知のバグがあります。
- Firefox のバージョン 1.5 から 1.5.0.4 には バグ 337095 があり、 これによっていくつかの状況で保護されたスクリプトに対してラッパーが作成されないことがあります。 特に保護されたスクリプトがプロパティのアクセスや関数の呼び出しを行って信頼できないオブジェクトが戻される場合、ラッパーが生成されますが、 保護されたスクリプトにある関数が C++ から呼び出され、信頼できないオブジェクトが引数として関数に渡された場合、ラッパーは作成されません。 関数がこの方法で呼び出されることが想定される場合には、自身でラッピングする必要があります。 このバグは Firefox 1.5.0.5 以降で解消されています。
- Firefox のバージョン 1.5 から 1.5.0.5 には バグ 345991 があり、これによって JavaScript で記述されたコンポーネントが保護されたスクリプトになりません。 このバグは Firefox 1.5.0.6 以降で解消されています。
XPCNativeWrapper
の制限事項
XPCNativeWrapper
を使用する場合、いくつかの一般的に使用されるプロパティやコーディングスタイルが使用できない場合があります。 特に注意すべき点を列挙します。
- DOM ノードや Window オブジェクトに設定された
XPCNativeWrapper
の、on*
プロパティを設定したり読み込もうとすると例外が送出されます。 (addEventListener を代わりに使用し、もし以前に "return false;" を使用していた場合には、作成したハンドラの中で "event.preventDefault();" を行うようにしてください) - ウィンドウ名によるフレームアクセス (例:
window.frameName
) は、XPCNativeWrapper
上では動作しません。 - document に設定された
XPCNativeWrapper
ではdocument.all
は動作しません。 - HTML の document に設定された
XPCNativeWrapper
では、名前付きのアイテムを名前でアクセスすることができません。 例えば、doc
という HTML 文書 があり、そこに<form name="foo">
があって、docWrapper
がこの文書に対するラッパーである場合、doc.foo
は、HTMLFormElement
になりますが、docWrapper.foo
はundefined
になってしまいます。 ラッパーを利用する場合は、代わりにコードとしてdocWrapper.forms.namedItem("foo")
を使用すればうまくいきます。 - HTML の document に設定された
XPCNativeWrapper
では、id によるノードアクセスは動作しません。 代わりにgetElementById
を使う必要があります。 - HTML の form に設定された
XPCNativeWrapper
では、名前による入力項目へのアクセスは動作しません。 この場合は、コードとしてform.elements.namedItem("inputname")
を使用すればうまくいきます。 -
HTMLCollection
に設定されたXPCNativeWrapper
では、名前による要素のアクセスが動作しません。 この場合は、コードとしてnamedItem()
メソッドを使用する必要があります。 なおnamedItem
は、(例えばラジオボタンのように) フォームの中に同じ名前の要素が複数あったとしても、その名前に該当する最初の入力要素だけを戻すことに注意してください。 - 関連するノードに設定された
XPCNativeWrapper
を通しての NPAPI プラグインによって実装されたメソッドの呼び出しは動作しません。 - 関連するノードに設定された
XPCNativeWrapper
を通しての NPAPI プラグインによって実装されたプロパティの取得や設定は動作しません。 - ノードに設定された
XPCNativeWrapper
を通して、そのノードに結びつけられた XBL バインディングによって実装されたメソッドの呼び出しは動作しません。 - ノードに設定された
XPCNativeWrapper
を通して、そのノードに結びつけられた XBL バインディングによって実装されたプロパティの設定や取得は動作しません。 - "
for (var p in wrapper)
" によって、XPCNativeWrapper
のプロパティの列挙を行った場合、IDL で定義されたプロパティは列挙されません。 - Object.prototype は、
XPCNativeWrapper
の prototype チェインに含まれません。 そのため、XPCNativeWrapper
では、いくつかのObject.prototype
プロパティが undefined になります。 (正確には、__proto__
,__parent__
,__count__
,toSource
,toLocaleString
,valueOf
,watch
,unwatch
,hasOwnProperty
,isPrototypeOf
,propertyIsEnumerable
,__defineGetter__
,__defineSetter__
,__lookupGetter__
, and__lookupSetter__
が該当します) - 古い
XPCNativeWrapper
の実装に存在したimportXPCNative
メソッドは現在ではサポートされていません。 - XPCNativeWrapper を通して、(
Function
のような) 標準クラスへのアクセスは動作しません。 特定のウィンドウの親を伴って、関数やオブジェクトを生成したい場合は、そのウィンドウのeval
関数を使用してください。
Avoid Common Pitfalls in Greasemonkey には、これらの制限事項のいくつかについての詳細な説明があります (Greasemonkey スクリプトから利用する観点です)。