XPConnect wrappers

This article is in need of a technical review.

This document is a high-level overview of XPConnect wrapper objects (for the more technical description see XPConnect security membranes). For practical advice on dealing with wrappers, see Safely accessing content DOM from chrome.

Developers in the know understand that wrappers play a large role in XPConnect, and that we have a lot of them. Less informed developers understand that wrappers exist and are somehow important, but they don't know when they should use what wrappers, or if they should be seeing a wrapper.

Note: Wrappers can appear in the console log; for example "[object XrayWrapper [object blah]]". Because these are wrapped, you won't be able to peek down inside them from the console.

Basic XPConnect objects

XPCWrappedNative

These objects are created when any natively implemented XPCOM object (that is, any object implemented in C++) needs to be reflected into JavaScript. This includes all DOM objects (including Window) and chrome elements that are reflected into JavaScript.

This wrapper is responsible for mapping calls from JavaScript into C++. This means that when you say window.focus(), you're calling into XPCWrappedNative code.

If you call 'toString()' and get "[xpconnect wrapped nsIFoo]" then the reference is to a XPCWrappedNative object with interface nsIFoo.

They are implicitly created by XPConnect and you should not have to worry about how that happens. There are several types of wrapped natives, but I won't cover those here.

XPCWrappedJS

These objects are the exact opposite of XPCWrappedNative. They exist to reflect an object from JavaScript into C++. This means that whenever you pass your JavaScript object into a C++ function, we create one of these wrappers. For example, if you've implemented some component with an interface nsIFoo and you pass your JavaScript object into a C++ function taking an nsIFoo, an XPCWrappedJS is created around your object. C++ calls are routed through XPCWrappedJS code into your JavaScript implementation.

These wrappers are created by XPConnect, so you should not have to worry about how to construct them or whether to construct them.

Double wrapping. There is one case where an XPCWrappedNative wraps another wrapper object. This case is where a JS object was passed in via some IDL-declared interface, creating an XPCWrappedJS, and is now being returned to JavaScript via some other interface. In order to preserve API compatibility, an XPCWrappedNative is created around the XPCWrappedJS.

Security wrappers exposed to chrome

The two basic wrappers above exist to support calling back and forth between C++ and JavaScript. The rest are security wrappers. These are JavaScript objects that wrap other JavaScript objects and enforce various security rules. (The code that enforces these rules is C++, but that is just an implementation detail.)

XPCNativeWrapper (XrayWrapper)

This was the first wrapper wrapper. XPCNativeWrapper will only wrap XPCWrappedNatives, and will throw an exception if it is asked to wrap any other type of JavaScript object (see the section about double wrapping for more information about this statement).

XPCNativeWrappers are designed to protect the user from the dynamicism that JavaScript provides. For example, given the code someAnchor.href = "http://www.mozilla.com" some evil code might run during the someAnchor call to prevent it from working correctly. If someAnchor is wrapped by an XPCNativeWrapper, this will not happen. The wrapper will always perform the original action, as defined by idl (or convention in the case of DOM0 behavior). "Expando" properties, not defined in idl, but created by the wrapped object are not visible through this wrapper.

Getting a wrapper:

  • XPCNativeWrappers are implicitly created by XPConnect for chrome JavaScript or JavaScript components, whenever they access content (less privileged) objects. However, an implicit wrapper may not be created in certain circumstances if the object isn't an XPCWrappedNative to begin with.  This generally only happens when taking arbitrary objects from content JavaScript or when inspecting unwrapped content objects. See XPCNativeWrapper for more information.
  • They can also be created manually by using new XPCNativeWrapper(wrappedNative).

Given an XPCNativeWrapper you can get closer to the underlying JS object (sometimes called "waiving the Xray behavior") using one of the following constructs:

  • XPCNativeWrapper.unwrap(obj)
  • wrapper.wrappedJSObject - if you're sure wrapper is an XPCNativeWrapper.

This returns a different kind of wrapper (called XPCSafeJSObjectWrapper (SJOW) below, or, sometimes, a "waiver"). Unlike XPCNativeWrapper, this other wrapper has almost no visible effect for the code dealing with it. Objects obtained from this unwrapped object are not automatically wrapped into an XPCNativeWrapper.

See XPCNativeWrapper and Safely accessing content DOM from chrome for more information on this topic.

Note: Since Firefox 4 this wrapper is also known as XrayWrapper, although to construct a wrapper you still use new XPCNativeWrapper(obj), and to unwrap, you still use XPCNativeWrapper.unwrap().

XPCSafeJSObjectWrapper

This wrapper was created to address some problems with XPCNativeWrapper. In particular, some extensions want to be able to safely access non-natively-implemented content defined objects (and to access the underlying JavaScript object under an XPCNativeWrapper without its strong behavior guarantees). SJOW (pronounced "show") act as a buffer between the chrome code.

