wrappedJSObject 编辑

wrappedJSObject is a property sometimes available on XPConnect wrappers. When available, it lets you access the JavaScript object hidden by the wrapper.

There are two kinds of XPConnect wrappers that support the wrappedJSObject property:

  • XPCNativeWrappers which are used to protect the chrome code working with content objects. See XPCNativeWrapper for detailed documentation.
  • Regular XPConnect wrappers which you can encounter, for example, when using XPCOM components implemented in JS.

This article focuses on the latter kind of wrappers, which hide any properties or methods on the component that are not part of the supported interfaces as declared in xpidl.

The rest of this article demonstrates what XPConnect wrappers do and how wrappedJSObject can be used to bypass them.

Example component

To see how the wrappedJSObject property works, we need an example XPCOM component implemented in JS. See How to Build an XPCOM Component in Javascript for details on creating one.

For simplicity we omit the component registration code. Suppose we register the component below with the @myself.com/my-component;1 contract.

// constructor
function HelloWorld() {
};

HelloWorld.prototype = {
  hello: function() {
    return "Hello World!";
  },

  QueryInterface: function(aIID)
  {
    if (!aIID.equals(Components.interfaces.nsISupports) &&
        !aIID.equals(Components.interfaces.nsIHelloWorld))
      throw Components.results.NS_ERROR_NO_INTERFACE;
    return this;
  }
};

XPConnect wrapping

Now let's get a reference to our component. In this example we use getService, but as long as we get the reference from XPCOM, our component gets wrapped by XPConnect in the same way:

var comp = Components.classes["@myself.com/my-component;1"].getService();

If we try to call the hello() method we defined in our component implementation, we get:

> comp.hello();
TypeError on line 1: comp.hello is not a function

This happens because, as we mentioned earlier, comp is not the HelloWorld JS object itself, but an XPConnect wrapper around it:

> dump(comp);
[xpconnect wrapped nsISupports]

The idea of these wrappers is to make the JavaScript-implemented XPCOM components look just like any other XPCOM component to the user. This also makes the public interface of the component clearer and provides protection for the component's internal data.

Calling QueryInterface on the wrapper works, because it is defined in the nsISupports interface, and the wrapper knows the underlying object implements nsISupports:

> comp.QueryInterface(Components.interfaces.nsIHelloWorld);
[xpconnect wrapped (nsISupports, nsIHelloWorld)]

As you can see, the QueryInterface call also made the wrapper know about the other interface our component supports. Assuming the nsIHelloWorld interface defines the hello method, we can now call it:

> comp.hello()
Hello World!

While this behavior is nice for production code as it forces you to clearly define the interfaces that should be used to access the component, it's inconvenient to write the interfaces (and recompile each time you change them) when working on a prototype of the component.

Meet wrappedJSObject

XPConnect lets you bypass its wrappers and access the underlying JS object directly using the wrapper.wrappedJSObject property if the wrapped object allows this.

More specifically, as XPConnect source comments say, you can get comp.wrappedJSObject if three conditions are met:

  • comp really is an XPConnect wrapper around a JS object. Wrappers around non-JS objects don't have this property.
  • The underlying object has a wrappedJSObject property that returns a JS object.
  • nsIXPCSecurityManager allows access (see the source code comments for details; this is usually not an issue for Mozilla extensions and applications)

This means that in order to access the JS object implementing our component directly, we must modify the component. For example:

function HelloWorld() {
  this.wrappedJSObject = this;
};

Now we can get the component directly:

var comp = Components.classes["@myself.com/my-component;1"]
                     .getService().wrappedJSObject;

This is a real JS object:

> comp
[object Object]

So we can access any property on it:

> comp.hello();
Hello World!

This functionality can be used for quick prototyping, as well as to painlessly pass arbitrary JS values to the component (which can be used for sharing complex JS data in particular).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

词条统计

浏览:58 次

字数:6192

最后编辑:8年前

编辑次数:0 次

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文