You are currently viewing a snapshot of www.mozilla.org taken on April 21, 2008. Most of this content is highly out of date (some pages haven't been updated since the project began in 1998) and exists for historical purposes only. If there are any pages on this archive site that you think should be added back to www.mozilla.org, please file a bug.



XPConnect Scriptable proposal

John Bandhauer <jband@netscape.com>

Introduction

XPConnect supports reflection into JavaScript of the methods, attributes, and constants as statically declared in each given xpcom interface's xpidl declaration. This is very useful, but it does not yet support the style of dynamically aggregated properties that make JavaScript so powerful and which are required to fully support such modules as the DOM and SmartUpdate. This proposal suggests a system to remedy this problem.

Definitions

I speak here about three sorts of properties (where 'property' means both methods and attributes):
  • static - Static properties are those defined in xpidl. Static methods are immutable. The set of static attributes can not change, though the values of static attributes might.
  • dynamic - Dynamic properties are the set of reflected properties of the XPCOM object that might be added or removed at runtime. They are not declared in xpidl. The XPCOM object should exert control over what properties can be dynamically added, removed, or changed.
  • arbitrary - Arbitrary properties are the set of properties that are neither static nor dynamic (as defined above). These are properties that can be aggregated to the wrapper object (which reflects the XPCOM object into JavaScript) but do not otherwise have any meaning to the reflected xpcom object. For instance...
    document.form.element = new Button("push me");
    ...is an example of manipulating a dynamic property because it has semantic meaning to the wrapped xpcom object. While...
    document.foo = "Hi there";
    ... is an example of manipulating an arbitrary property because it just adds a plain named object to the wrapped xpcom object. It is really up to the xpcom object being wrapped to distinguish between dynamic and arbitrary properties.

Proposal

I propose to declare an interface called nsIXPCScriptable. Any xpcom object can implement that interface and expose it via QueryInterface if it so chooses. When XPConnect wraps any xpcom object it will query for nsIXPCScriptable and act accordingly if that interface is supported by the object.

nsIXPCScriptable will include methods that mirror the jsObjectOps methods used by XPConnect to reflect the xpcom object into JavaScript. XPConnect will support forwarding these method calls from the JS engine to the nsIXPCScriptable interface of the wrapped xpcom object when appropriate. This will allow the xpcom object to customize how it is reflected into JavaScript.

These methods will include:

 
    lookupProperty
    defineProperty
    getPropertyById
    setPropertyById
    getAttributes
    setAttributes
    deleteProperty
    defaultValue
    newEnumerate
    checkAccess
    construct
    call
    convert
In addition to the normal params of these JS methods we'll also pass a reference to the wrapper itself. These methods will return a value indicating one of:
  • 'I handled it without error'
  • 'I didn't handle it, pass the request along'
  • 'Return an error'

Some addtional methods will be added to this interface. For instance, the xpcom object implementing nsIXPCScriptable should be able to indicate the call order between the three sets of handlers: The 'static' XPCOM reflection, the 'dynamic' reflection using nsIXPCScriptable, and the 'arbitrary' property handling implemented by treating the wrapper like a plain JSObject. A method on nsIXPCScriptable will be called when the wrapper is constructed to find out which order of these three handlers should be used for all calls. The order controls which sets properties can shadow and hide which other sets of properties.

An example...

Suppose that the call order is set to: "static, dynamic, arbitrary" and a setPropertyById call comes in from the JS engine. XPConnect will check to see if the property in question is one of the static properties. If it is then it will be handled. If not, then the call is forwarded to nsIXPCScriptable::setPropertyById. This gives the xpcom object an opportunity to do whatever it wants to do. Likely it will look to see if this is a property to which it ascribes any special semantic meaning. If it is not such a property, then the object likely returns 'not handled' and then XPConnect calls the 'plain' JSObject methods to set the property on the wrapper. Else, if the xpcom object does care about this property than it might examine the value being set into the property. That value might be a wrapped xpcom object itself. It can use the XPConnect api methods to unwrap the object and do as it chooses. It might return 'I handled it' or it might update its own internal representation and still want that value to be aggregated to the wrapper object by the plain JSObject code. There is a lot of flexibility here.

The nsIXPCScriptable will also allow the reflected object to do things that would not necessarily be supported (or at least not as flexibly) in the static reflection using XPConnect. Such as... using the object as a constructor or synthesizing a function object so that the object could be used with the 'new' operator or with the function call '()' operator. It could also support custom type conversions and 'toString' handling.

Issues

My prefered scheme for doing all this is to implement the WrappedNative JS class such that it supports both the low level jsObjectOps and also forwards to the 'standard' handlers via the js_ObjectOps 'vtbl'; i.e. js_ObjectOps.xxx();. I've played with this and it works, but there may be cases where it will blow up. There is a discussion on news://news.mozilla.org/netscape.public.mozilla.jseng regarding whether or not this approach is viable.

One alternative is to not use the jsObjectOps at all and instead do everything through the classic jsclass interface. This would likely be a real performance limitation on the static reflection of xpcom objects (the main thing that XPConnect lives to do).

Another alternative I considered is to use the jsObjectOps and the nsIXPCScriptable interface as suggested above but make it the nsIXPCScriptable interface's problem to handle arbitrary properties. We could write some shared code to which it could forward calls that would handle managing properties and rooting them, etc. This totally fails to leverage the powerful code for just this function in the JS engine and has gc drawbacks. But it would work. I like the main proposal a lot better if it will work.

Conclusions

I'm not sure how clear this all is. The goal is to allow XPCOM objects to customize their reflection into JavaScript where necessary while still doing most of the grunt work in XPConnect. Much of the commonly used code (like converting a jsval param to the xpcom object it may wrap) can be made into XPConnect library funcions. Most objects reflected into JS will not need support for dynamic and arbitrary properties and will be able to ignore all of this. Those object that do need such support need only implement one interface to get the functionality.

I would very much like to hear feedback, ideas, and requirements. Please respond to: news://news.mozilla.org/netscape.public.mozilla.xpcom


document history...
  • 01 Feb 1999 - jband - initial creation of document