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?

                                    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