XPConnect Scriptable proposal
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 callednsIXPCScriptable
. 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 convertIn 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