Note: Because any type of object can be wrapped here, there is no predefined behavior. The only guarantee here is that untrusted code will run in an untrusted context. In particular, the underlying object might act entirely differently from what is expected.

Safe JSObject wrappers are implicitly created in some circumstances. All properties are visible through this wrapper.

Other security wrappers

XPCCrossOriginWrapper. A cross-origin wrapper, or XOW, is automatically created when an object from one domain is exposed to content from another domain.

If a XOW and the object it wraps happen to be from the same origin, then the XOW acts like an XPCSafeJSObjectWrapper. It becomes as transparent as possible.

If the underlying object is not from the same origin, then the XOW checks every property access against a short whitelist of properties and methods that are exposed across domains, as special exceptions to the Same Origin Policy. When access to a property or method is granted, the XOW acts like an XPCNativeWrapper: it calls the method or accessor of the underlying XPCOM object, as defined by IDL, seeing through whatever the other site may have done to the JS object.

XPCSystemOnlyWrapper. A system-only wrapper, or SOW, is automatically created when native anonymous content is exposed to JS. Access to the underlying object is always denied except when the calling code is chrome. This allows XBL code that is cloned into a Web page to operate on native anonymous content, which Web pages are not otherwise allowed to see.

XPCChromeObjectWrapper. A chrome object wrapper, or COW, is automatically created when a chrome object is exposed to content. If the chrome object has an __exposedProps__ property, then content may only access the whitelisted properties. This allows chrome to expose limited parts of objects to content without having to use IDL.

__exposedProps__

The __exposedProps__ property is an object with a property corresponding to each of the chrome object's properties which should be exposed.  The value of the properties within __exposedProps__ is either "w", "wr", or "r".  These will allow content to read and/or write the property (executing a function is considered reading).  Remember that functions defined in chrome context execute with chrome privileges.

Note: Starting in Gecko 15.0, exposing functions or objects without using __exposedProps__ is deprecated. Using __exposedProps__ will be mandatory starting with Gecko 17.0 (Firefox 17.0 / Thunderbird 17.0 / SeaMonkey 2.14).

To share a chrome object with the content, the following code can be used:

var sharedObject = { foo : "Hello!", __exposedProps__ : { foo : "r"} };
contentWindow.wrappedJSObject.sharedObject = sharedObject;

That sets sharedObject as a member of the content window, and allows the content read-only access to foo. Without __exposedProps__, window.sharedObject is defined, but window.sharedObject.foo isn't. Using __exposedProps__, a content script can then use its value:

alert(window.sharedObject.foo);

What wrappers should I use?

All right, you've skimmed the above and now you want to write some code! What wrappers should you use?

Web pages should ignore the wrappers entirely. They are sentries; by design, you can't do much of anything about them. They help protect your web page from being hijacked or snooped on by other web pages that happen to be loaded in the user's browser at the same time. They enforce the Same Origin Policy and other security rules that are built into Firefox and the Web platform generally. As long as your code follows those rules, the wrappers will be invisible. Of course, this does not mean that you can ignore security. The security restrictions are like a safety belt: they will try to protect you, but you can still get in an accident.

Chrome code (extensions, etc.) can also generally rely on the wrappers to enforce basic security rules and provide safeguards. If you are writing an extension or chrome code that never touches content code or objects, you don't have to worry about wrappers at all.

However, most extensions do touch content in some way. If you do touch content (via its window, for example) you will always automatically get an XPCNativeWrapper. If you don't, then you have found a bug (unless you opted out in your extension and you're intentionally writing insecure code). See the documentation on XPCNativeWrapper for more information about its use.

Chrome code that needs to access a content-defined object that is implemented in JS or otherwise not available through an IDL interface, can use a XPCSafeJSObjectWrapper. Starting with Firefox 3, you can either get one through XPCNativeWrapper.wrappedJSObject or by explictly writing new XPCSafeJSObjectWrapper(object). But note that we're looking into implicitly creating these wrappers in all relevant cases and removing the visible constructor. It's not possible to access content-defined JS objects securely in Firefox 2 or earlier.

Here is another way to understand the wrapper issues. Suppose you are writing code for an extension. The scope you are in is either "browser.xul" or a side bar window. You obtain a reference to a web page nsIDOMWindow, eg from onLocationChange(). Call that reference 'win'. When it comes to you from the Mozilla API, it will be an XPCNativeWrapper around the nsIDOMWindow. This means all of the nsIDOMWindow APIs will work as normal. However, the properties of this win object are not the Web page properties: you are outside of the nsIDOMWindow looking in. To get the inside view, operate on win.wrappedJSObject. This is a reference to the Web page 'window' global object as the window sees it. For example, if the Web page has set

 window.parent = "foo";

then win.parent will still refer to the parent of the nsIDOMWindow, but win.wrappedJSObject.parent will be "foo".

Document Tags and Contributors

Last updated by: Sheppy,