Confirmed users
98
edits
(New page for nsIScriptableObject, with the technical details left in their own page.) |
(bring up to date with patch) |
||
Line 11: | Line 11: | ||
Not all implementations of the interface will support all operations, but the interface itself must be able to describe the above semantics. | Not all implementations of the interface will support all operations, but the interface itself must be able to describe the above semantics. | ||
Script languages may provide special support for this interface. For example, given an nsIScriptableObject object, script languages should be able to dynamically translate "x = object.foo" into the necessary | Script languages may provide special support for this interface. For example, given an nsIScriptableObject object, script languages should be able to dynamically translate "x = object.foo" into the necessary nsIScriptableObject calls to lookup and fetch the attribute 'foo' on the interface. | ||
== nsIScriptableObject design == | == nsIScriptableObject design == | ||
We will shamelessly borrow the MS COM IDispatchEx interface as a model. IDispatchEx is designed for this purpose and provides a number of enhancements to the older IDispatch interfaces. | We will shamelessly borrow the MS COM IDispatchEx interface as a model. IDispatchEx is designed for this purpose and provides a number of enhancements to the older IDispatch interfaces. For convenience, this document often refers to simply IDispatch, but this should be read as meaning IDispatchEx. | ||
It is worth discussing the role of IDispatchEx with MS's JScript and IE. JScript has native support for working with IDispatchEx as a namespace - ie, property references, assignments and method calls etc make IDispatchEx calls. GetNameSpaceParent is used to manage scoping. IE provides the | It is worth discussing the role of IDispatchEx with MS's JScript and IE. JScript has native support for working with IDispatchEx as a namespace - ie, property references, assignments and method calls etc make IDispatchEx calls. GetNameSpaceParent is used to manage scoping. IE provides the IDispatchEx implementation. As IE initializes script engines (ie, script languages), it provides an IDispatchEx implementation for each "named object" - such as 'window'. As all script languages share this common IDispatchEx instance, all languages work with the same namespace - a property set in one language is reflected in another. | ||
A couple of excellent resources on this topic are at http://blogs.msdn.com/ericlippert/archive/2004/10/07/239289.aspx and http://blogs.msdn.com/ericlippert/archive/2004/09/20/231852.aspx | A couple of excellent resources on this topic are at http://blogs.msdn.com/ericlippert/archive/2004/10/07/239289.aspx and http://blogs.msdn.com/ericlippert/archive/2004/09/20/231852.aspx | ||
Line 29: | Line 29: | ||
==== existing dialog widget ==== | ==== existing dialog widget ==== | ||
Consider the dialog widget, which allows you to have an "ondialogaccept" event handler. This widget is implemented in JS. | Consider the dialog widget, which allows you to have an "ondialogaccept" event handler. This widget is implemented in JS. | ||
See dialog.xul, http://lxr.mozilla.org/seamonkey/source/xpfe/global/resources/content/bindings/dialog.xml# | See dialog.xul, http://lxr.mozilla.org/seamonkey/source/xpfe/global/resources/content/bindings/dialog.xml#304 | ||
In summary, a dialog widget's "ondialogaccept" handler is called using the following paraphrased code: | In summary, a dialog widget's "ondialogaccept" handler is called using the following paraphrased code: | ||
// fetch the 'ondialogaccept' or 'ondialogcancel' attribute | |||
var handler = this.getAttribute("ondialog"+aDlgType); | var handler = this.getAttribute("ondialog"+aDlgType); | ||
// compile code returning a function object | |||
var fn = new Function("event", handler); | var fn = new Function("event", handler); | ||
// call the function. | |||
var returned = fn(event); | var returned = fn(event); | ||
Line 41: | Line 44: | ||
This would be achieved by: | This would be achieved by: | ||
[scriptable, uuid(...)] | [scriptable, uuid(...)] | ||
interface | interface nsIDOMScriptableObjectHelper: nsISupports | ||
{ | |||
// Compile and call an "event handler" for the specified language, returning | |||
// the result. |aGlobal| must be the global window in which the | |||
// returned function will execute when called. Generally you pass |window|. | |||
// This is basically a "scriptable" version of | |||
// nsIScriptContext::CompileEventHandler/CallEventHandler - note that | |||
// a number of things conspire against this interface exposing compile and | |||
// call distinctly: | |||
// * Ideally, compile could return an nsIScriptableObject which could be 'called' | |||
// using generic nsIScriptableObject semantics - but nsIScriptContext | |||
// semantics require the global object is passed to the call. | |||
// It might be possible to pollute nsIScriptableObject to work around this, but | |||
// that's messy, and doesn't work in the general case for Python. | |||
// * BindCompiledEventHandler and GetBoundEventHandler must be used to | |||
// ensure the correct 'binding' of the handler with the target. | |||
// * The 'handler' to the call step must be an nsISupports or similar, and | |||
// converting this to the 'void *' for the language, and managing the | |||
// lifetime of that void * are complicated hoops, especially given the above. | |||
nsIVariant callEventHandler(in nsISupports aGlobal, | |||
in PRUint32 aScriptTypeID, | |||
in DOMString aFuncName, | |||
in PRUint32 argCount, | |||
[array,size_is(argCount)] in string argNames, | |||
[array,size_is(argCount)] in nsIVariant argValues, | |||
in DOMString aBody, | |||
in AUTF8String aURL, | |||
in PRUint32 aLineNumber); | |||
// Execute arbitrary code. | |||
void execute(in nsISupports aGlobal, | |||
in PRUint32 aScriptTypeID, | |||
in DOMString aCode); | |||
// Evaluate an arbitrary expression. | |||
nsIScriptableObject evaluate(in nsISupports aGlobal, | |||
in PRUint32 aScriptTypeID, | |||
in DOMString aCode); | |||
// Takes a script-type ID (which is presumably the ID for | |||
// a language other than the one being executed), and nsISupports | |||
// |aGlobal| (which must be the global object for the currently | |||
// executing language) and returns an nsIScriptableObject, which is | |||
// the global scope object for the requested language. | |||
nsIScriptableObject getLanguageGlobal(in nsISupports aGlobal, | |||
in PRUint32 aScriptTypeID); | |||
}; | }; | ||
And the JS code implementing dialog.xml would change to: | And the JS code implementing dialog.xml would change to: | ||
var handler_name = "ondialog"+aDlgType; | |||
var handler_code = this.getAttribute(handler_name); | |||
if (handler_code != "") { | |||
// Execute the handler via the 'Script Object Helper' so all | |||
// script languages are supported. | |||
var helper = Components.classes[ | |||
'@mozilla.org/dom/scriptable-object-helper;1'].getService(); | |||
// Determine the default 'script type ID' specified for the target. | |||
var stid = event.target.scriptTypeID; | |||
// Apparently impossible to determine URL and lineno? | |||
// Make the call. | |||
var returned = helper.callEventHandler( | |||
window, stid, | |||
handler_name, | |||
1, // args count | |||
['event'], // arg names | |||
[event], // args | |||
handler_code, | |||
"<dialog event handler>", // URL | |||
0); // linenumber | |||
==== Contrived dialog widget ==== | ==== Contrived dialog widget ==== |