XPCNativeWrapper
позволяет так обернуть объект, чтобы доступ к нему был безопасен для привилегированного кода. Эта обёртка может быть использована во всех версиях, хотя её поведение слегка изменилось начиная с Firefox 1.5 (Gecko 1.8). Информацию о поведении XPCNativeWrapper
в Firefox версий младше 1.5 можно получить из статьи о XPCNativeWrapper
в MozillaZine KnowledgeBase. Эта же статья посвящена XPCNativeWrapper
в Firefox версий 1.5 и выше.
Что делает XPCNativeWrapper
An XPCNativeWrapper
limits access to the properties and methods of the object it wraps. The only properties and methods accessible through an XPCNativeWrapper
are those that are defined in IDL or defined by DOM Level 0 (though some DOM Level 0 properties and methods do not work on an XPCNativeWrapper
). In particular, properties added to an object via JavaScript are not exposed by an XPCNativeWrapper
for this object, and nor are getters and setters defined with __defineGetter__
and __defineSetter__
. The intent is to allow safe access to the IDL-defined methods of the object.
Please make sure to read the known bugs section, especially when writing code targeted at a range of 1.5.0.x Firefox releases.
Типы XPCNativeWrapper
There are three different types of XPCNativeWrapper
in Firefox 1.5. All three types wrap a possibly-unsafe object and provide safe access to its properties and methods.
The differences in behavior between the three types of XPCNativeWrapper
are determined by two characteristics an XPCNativeWrapper
wrapper can have. An XPCNativeWrapper
can be explicit (or the opposite, implicit) and can be deep (or the opposite, shallow). The type of wrapper created is determined by the way it was created as follows:
Created by | Explicit/Implicit | Deep/Shallow |
---|---|---|
Protected script accessing an untrusted object | Implicit | Deep |
Constructor called with string arguments | Explicit | Shallow |
Constructor called with no string arguments | Explicit | Deep |
Explicit vs. Implicit
The difference in behavior between explicit and implicit XPCNativeWrapper
is that a property access on an implicit XPCNativeWrapper
from script that is not protected is NOT safe. The property access will be forwarded through to the wrappedJSObject
of the XPCNativeWrapper
.
This means that scripts that are not protected don't need to worry about bugs arising because other code hands them an implicit XPCNativeWrapper
. On the other hand, such scripts do need to watch out for unsafe object access.
Property access on an explicit XPCNativeWrapper
is safe no matter whether the caller is protected.
Deep vs. Shallow
The difference in behavior between deep and shallow XPCNativeWrapper
is that when a property is accessed or a function is called on a deep wrapper the return value will be wrapped in a XPCNativeWrapper
of its own. The new XPCNativeWrapper
will also be deep and it will be explicit if and only if the XPCNativeWrapper
whose property is accessed was explicit. By contrast, when a property is accessed or a function is called on a shallow wrapper, the return value may be an unsafe object.
For example, say we are given three instances of XPCNativeWrapper
for the same window object. Let us call them deepExplicitWindow
, deepImplicitWindow
and shallowWindow
. Then we have:
var doc1 = deepExplicitWindow.document;
// doc1 is now a deep explicit XPCNativeWrapper
for
// the document object. Accessing doc1.open(), say, is safe.
var doc2 = deepImplicitWindow.document;
// If the caller has xpcnativewrappers=yes set, doc2 is now a deep
// implicit XPCNativeWrapper
for the document object.
// Otherwise doc2 is now the unsafe document object, since the
// property access was simply passed along to the unsafe window object.
var doc3 = shallowWindow.document; // doc3 is now the unsafe document object.
Создание объектов XPCNativeWrapper
Существует три различных способа создания объекта XPCNativeWrapper
; по одному способу на каждый из трёх типов.
Protected script accessing an untrusted object
Any time a protected script accesses an untrusted object it will get back an implicit deep XPCNativeWrapper
. Accessing properties of this XPCNativeWrapper
is safe from protected scripts.
A wrapper created in this way will stick around as long as the object being wrapped does and accessing an object twice in a row will give the same XPCNativeWrapper
.
What is a protected script?
In Firefox versions 1.5 through 1.5.0.5, a script is protected or not protected based solely on its URI. A script is protected only if its URI starts with a known protected prefix; scripts not loaded by URI (e.g. JavaScript-implemented components) are not protected. The protected prefixes in Firefox 1.5 are determined by the Chrome Registry.
By default, all content packages are protected. This means that all URIs that start "chrome://<package name>/content/
" (for any package) are protected. Individual packages can override this using a flag in their chrome manifest file.
Starting with Firefox 1.5.0.6, JavaScript-implemented components are protected scripts. So a script is protected if it's either loaded from a URI which starts with a protected prefix or is a JavaScript-implemented component.
What is an untrusted object?
All objects are either trusted or untrusted. An object is trusted if any of the following hold:
- Its parent (
__parent__
property in JavaScript) is a trusted object. - It is the root scope object for a JavaScript component.
- It is the window object for a trusted window.
Since all DOM objects in a window have the window on their __parent__
chain, they will be trusted if and only if their window is trusted.
What is a trusted window?
Whether a window is trusted depends on its container. A window is trusted if any of the following holds:
- It is a top-level window (e.g.
<xul:window>
,<xul:dialog>
, or some URI passed to the-chrome
command-line flag). - Its parent is trusted, and one of the following three options holds:
- It is not loaded in a
<xul:iframe>
or<xul:browser>
. - The
<xul:iframe>
or<xul:browser>
loading it doesn't have a "type" attribute. - The value of the "type" attribute of the
<xul:iframe>
or<xul:browser>
loading it is not "content" and does not start with "content-".
- It is not loaded in a
Note that whether a window is trusted does not depend on the URI loaded in the window. So for example, the following would create trusted windows when used inside a document whose window is already trusted:
<xul:browser>
<xul:browser type="chrome">
<xul:browser type="rabid_dog">
<xul:iframe type="foofy">
<html:iframe>
<html:iframe type="content">
The following would not create trusted windows:
<xul:browser type="content">
<xul:iframe type="content-primary">
Further note that any child window of an untrusted window is automatically untrusted.
What happens when a script accesses an object?
The table below describes what happens when a script accesses an object, and how the wrapper is involved.
Script | Object | Effects |
---|---|---|
Protected | Trusted | No wrapper is created and therefore the script gets full access to the object. |
Protected | Untrusted | An implicit deep XPCNativeWrapper is created. |
Unprotected | Trusted | No wrapper is created, just as in the protected/trusted case. |
Unprotected | Untrusted | No wrapper is created, just as in the protected/trusted case. |
XPCNativeWrapper
constructor call with string arguments
For example:
var contentWinWrapper = new XPCNativeWrapper(content, "document");
This creates an explicit shallow XPCNativeWrapper
. This syntax has been kept for compatibility with versions prior to Firefox 1.5. While all properties of the contentWinWrapper
object can now be safely accessed, the return values of these properties are NOT safe to access (just like in versions prior to Firefox 1.5), since the XPCNativeWrapper
is shallow. So to compare the content document title to the current content selection, one must do:
var winWrapper = new XPCNativeWrapper(content, "document", "getSelection()"); var docWrapper = new XPCNativeWrapper(winWrapper.document, "title"); return docWrapper.title == winWrapper.getSelection();
just like in versions before Firefox 1.5. Note that the "getSelection()"
argument is not strictly needed here; if the code is not intended for use with Firefox versions before 1.5 it can be removed. A single string argument after the object being wrapped is all that is required for Firefox 1.5 to create this type of XPCNativeWrapper
.
XPCNativeWrapper
constructor call with no string arguments
For example:
var contentWinWrapper = new XPCNativeWrapper(content);
This creates an explicit deep XPCNativeWrapper
. Accessing properties of this XPCNativeWrapper
is safe, and the return values will also be wrapped in explicit deep XPCNativeWrapper
objects.
Setting "expando" properties on XPCNativeWrapper
It is possible to set "expando" properties (properties with names that don't correspond to IDL-defined properties) on XPCNativeWrapper
objects. If this is done, then chrome will be able to see these expando properties, but content will not be able to. There is no safe way to set an expando property from chrome and have it be readable from content.
XPCNativeWrapper
lifetime
Explicit XPCNativeWrapper
objects exist while they are referenced. Creating a new explicit XPCNativeWrapper
for the same possibly-unsafe object will create a new wrapper object; something to watch out for when setting "expando" properties
Implicit XPCNativeWrapper
objects have the same lifetime as the object they're wrapping.
Accessing unsafe properties
If unsafe access to a property is required for some reason, this can be accomplished via the wrappedJSObject
property of the wrapper. For example, if docWrapper
is a wrapper for doc
, then
docWrapper.wrappedJSObject.prop
is the same as
doc.prop
Известные ошибки
Известны две ошибки реализации XPCNativeWrapper
в версиях 1.5.0.x:
- Firefox с версии 1.5 по версию 1.5.0.4 содержит bug 337095, в результате чего в некоторых случаях для защищённых скриптов не создаются обёртки. А именно, если из защищённого скрипта происходит обращение к свойству или вызов функции, которые возвращают недоверенный (untrusted) объект, обёртка будет создана. Однако, если функция в защищённом скрипте вызывается из C++, и в качестве аргумента этой функции передаётся недоверенный объект, обёртка не будет создана. Поэтому функции, которые могут быть вызваны подобным образом, должны сами производить обёртывание. Эта ошибка исправлена в Firefox версии 1.5.0.5 и выше.
- Firefox с версии 1.5 по версию 1.5.0.5 содержит bug 345991, в результате чего компоненты написанные на JavaScript не могут быть защищёнными скриптами. Эта ошибка исправлена в Firefox версии 1.5.0.6 и выше.
Limitations of XPCNativeWrapper
There are some commonly used properties and coding styles that cannot be used with XPCNativeWrapper
. Specifically:
- Assigning to or reading an
on*
property on anXPCNativeWrapper
of a DOM node or Window object will throw an exception. (Use addEventListener instead, and use "event.preventDefault();" in your handler if you used "return false;" before.) - Access to frames by window name (e.g.
window.frameName
) does not work on anXPCNativeWrapper
document.all
does not work on anXPCNativeWrapper
for a document.- Access to named items by name does not work on an
XPCNativeWrapper
for an HTML document. For example, if you have a<form name="foo">
anddocWrapper
is a wrapper for the HTML documentdoc
thendoc.foo
is anHTMLFormElement
whiledocWrapper.foo
isundefined
. Code that wishes to do this could usedocWrapper.forms.namedItem("foo")
instead. - Access to nodes by id doesn't work on an
XPCNativeWrapper
for an HTML document.getElementById
should be used instead. - Access to inputs by name doesn't work on an
XPCNativeWrapper
for an HTML form. Code that wishes to do this should useform.elements.namedItem("inputname")
. - Access to elements by name doesn't work on an
XPCNativeWrapper
for anHTMLCollection
. Code that wishes to do this should use thenamedItem()
method. Note thatnamedItem
only returns the first input element with the name, even if there are multiple elements (e.g. radio buttons) with the same name in the form. - Calling methods implemented by NPAPI plugins through the
XPCNativeWrapper
for the corresponding node does not work. - Getting or setting properties implemented by NPAPI plugins though the
XPCNativeWrapper
for the corresponding node does not work. - Calling methods implemented via XBL bindings attached to a node through an
XPCNativeWrapper
for that node does not work. - Getting or setting properties implemented via XBL bindings attached to a node through an
XPCNativeWrapper
for that node does not work. - Enumerating the properties of an
XPCNativeWrapper
via "for (var p in wrapper)
" does not enumerate the IDL-defined properties. - Object.prototype is not on the prototype chain of an
XPCNativeWrapper
. As a result, variousObject.prototype
properties are undefined on anXPCNativeWrapper
(to be precise, these are__proto__
,__parent__
,__count__
,toSource
,toLocaleString
,valueOf
,watch
,unwatch
,hasOwnProperty
,isPrototypeOf
,propertyIsEnumerable
,__defineGetter__
,__defineSetter__
,__lookupGetter__
, and__lookupSetter__
). - There is no support for the
importXPCNative
method the oldXPCNativeWrapper
implementation used to have. - Accessing standard classes (such as
Function
) through an XPCNativeWrapper will not work. To create functions and objects with a particular window's parent, use that window'seval
function.
Avoid Common Pitfalls in Greasemonkey has an elaborate explanation for some of these limitations (in context of Greasemonkey scripts).