Alec Flett wrote:
> 
> I'm attaching some files which I'm calling nsJSCaller.cpp, etc....
> 
> The problem I'm trying to solve is that a number of people are calling
> JS_PushArguments()/JS_PopArguments directly from their component in
> order do create argument lists for calls like
> window->Open()/window->OpenDialog() and so forth... this is mainly bad
> because:
> - we've had a history of people misusing JS_PopArgs (i.e. not calling it
> when they should) which confuses the heck out of the JS GC
> - this way you don't have to link against libmozjs.so just to open a
> window.
> 
> so what I've created is a generic interface and object, accessable only
> from C++, for creating an argument list, calling a function that
> requires it, and then destroying the argument list..
> 
> I'm going to put this in xpfe/appshell (unless someone else has a better
> idea.. maybe in libxpconnect?)
> 
> Comments? reviews/super reviews? Anyone got a better name?


The use of NS_IMETHOD is funky since on win32 this implies
__stdcall. But that can't be since you use varargs. Does this
compile on Win32? At least give a warning? Maybe "virtual
nsresult" would be better? This is really an evil method as far
as xpcom interfaces are concerned. But, I suppose it needs to
look like an interface to allow for factory access to it without
adding linkage dependencies.

For "&window_opener_args->child" did you mean "&wargs->child" ?

I'm not clear on why the prototype for JSCallerCallback does not
include the argc, yet the place you seem to be implementing one
does include argc. You want that in the prototype too, no?

I'm not too concerned about where it goes. Is appshell a general
enough place? Putting it in xpconnect is ok with some cleanup and
changes to conform to the local traditions.

John.


> 
>                                     Alec
> 
>   -----------------------------------------------------------------
> 
> #include "nsISupports.h"
> #include "nsIScriptContext.h"
> 
> // stdarg must be before jsapi so we get JS_PushArgumentsVA
> #include "stdarg.h"
> #include "jsapi.h"
> 
> /*
> 
>  * How to use this interface:
>  * Say you want to call nsIDOMWindowInternal::OpenDialog() and pass
>  * one object, an nsIFoo* as an argument.
> 
>  // create the closure object - will get passed to your function
>  struct window_opener_args {
>    nsIDOMWindowInternal *parent;
>    nsIDOMWindowInternal *child;
>  };
> 
>  // when this is called, argv has been set up for us
>  // note that we know we were called with 4 arguments.. this could also
>  // be passed in via the closure
>  static nsresult
>  openWindow(JSContext *cx, void *aClosure, jsval* argv, PRUint32 argc)
>  {
>    window_opener_args *wargs = (window_opener_args*)aClosure;
>    return wargs->parent->OpenDialog(cx, argv, 4,
>                                     &window_opener_args->child);
>  }
> 
>  ... later, in your code ...
> 
>  // get the foo object so we can pass it in
>  nsCOMPtr<nsIFoo> foo;
>  someObject->GetFoo(getter_AddRefs(foo));
> 
>  // the JSCaller object is a service because it contains no state
>  // (i.e. it can be shared amongst all consumers)
>  nsCOMPtr<nsIJSCaller> caller = do_GetService(NS_JSCALLER_PROGID, &rv);
> 
>  // now make the call. Note that:
>  //  - our closure contains both an in parameter, |parent|
>  //    and an out parameter, |child|
>  //  - passing in our callback as a pointer to a function
>  //  - first 3 arguments to openDialog() are well known.
>  //  - using %ip to pass a XPCOM object (see JS_PushArguments
>  //    for other formatting rules)
>  //  - manually casting the nsCOMPtr to the raw ptr, so it gets
>  //    sent as a vararg correctly
>  //  - |rv| is the result of your callback, so you can bubble up failure
>  window_opener_args wargs = { someWindow, nsnull };
>  rv = caller->CallFunction(cx, openWindow, (void *)wargs, "sss%ip",
>                            "_blank", "chrome,modal",
>                            "chrome://package/content/",
>                            (nsIFoo*)foo);
> 
>  // now, inside of the the window, window.arguments[0] is |foo|
>  // also at this point, wargs->child is the new nsIDOMWindow
> 
>  */
> 
> typedef nsresult (*JSCallerCallback)(JSContext *cx, void *aClosure,
>                                      jsval *argv);
> 
> #define NS_IJSCALLER_IID_STR "b05fac34-1dd1-11b2-ac07-cca38251f2f7"
> 
> #define NS_IJSCALLER_IID \
>   {0xb05fac34, 0x1dd1, 0x11b2, \
>     { 0xac, 0x07, 0xcc, 0xa3, 0x82, 0x51, 0xf2, 0xf7 }}
> 
> class NS_NO_VTABLE nsIJSCaller : public nsISupports
> {
> public:
>     NS_DEFINE_STATIC_IID_ACCESSOR(NS_IJSCALLER_IID);
> 
>     NS_IMETHOD CallFunction(nsIScriptContext *aContext,
>                             JSCallerCallback *aCallback,
>                             void *aClosure,
>                             const char *formatString, ...) = 0;
> };
> 
> #define NS_DECL_NSIJSCALLER \
>     NS_IMETHOD CallFunction(nsIScriptContext *aContext, JSCallerCallback *aCallback, 
>void *aClosure, const char *formatString, ...);
> 
>   -----------------------------------------------------------------
> 
> #include "nsIJSCaller.h"
> 
> #define NS_JSCALLER_CID \
>   { /* 5ea4f5b2-044c-40d1-b9ea-0dcc14c5c367 */ \
>     0x5ea4f5b2, \
>     0x044c, \
>     0x40d1, \
>     { 0xb9, 0xea, 0x0d, 0xcc, 0x14, 0xc5, 0xc3, 0x67 }}
> 
> #define NS_JSCALLER_CONTRACTID \
>     "@mozilla.org/javascript-caller-glue;1"
> 
> class nsJSCaller : public nsIJSCaller {
> public:
>     nsJSCaller();
>     virtual ~nsJSCaller();
> 
>     NS_DECL_ISUPPORTS
> 
>     NS_DECL_NSIJSCALLER
> private:
> 
> };
> 
>   -----------------------------------------------------------------
> 
> #include "nsJSCaller.h"
> 
> nsJSCaller::nsJSCaller()
> {
>     NS_INIT_ISUPPORTS();
> }
> 
> nsJSCaller::~nsJSCaller()
> {
> 
> }
> 
> NS_IMPL_ISUPPORTS1(nsJSCaller, nsIJSCaller);
> 
> NS_IMETHODIMP
> nsJSCaller::CallFunction(nsIScriptContext *aContext,
>                          JSCallerCallback *aCallback,
>                          void *aClosure,
>                          const char *aFormatString, ...)
> {
> 
>     nsresult rv;
> 
>     void *mark;
>     jsval *argv;
> 
>     JSContext *cx = (JSContext *)aContext->GetNativeContext();
> 
>     // set up arguments
>     va_list ap;
>     va_start(ap, aFormatString);
>     argv = JS_PushArgumentsVA(cx, &mark, aFormatString, ap);
>     va_end(ap);
> 
>     if (!argv)
>         return NS_ERROR_FAILURE;
> 
>     // now make the call
>     rv = (*aCallback)(cx, aClosure, argv);
> 
>     // always pop the arguments, even in case of failure of rv!
>     JS_PopArguments(cx, mark);
> 
>     return rv;
> }

Reply via email to