ScriptableObject: Difference between revisions

bring up to date with patch
(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 IScriptable calls to lookup and fetch the attribute 'foo' on the interface.
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 IDispatch 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 IDispatch instance, all languages work with the same namespace - a property set in one language is reflected in another.
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#297
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:


  // A new helper xpcom service for cross-language work.
   [scriptable, uuid(...)]
   [scriptable, uuid(...)]
   interface nsIDOMScriptObjectHelper: nsISupports
   interface nsIDOMScriptableObjectHelper: nsISupports
  {
  {
      // Compile a function using the specified language.  Returns
    // Compile and call an "event handler" for the specified language, returning
      // an nsIScriptableObject that can be directly called (ie, by passing
    // the result. |aGlobal| must be the global window in which the
      // DISPID_DEFAULT). |glob| must be the global window in
    // returned function will execute when called. Generally you pass |window|.
      // which the returned function will execute when called.
    // This is basically a "scriptable" version of
      // Generally you pass |this|.
    // nsIScriptContext::CompileEventHandler/CallEventHandler - note that
      nsIScriptableObject compileFunction(in nsISupports glob,
    // a number of things conspire against this interface exposing compile and
                                    in PRUint32 langID,  
    // call distinctly:
                                    in DOMString aCode,  
    // * Ideally, compile could return an nsIScriptableObject which could be 'called'
                                    [array] in DOMString argNames);
    //  using generic nsIScriptableObject semantics - but nsIScriptContext
 
    //  semantics require the global object is passed to the call.
      // Execute arbitrary code.
    //  It might be possible to pollute nsIScriptableObject to work around this, but
      void execute(PRUint32 langID, in DOMString aCode);
    //  that's messy, and doesn't work in the general case for Python.
 
    // * BindCompiledEventHandler and GetBoundEventHandler must be used to
      // Takes a script-type ID (which is presumably the ID for
    //  ensure the correct 'binding' of the handler with the target.
      // a language other than the one being executed), and nsISupports  
    // * The 'handler' to the call step must be an nsISupports or similar, and
      // |glob| (which must be the global object for the currently
    //  converting this to the 'void *' for the language, and managing the
      // executing language) and returns an nsIScriptableObject, which is  
    //  lifetime of that void * are complicated hoops, especially given the above.
      // the global scope object for the requested language.
    nsIVariant callEventHandler(in nsISupports aGlobal,
      nsIScriptableObject getLanguageGlobal(in PRUint32 aRequestedLanguage, in nsISupports glob);
                                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 lang_id = node.scriptTypeID; // new attribute on nsIDOMNSEventTarget.
  var handler_name = "ondialog"+aDlgType;
  var s = components.classes["@mozilla.org/scriptobjecthelper"].getService();
  var handler_code = this.getAttribute(handler_name);
   
  if (handler_code != "") {
  var func = s.compileFunction(this, lang_id, handler, "event");
    // Execute the handler via the 'Script Object Helper' so all
  // |func| is an nsIScriptableObject -- call it.
    // script languages are supported.
  var returned = func(event);
    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 ====
Confirmed users
98

edits