Back to index

lightning-sunbird  0.9+nobinonly
nsJSNPRuntime.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsJSNPRuntime.h"
00039 #include "ns4xPlugin.h"
00040 #include "ns4xPluginInstance.h"
00041 #include "nsIPluginInstancePeer2.h"
00042 #include "nsPIPluginInstancePeer.h"
00043 #include "nsIScriptGlobalObject.h"
00044 #include "nsIScriptContext.h"
00045 #include "nsIDocument.h"
00046 #include "nsIJSRuntimeService.h"
00047 #include "nsIJSContextStack.h"
00048 #include "nsIXPConnect.h"
00049 #include "nsIDOMElement.h"
00050 #include "prmem.h"
00051 #include "nsIContent.h"
00052 
00053 // FIXME(bug 332648): Give me a real API please!
00054 #include "jscntxt.h"
00055 
00056 // Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
00057 // will be one wrapper per JSObject per plugin instance, i.e. if two
00058 // plugins access the JSObject x, two wrappers for x will be
00059 // created. This is needed to be able to properly drop the wrappers
00060 // when a plugin is torn down in case there's a leak in the plugin (we
00061 // don't want to leak the world just because a plugin leaks an
00062 // NPObject).
00063 static PLDHashTable sJSObjWrappers;
00064 
00065 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
00066 static PLDHashTable sNPObjWrappers;
00067 
00068 // Global wrapper count. This includes JSObject wrappers *and*
00069 // NPObject wrappers. When this count goes to zero, there are no more
00070 // wrappers and we can kill off hash tables etc.
00071 static PRInt32 sWrapperCount;
00072 
00073 // The JSRuntime. Used to unroot JSObjects when no JSContext is
00074 // reachable.
00075 static JSRuntime *sJSRuntime;
00076 
00077 // The JS context stack, we use this to push a plugin's JSContext onto
00078 // while executing JS on the context.
00079 static nsIJSContextStack *sContextStack;
00080 
00081 NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
00082   {
00083     NP_CLASS_STRUCT_VERSION,
00084     nsJSObjWrapper::NP_Allocate,
00085     nsJSObjWrapper::NP_Deallocate,
00086     nsJSObjWrapper::NP_Invalidate,
00087     nsJSObjWrapper::NP_HasMethod,
00088     nsJSObjWrapper::NP_Invoke,
00089     nsJSObjWrapper::NP_InvokeDefault,
00090     nsJSObjWrapper::NP_HasProperty,
00091     nsJSObjWrapper::NP_GetProperty,
00092     nsJSObjWrapper::NP_SetProperty,
00093     nsJSObjWrapper::NP_RemoveProperty
00094   };
00095 
00096 JS_STATIC_DLL_CALLBACK(JSBool)
00097 NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00098 
00099 JS_STATIC_DLL_CALLBACK(JSBool)
00100 NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00101 
00102 JS_STATIC_DLL_CALLBACK(JSBool)
00103 NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00104 
00105 JS_STATIC_DLL_CALLBACK(JSBool)
00106 NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
00107 
00108 JS_STATIC_DLL_CALLBACK(JSBool)
00109 NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
00110                         JSObject **objp);
00111 
00112 JS_STATIC_DLL_CALLBACK(void)
00113 NPObjWrapper_Finalize(JSContext *cx, JSObject *obj);
00114 
00115 JS_STATIC_DLL_CALLBACK(JSBool)
00116 NPObjWrapper_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00117                   jsval *rval);
00118 
00119 static bool
00120 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj,
00121                      NPObject *npobj, jsval id, jsval *vp);
00122 
00123 static JSClass sNPObjectJSWrapperClass =
00124   {
00125     "NPObject JS wrapper class", JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,
00126     NPObjWrapper_AddProperty, NPObjWrapper_DelProperty,
00127     NPObjWrapper_GetProperty, NPObjWrapper_SetProperty, JS_EnumerateStub,
00128     (JSResolveOp)NPObjWrapper_NewResolve, JS_ConvertStub,
00129     NPObjWrapper_Finalize, nsnull, nsnull, NPObjWrapper_Call, nsnull, nsnull,
00130     nsnull
00131   };
00132 
00133 typedef struct NPObjectMemberPrivate {
00134     JSObject *npobjWrapper;
00135     jsval fieldValue;
00136     jsval methodName;
00137     NPP   npp;
00138 } NPObjectMemberPrivate;
00139 
00140 JS_STATIC_DLL_CALLBACK(JSBool)
00141 NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp);
00142 
00143 JS_STATIC_DLL_CALLBACK(void)
00144 NPObjectMember_Finalize(JSContext *cx, JSObject *obj);
00145 
00146 JS_STATIC_DLL_CALLBACK(JSBool)
00147 NPObjectMember_Call(JSContext *cx, JSObject *obj, uintN argc,
00148                     jsval *argv, jsval *rval);
00149 
00150 JS_STATIC_DLL_CALLBACK(uint32)
00151 NPObjectMember_Mark(JSContext *cx, JSObject *obj, void *arg);
00152 
00153 static JSClass sNPObjectMemberClass =
00154   {
00155     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE,
00156     JS_PropertyStub, JS_PropertyStub,
00157     JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub,
00158     JS_ResolveStub, NPObjectMember_Convert,
00159     NPObjectMember_Finalize, nsnull, nsnull, NPObjectMember_Call,
00160     nsnull, nsnull, nsnull, NPObjectMember_Mark, nsnull
00161   };
00162 
00163 static void
00164 OnWrapperCreated()
00165 {
00166   if (sWrapperCount++ == 0) {
00167     static const char rtsvc_id[] = "@mozilla.org/js/xpc/RuntimeService;1";
00168     nsCOMPtr<nsIJSRuntimeService> rtsvc(do_GetService(rtsvc_id));
00169     if (!rtsvc)
00170       return;
00171 
00172     rtsvc->GetRuntime(&sJSRuntime);
00173     NS_ASSERTION(sJSRuntime != nsnull, "no JSRuntime?!");
00174 
00175     CallGetService("@mozilla.org/js/xpc/ContextStack;1", &sContextStack);
00176   }
00177 }
00178 
00179 static void
00180 OnWrapperDestroyed()
00181 {
00182   NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
00183 
00184   if (--sWrapperCount == 0) {
00185     if (sJSObjWrappers.ops) {
00186       NS_ASSERTION(sJSObjWrappers.entryCount == 0, "Uh, hash not empty?");
00187 
00188       // No more wrappers, and our hash was initalized. Finish the
00189       // hash to prevent leaking it.
00190       PL_DHashTableFinish(&sJSObjWrappers);
00191 
00192       sJSObjWrappers.ops = nsnull;
00193     }
00194 
00195     if (sNPObjWrappers.ops) {
00196       NS_ASSERTION(sNPObjWrappers.entryCount == 0, "Uh, hash not empty?");
00197 
00198       // No more wrappers, and our hash was initalized. Finish the
00199       // hash to prevent leaking it.
00200       PL_DHashTableFinish(&sNPObjWrappers);
00201 
00202       sNPObjWrappers.ops = nsnull;
00203     }
00204 
00205     // No more need for this.
00206     sJSRuntime = nsnull;
00207 
00208     NS_IF_RELEASE(sContextStack);
00209   }
00210 }
00211 
00212 struct AutoCXPusher
00213 {
00214   AutoCXPusher(JSContext *cx)
00215   {
00216     // Precondition explaining why we don't need to worry about errors
00217     // in OnWrapperCreated.
00218     NS_PRECONDITION(sWrapperCount > 0,
00219                     "must have live wrappers when using AutoCXPusher");
00220 
00221     // Call OnWrapperCreated and OnWrapperDestroyed to ensure that the
00222     // last OnWrapperDestroyed doesn't happen while we're on the stack
00223     // and null out sContextStack.
00224     OnWrapperCreated();
00225 
00226     sContextStack->Push(cx);
00227   }
00228 
00229   ~AutoCXPusher()
00230   {
00231     JSContext *cx = nsnull;
00232     sContextStack->Pop(&cx);
00233 
00234     JSContext *currentCx = nsnull;
00235     sContextStack->Peek(&currentCx);
00236 
00237     if (!currentCx) {
00238       // No JS is running, tell the context we're done executing
00239       // script.
00240 
00241       nsIScriptContext *scx = GetScriptContextFromJSContext(cx);
00242 
00243       if (scx) {
00244         scx->ScriptEvaluated(PR_TRUE);
00245       }
00246     }
00247 
00248     OnWrapperDestroyed();
00249   }
00250 };
00251 
00252 static JSContext *
00253 GetJSContext(NPP npp)
00254 {
00255   NS_ENSURE_TRUE(npp, nsnull);
00256 
00257   ns4xPluginInstance *inst = (ns4xPluginInstance *)npp->ndata;
00258   NS_ENSURE_TRUE(inst, nsnull);
00259 
00260   nsCOMPtr<nsPIPluginInstancePeer> pp(do_QueryInterface(inst->Peer()));
00261   NS_ENSURE_TRUE(pp, nsnull);
00262 
00263   nsCOMPtr<nsIPluginInstanceOwner> owner;
00264   pp->GetOwner(getter_AddRefs(owner));
00265   NS_ENSURE_TRUE(owner, nsnull);
00266 
00267   nsCOMPtr<nsIDocument> doc;
00268   owner->GetDocument(getter_AddRefs(doc));
00269   NS_ENSURE_TRUE(doc, nsnull);
00270 
00271   nsCOMPtr<nsISupports> documentContainer = doc->GetContainer();
00272   nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(documentContainer));
00273   NS_ENSURE_TRUE(sgo, nsnull);
00274 
00275   nsIScriptContext *scx = sgo->GetContext();
00276   NS_ENSURE_TRUE(scx, nsnull);
00277 
00278   return (JSContext *)scx->GetNativeContext();
00279 }
00280 
00281 
00282 static NPP
00283 LookupNPP(NPObject *npobj);
00284 
00285 
00286 static jsval
00287 NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
00288 {
00289   switch (variant->type) {
00290   case NPVariantType_Void :
00291     return JSVAL_VOID;
00292   case NPVariantType_Null :
00293     return JSVAL_NULL;
00294   case NPVariantType_Bool :
00295     return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant));
00296   case NPVariantType_Int32 :
00297     return INT_TO_JSVAL(NPVARIANT_TO_INT32(*variant));
00298   case NPVariantType_Double :
00299     {
00300       jsval val;
00301       if (::JS_NewNumberValue(cx, NPVARIANT_TO_DOUBLE(*variant), &val)) {
00302         return val;
00303       }
00304 
00305       break;
00306     }
00307   case NPVariantType_String :
00308     {
00309       const NPString *s = &NPVARIANT_TO_STRING(*variant);
00310       PRUint32 len;
00311       PRUnichar *p =
00312         UTF8ToNewUnicode(nsDependentCString(s->utf8characters, s->utf8length),
00313                          &len);
00314 
00315       JSString *str = ::JS_NewUCString(cx, (jschar *)p, len);
00316 
00317       if (str) {
00318         return STRING_TO_JSVAL(str);
00319       }
00320 
00321       break;
00322     }
00323   case NPVariantType_Object:
00324     {
00325       if (npp) {
00326         JSObject *obj =
00327           nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
00328 
00329         if (obj) {
00330           return OBJECT_TO_JSVAL(obj);
00331         }
00332       }
00333 
00334       NS_ERROR("Error wrapping NPObject!");
00335 
00336       break;
00337     }
00338   default:
00339     NS_ERROR("Unknown NPVariant type!");
00340   }
00341 
00342   NS_ERROR("Unable to convert NPVariant to jsval!");
00343 
00344   return JSVAL_VOID;
00345 }
00346 
00347 bool
00348 JSValToNPVariant(NPP npp, JSContext *cx, jsval val, NPVariant *variant)
00349 {
00350   NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
00351 
00352   if (JSVAL_IS_PRIMITIVE(val)) {
00353     if (val == JSVAL_VOID) {
00354       VOID_TO_NPVARIANT(*variant);
00355     } else if (JSVAL_IS_NULL(val)) {
00356       NULL_TO_NPVARIANT(*variant);
00357     } else if (JSVAL_IS_BOOLEAN(val)) {
00358       BOOLEAN_TO_NPVARIANT(JSVAL_TO_BOOLEAN(val), *variant);
00359     } else if (JSVAL_IS_INT(val)) {
00360       INT32_TO_NPVARIANT(JSVAL_TO_INT(val), *variant);
00361     } else if (JSVAL_IS_DOUBLE(val)) {
00362       DOUBLE_TO_NPVARIANT(*JSVAL_TO_DOUBLE(val), *variant);
00363     } else if (JSVAL_IS_STRING(val)) {
00364       JSString *jsstr = JSVAL_TO_STRING(val);
00365       nsDependentString str((PRUnichar *)::JS_GetStringChars(jsstr),
00366                             ::JS_GetStringLength(jsstr));
00367 
00368       PRUint32 len;
00369       char *p = ToNewUTF8String(str, &len);
00370 
00371       if (!p) {
00372         return false;
00373       }
00374 
00375       STRINGN_TO_NPVARIANT(p, len, *variant);
00376     } else {
00377       NS_ERROR("Unknown primitive type!");
00378 
00379       return false;
00380     }
00381 
00382     return true;
00383   }
00384 
00385   NPObject *npobj =
00386     nsJSObjWrapper::GetNewOrUsed(npp, cx, JSVAL_TO_OBJECT(val));
00387   if (!npobj) {
00388     return false;
00389   }
00390 
00391   // Pass over ownership of npobj to *variant
00392   OBJECT_TO_NPVARIANT(npobj, *variant);
00393 
00394   return true;
00395 }
00396 
00397 static void
00398 ThrowJSException(JSContext *cx, const char *message)
00399 {
00400   const char *ex = PeekException();
00401 
00402   if (ex) {
00403     nsAutoString ucex;
00404 
00405     if (message) {
00406       AppendASCIItoUTF16(message, ucex);
00407 
00408       AppendASCIItoUTF16(" [plugin exception: ", ucex);
00409     }
00410 
00411     AppendUTF8toUTF16(ex, ucex);
00412 
00413     if (message) {
00414       AppendASCIItoUTF16("].", ucex);
00415     }
00416 
00417     JSString *str = ::JS_NewUCStringCopyN(cx, (jschar *)ucex.get(),
00418                                           ucex.Length());
00419 
00420     if (str) {
00421       ::JS_SetPendingException(cx, STRING_TO_JSVAL(str));
00422     }
00423 
00424     PopException();
00425   } else {
00426     ::JS_ReportError(cx, message);
00427   }
00428 }
00429 
00430 static JSBool
00431 ReportExceptionIfPending(JSContext *cx)
00432 {
00433   const char *ex = PeekException();
00434 
00435   if (!ex) {
00436     return JS_TRUE;
00437   }
00438 
00439   ThrowJSException(cx, nsnull);
00440 
00441   return JS_FALSE;
00442 }
00443 
00444 
00445 nsJSObjWrapper::nsJSObjWrapper(NPP npp)
00446   : nsJSObjWrapperKey(nsnull, npp)
00447 {
00448   OnWrapperCreated();
00449 }
00450 
00451 nsJSObjWrapper::~nsJSObjWrapper()
00452 {
00453   // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers.
00454   NP_Invalidate(this);
00455 
00456   OnWrapperDestroyed();
00457 }
00458 
00459 // static
00460 NPObject *
00461 nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
00462 {
00463   NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
00464                "Huh, wrong class passed to NP_Allocate()!!!");
00465 
00466   return new nsJSObjWrapper(npp);
00467 }
00468 
00469 // static
00470 void
00471 nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
00472 {
00473   // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
00474   delete (nsJSObjWrapper *)npobj;
00475 }
00476 
00477 // static
00478 void
00479 nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
00480 {
00481   nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
00482 
00483   if (jsnpobj && jsnpobj->mJSObj) {
00484     // Unroot the object's JSObject
00485     ::JS_RemoveRootRT(sJSRuntime, &jsnpobj->mJSObj);
00486 
00487     if (sJSObjWrappers.ops) {
00488       // Remove the wrapper from the hash
00489 
00490       nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
00491       PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_REMOVE);
00492     }
00493 
00494     // Forget our reference to the JSObject.
00495     jsnpobj->mJSObj = nsnull;
00496   }
00497 }
00498 
00499 static JSBool
00500 GetProperty(JSContext *cx, JSObject *obj, NPIdentifier identifier, jsval *rval)
00501 {
00502   jsval id = (jsval)identifier;
00503 
00504   AutoCXPusher pusher(cx);
00505 
00506   if (JSVAL_IS_STRING(id)) {
00507     JSString *str = JSVAL_TO_STRING(id);
00508 
00509     return ::JS_GetUCProperty(cx, obj, ::JS_GetStringChars(str),
00510                               ::JS_GetStringLength(str), rval);
00511   }
00512 
00513   NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
00514 
00515   return ::JS_GetElement(cx, obj, JSVAL_TO_INT(id), rval);
00516 }
00517 
00518 // static
00519 bool
00520 nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier identifier)
00521 {
00522   NPP npp = NPPStack::Peek();
00523   JSContext *cx = GetJSContext(npp);
00524 
00525   if (!cx || !npobj) {
00526     return PR_FALSE;
00527   }
00528 
00529   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00530   jsval v;
00531   JSBool ok = GetProperty(cx, npjsobj->mJSObj, identifier, &v);
00532 
00533   return ok && !JSVAL_IS_PRIMITIVE(v) &&
00534     ::JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v));
00535 }
00536 
00537 static bool
00538 doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
00539          uint32_t argCount, NPVariant *result)
00540 {
00541   NPP npp = NPPStack::Peek();
00542   JSContext *cx = GetJSContext(npp);
00543 
00544   if (!cx || !npobj || !result) {
00545     // XXX: Throw null-ptr exception
00546 
00547     return PR_FALSE;
00548   }
00549 
00550   // Initialize *result
00551   VOID_TO_NPVARIANT(*result);
00552 
00553   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00554   jsval fv;
00555 
00556   AutoCXPusher pusher(cx);
00557 
00558   if ((jsval)method != JSVAL_VOID) {
00559     if (!GetProperty(cx, npjsobj->mJSObj, method, &fv) ||
00560         ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
00561       return PR_FALSE;
00562     }
00563   } else {
00564     fv = OBJECT_TO_JSVAL(npjsobj->mJSObj);
00565   }
00566 
00567   jsval jsargs_buf[8];
00568   jsval *jsargs = jsargs_buf;
00569 
00570   if (argCount > (sizeof(jsargs_buf) / sizeof(jsval))) {
00571     // Our stack buffer isn't large enough to hold all arguments,
00572     // malloc a buffer.
00573     jsargs = (jsval *)PR_Malloc(argCount * sizeof(jsval));
00574 
00575     if (!jsargs) {
00576       // XXX: throw an OOM exception!
00577 
00578       return PR_FALSE;
00579     }
00580   }
00581 
00582   JSTempValueRooter tvr;
00583   JS_PUSH_TEMP_ROOT(cx, 0, jsargs, &tvr);
00584 
00585   // Convert args
00586   for (PRUint32 i = 0; i < argCount; ++i) {
00587     jsargs[i] = NPVariantToJSVal(npp, cx, args + i);
00588     ++tvr.count;
00589   }
00590 
00591   jsval v;
00592   JSBool ok = ::JS_CallFunctionValue(cx, npjsobj->mJSObj, fv, argCount, jsargs,
00593                                      &v);
00594 
00595   JS_POP_TEMP_ROOT(cx, &tvr);
00596 
00597   if (jsargs != jsargs_buf)
00598     PR_Free(jsargs);
00599 
00600   if (ok)
00601     ok = JSValToNPVariant(npp, cx, v, result);
00602 
00603   // return ok == JS_TRUE to quiet down compiler warning, even if
00604   // return ok is what we really want.
00605   return ok == JS_TRUE;
00606 }
00607 
00608 // static
00609 bool
00610 nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
00611                           const NPVariant *args, uint32_t argCount,
00612                           NPVariant *result)
00613 {
00614   if ((jsval)method == JSVAL_VOID) {
00615     return PR_FALSE;
00616   }
00617 
00618   return doInvoke(npobj, method, args, argCount, result);
00619 }
00620 
00621 // static
00622 bool
00623 nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
00624                                  uint32_t argCount, NPVariant *result)
00625 {
00626   return doInvoke(npobj, (NPIdentifier)JSVAL_VOID, args, argCount, result);
00627 }
00628 
00629 // static
00630 bool
00631 nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier identifier)
00632 {
00633   NPP npp = NPPStack::Peek();
00634   JSContext *cx = GetJSContext(npp);
00635 
00636   if (!cx || !npobj) {
00637     // XXX: Throw null ptr exception
00638 
00639     return PR_FALSE;
00640   }
00641 
00642   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00643   jsval id = (jsval)identifier;
00644   JSBool found, ok = JS_FALSE;
00645 
00646   if (JSVAL_IS_STRING(id)) {
00647     JSString *str = JSVAL_TO_STRING(id);
00648 
00649     ok = ::JS_HasUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
00650                             ::JS_GetStringLength(str), &found);
00651   } else {
00652     NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
00653 
00654     ok = ::JS_HasElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &found);
00655   }
00656 
00657   return ok && found;
00658 }
00659 
00660 // static
00661 bool
00662 nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier identifier,
00663                                NPVariant *result)
00664 {
00665   NPP npp = NPPStack::Peek();
00666   JSContext *cx = GetJSContext(npp);
00667 
00668   if (!cx || !npobj)
00669     return PR_FALSE;
00670 
00671   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00672 
00673   AutoCXPusher pusher(cx);
00674 
00675   jsval v;
00676   return (GetProperty(cx, npjsobj->mJSObj, identifier, &v) &&
00677           JSValToNPVariant(npp, cx, v, result));
00678 }
00679 
00680 // static
00681 bool
00682 nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier identifier,
00683                                const NPVariant *value)
00684 {
00685   NPP npp = NPPStack::Peek();
00686   JSContext *cx = GetJSContext(npp);
00687 
00688   if (!cx || !npobj)
00689     return PR_FALSE;
00690 
00691   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00692   jsval id = (jsval)identifier;
00693   JSBool ok = JS_FALSE;
00694 
00695   AutoCXPusher pusher(cx);
00696 
00697   jsval v = NPVariantToJSVal(npp, cx, value);
00698   JSAutoTempValueRooter tvr(cx, v);
00699 
00700   if (JSVAL_IS_STRING(id)) {
00701     JSString *str = JSVAL_TO_STRING(id);
00702 
00703     ok = ::JS_SetUCProperty(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
00704                             ::JS_GetStringLength(str), &v);
00705   } else {
00706     NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
00707 
00708     ok = ::JS_SetElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id), &v);
00709   }
00710 
00711   // return ok == JS_TRUE to quiet down compiler warning, even if
00712   // return ok is what we really want.
00713   return ok == JS_TRUE;
00714 }
00715 
00716 // static
00717 bool
00718 nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier identifier)
00719 {
00720   NPP npp = NPPStack::Peek();
00721   JSContext *cx = GetJSContext(npp);
00722 
00723   if (!cx || !npobj)
00724     return PR_FALSE;
00725 
00726   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
00727   jsval id = (jsval)identifier;
00728   JSBool ok = JS_FALSE;
00729 
00730   AutoCXPusher pusher(cx);
00731 
00732   if (JSVAL_IS_STRING(id)) {
00733     JSString *str = JSVAL_TO_STRING(id);
00734 
00735     jsval unused;
00736     ok = ::JS_DeleteUCProperty2(cx, npjsobj->mJSObj, ::JS_GetStringChars(str),
00737                                 ::JS_GetStringLength(str), &unused);
00738   } else {
00739     NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
00740 
00741     ok = ::JS_DeleteElement(cx, npjsobj->mJSObj, JSVAL_TO_INT(id));
00742   }
00743 
00744   // return ok == JS_TRUE to quiet down compiler warning, even if
00745   // return ok is what we really want.
00746   return ok == JS_TRUE;
00747 }
00748 
00749 class JSObjWrapperHashEntry : public PLDHashEntryHdr
00750 {
00751 public:
00752   nsJSObjWrapper *mJSObjWrapper;
00753 };
00754 
00755 
00756 PR_STATIC_CALLBACK(PLDHashNumber)
00757 JSObjWrapperHash(PLDHashTable *table, const void *key)
00758 {
00759   const nsJSObjWrapperKey *e = NS_STATIC_CAST(const nsJSObjWrapperKey *, key);
00760 
00761   return (PLDHashNumber)((PRWord)e->mJSObj ^ (PRWord)e->mNpp) >> 2;
00762 }
00763 
00764 PR_STATIC_CALLBACK(const void *)
00765 JSObjWrapperHashGetKey(PLDHashTable *table, PLDHashEntryHdr *entry)
00766 {
00767   JSObjWrapperHashEntry *e =
00768     NS_STATIC_CAST(JSObjWrapperHashEntry *, entry);
00769 
00770   return NS_STATIC_CAST(nsJSObjWrapperKey *, e->mJSObjWrapper);
00771 }
00772 
00773 PR_STATIC_CALLBACK(PRBool)
00774 JSObjWrapperHashMatchEntry(PLDHashTable *table, const PLDHashEntryHdr *entry,
00775                            const void *key)
00776 {
00777   const nsJSObjWrapperKey *objWrapperKey =
00778     NS_STATIC_CAST(const nsJSObjWrapperKey *, key);
00779   const JSObjWrapperHashEntry *e =
00780     NS_STATIC_CAST(const JSObjWrapperHashEntry *, entry);
00781 
00782   return (e->mJSObjWrapper->mJSObj == objWrapperKey->mJSObj &&
00783           e->mJSObjWrapper->mNpp == objWrapperKey->mNpp);
00784 }
00785 
00786 
00787 // Look up or create an NPObject that wraps the JSObject obj.
00788 
00789 // static
00790 NPObject *
00791 nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JSObject *obj)
00792 {
00793   if (!npp) {
00794     NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
00795 
00796     return nsnull;
00797   }
00798 
00799   if (!cx) {
00800     cx = GetJSContext(npp);
00801 
00802     if (!cx) {
00803       NS_ERROR("Unable to find a JSContext in "
00804                "nsJSObjWrapper::GetNewOrUsed()!");
00805 
00806       return nsnull;
00807     }
00808   }
00809 
00810   JSClass *clazz = JS_GET_CLASS(cx, obj);
00811 
00812   if (clazz == &sNPObjectJSWrapperClass) {
00813     // obj is one of our own, its private data is the NPObject we're
00814     // looking for.
00815 
00816     NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
00817 
00818     return _retainobject(npobj);
00819   }
00820 
00821   if (!sJSObjWrappers.ops) {
00822     // No hash yet (or any more), initalize it.
00823 
00824     static PLDHashTableOps ops =
00825       {
00826         PL_DHashAllocTable,
00827         PL_DHashFreeTable,
00828         JSObjWrapperHashGetKey,
00829         JSObjWrapperHash,
00830         JSObjWrapperHashMatchEntry,
00831         PL_DHashMoveEntryStub,
00832         PL_DHashClearEntryStub,
00833         PL_DHashFinalizeStub
00834       };
00835 
00836     if (!PL_DHashTableInit(&sJSObjWrappers, &ops, nsnull,
00837                            sizeof(JSObjWrapperHashEntry), 16)) {
00838       NS_ERROR("Error initializing PLDHashTable!");
00839 
00840       return nsnull;
00841     }
00842   }
00843 
00844   nsJSObjWrapperKey key(obj, npp);
00845 
00846   JSObjWrapperHashEntry *entry =
00847     NS_STATIC_CAST(JSObjWrapperHashEntry *,
00848                    PL_DHashTableOperate(&sJSObjWrappers, &key, PL_DHASH_ADD));
00849   if (!entry) {
00850     // Out of memory.
00851     return nsnull;
00852   }
00853 
00854   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObjWrapper) {
00855     // Found a live nsJSObjWrapper, return it.
00856 
00857     return _retainobject(entry->mJSObjWrapper);
00858   }
00859 
00860   // No existing nsJSObjWrapper, create one.
00861 
00862   nsJSObjWrapper *wrapper =
00863     (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
00864 
00865   if (!wrapper) {
00866     // OOM? Remove the stale entry from the hash.
00867 
00868     PL_DHashTableRawRemove(&sJSObjWrappers, entry);
00869 
00870     return nsnull;
00871   }
00872 
00873   wrapper->mJSObj = obj;
00874 
00875   entry->mJSObjWrapper = wrapper;
00876 
00877   NS_ASSERTION(wrapper->mNpp == npp, "nsJSObjWrapper::mNpp not initialized!");
00878 
00879   // Root the JSObject, its lifetime is now tied to that of the
00880   // NPObject.
00881   if (!::JS_AddNamedRoot(cx, &wrapper->mJSObj, "nsJSObjWrapper::mJSObject")) {
00882     NS_ERROR("Failed to root JSObject!");
00883 
00884     _releaseobject(wrapper);
00885 
00886     PL_DHashTableRawRemove(&sJSObjWrappers, entry);
00887 
00888     return nsnull;
00889   }
00890 
00891   return wrapper;
00892 }
00893 
00894 static NPObject *
00895 GetNPObject(JSContext *cx, JSObject *obj)
00896 {
00897   while (JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
00898     obj = ::JS_GetPrototype(cx, obj);
00899   }
00900 
00901   if (!obj) {
00902     return nsnull;
00903   }
00904 
00905   return (NPObject *)::JS_GetPrivate(cx, obj);
00906 }
00907 
00908 JS_STATIC_DLL_CALLBACK(JSBool)
00909 NPObjWrapper_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00910 {
00911   NPObject *npobj = GetNPObject(cx, obj);
00912 
00913   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
00914       !npobj->_class->hasMethod) {
00915     ThrowJSException(cx, "Bad NPObject as private data!");
00916 
00917     return JS_FALSE;
00918   }
00919 
00920   // We must permit methods here since JS_DefineUCFunction() will add
00921   // the function as a property
00922   if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id) &&
00923       !npobj->_class->hasMethod(npobj, (NPIdentifier)id)) {
00924     ThrowJSException(cx, "Trying to add unsupported property on scriptable "
00925                      "plugin object!");
00926 
00927     return JS_FALSE;
00928   }
00929 
00930   return ReportExceptionIfPending(cx);
00931 }
00932 
00933 JS_STATIC_DLL_CALLBACK(JSBool)
00934 NPObjWrapper_DelProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00935 {
00936   NPObject *npobj = GetNPObject(cx, obj);
00937 
00938   if (!npobj || !npobj->_class || !npobj->_class->hasProperty) {
00939     ThrowJSException(cx, "Bad NPObject as private data!");
00940 
00941     return JS_FALSE;
00942   }
00943 
00944   if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
00945     ThrowJSException(cx, "Trying to remove unsupported property on scriptable "
00946                      "plugin object!");
00947 
00948     return JS_FALSE;
00949   }
00950 
00951   return ReportExceptionIfPending(cx);
00952 }
00953 
00954 JS_STATIC_DLL_CALLBACK(JSBool)
00955 NPObjWrapper_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
00956 {
00957   NPObject *npobj = GetNPObject(cx, obj);
00958 
00959   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
00960       !npobj->_class->setProperty) {
00961     ThrowJSException(cx, "Bad NPObject as private data!");
00962 
00963     return JS_FALSE;
00964   }
00965 
00966   if (!npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
00967     ThrowJSException(cx, "Trying to set unsupported property on scriptable "
00968                      "plugin object!");
00969 
00970     return JS_FALSE;
00971   }
00972 
00973   // Find out what plugin (NPP) is the owner of the object we're
00974   // manipulating, and make it own any JSObject wrappers created here.
00975   NPP npp = LookupNPP(npobj);
00976 
00977   if (!npp) {
00978     ThrowJSException(cx, "No NPP found for NPObject!");
00979 
00980     return JS_FALSE;
00981   }
00982 
00983   NPVariant npv;
00984   if (!JSValToNPVariant(npp, cx, *vp, &npv)) {
00985     ThrowJSException(cx, "Error converting jsval to NPVariant!");
00986 
00987     return JS_FALSE;
00988   }
00989 
00990   JSBool ok = npobj->_class->setProperty(npobj, (NPIdentifier)id, &npv);
00991 
00992   // Release the variant
00993   _releasevariantvalue(&npv);
00994 
00995   if (!ok) {
00996     ThrowJSException(cx, "Error setting property on scriptable plugin "
00997                      "object!");
00998 
00999     return JS_FALSE;
01000   }
01001 
01002   return ReportExceptionIfPending(cx);
01003 }
01004 
01005 JS_STATIC_DLL_CALLBACK(JSBool)
01006 NPObjWrapper_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
01007 {
01008   NPObject *npobj = GetNPObject(cx, obj);
01009 
01010   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
01011       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
01012     ThrowJSException(cx, "Bad NPObject as private data!");
01013 
01014     return JS_FALSE;
01015   }
01016 
01017   PRBool hasProperty = npobj->_class->hasProperty(npobj, (NPIdentifier)id);
01018   PRBool hasMethod = npobj->_class->hasMethod(npobj, (NPIdentifier)id);
01019   NPP npp = nsnull;
01020   if (hasProperty) {
01021     // Find out what plugin (NPP) is the owner of the object we're
01022     // manipulating, and make it own any JSObject wrappers created
01023     // here.
01024     npp = LookupNPP(npobj);
01025     if (!npp) {
01026       ThrowJSException(cx, "No NPP found for NPObject!");
01027 
01028       return JS_FALSE;
01029     }
01030   }
01031 
01032   // To support ambiguous members, we return NPObject Member class here.
01033   if (hasProperty && hasMethod)
01034     return CreateNPObjectMember(npp, cx, obj, npobj, id, vp);
01035 
01036   if (hasProperty) {
01037     NPVariant npv;
01038     VOID_TO_NPVARIANT(npv);
01039 
01040     if (!npobj->_class->getProperty(npobj, (NPIdentifier)id, &npv)) {
01041       ThrowJSException(cx, "Error setting property on scriptable plugin "
01042                        "object!");
01043 
01044       return JS_FALSE;
01045     }
01046 
01047     *vp = NPVariantToJSVal(npp, cx, &npv);
01048 
01049     // *vp now owns the value, release our reference.
01050     _releasevariantvalue(&npv);
01051 
01052     return JS_TRUE;
01053   }
01054 
01055   if (!hasMethod) {
01056     ThrowJSException(cx, "Trying to get unsupported property on scriptable "
01057                      "plugin object!");
01058 
01059     return JS_FALSE;
01060   }
01061 
01062   return ReportExceptionIfPending(cx);
01063 }
01064 
01065 JS_STATIC_DLL_CALLBACK(JSBool)
01066 CallNPMethod(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01067              jsval *rval)
01068 {
01069   while (JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
01070     obj = ::JS_GetPrototype(cx, obj);
01071   }
01072 
01073   if (!obj) {
01074     ThrowJSException(cx, "NPMethod called on non-NPObject wrapped JSObject!");
01075 
01076     return JS_FALSE;
01077   }
01078 
01079   NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
01080 
01081   if (!npobj || !npobj->_class || !npobj->_class->invoke) {
01082     ThrowJSException(cx, "Bad NPObject as private data!");
01083 
01084     return JS_FALSE;
01085   }
01086 
01087   // Find out what plugin (NPP) is the owner of the object we're
01088   // manipulating, and make it own any JSObject wrappers created here.
01089   NPP npp = LookupNPP(npobj);
01090 
01091   if (!npp) {
01092     ThrowJSException(cx, "Error finding NPP for NPObject!");
01093 
01094     return JS_FALSE;
01095   }
01096 
01097   NPVariant npargs_buf[8];
01098   NPVariant *npargs = npargs_buf;
01099 
01100   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
01101     // Our stack buffer isn't large enough to hold all arguments,
01102     // malloc a buffer.
01103     npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
01104 
01105     if (!npargs) {
01106       ThrowJSException(cx, "Out of memory!");
01107 
01108       return JS_FALSE;
01109     }
01110   }
01111 
01112   // Convert arguments
01113   PRUint32 i;
01114   for (i = 0; i < argc; ++i) {
01115     if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
01116       ThrowJSException(cx, "Error converting jsvals to NPVariants!");
01117 
01118       return JS_FALSE;
01119     }
01120   }
01121 
01122   NPVariant v;
01123   VOID_TO_NPVARIANT(v);
01124 
01125   JSObject *funobj = JSVAL_TO_OBJECT(argv[-2]);
01126   JSBool ok;
01127 
01128   if (funobj != obj) {
01129     // A obj.function() style call is made, get the method name from
01130     // the function object.
01131 
01132     JSFunction *fun = (JSFunction *)::JS_GetPrivate(cx, funobj);
01133     jsval method = STRING_TO_JSVAL(::JS_GetFunctionId(fun));
01134 
01135     ok = npobj->_class->invoke(npobj, (NPIdentifier)method, npargs, argc, &v);
01136   } else {
01137     // obj is a callable object that is being called, no method name
01138     // available then. Invoke the default method.
01139 
01140     ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
01141   }
01142 
01143   // Release arguments.
01144   for (i = 0; i < argc; ++i) {
01145     _releasevariantvalue(npargs + i);
01146   }
01147 
01148   if (npargs != npargs_buf) {
01149     PR_Free(npargs);
01150   }
01151 
01152   if (!ok) {
01153     ThrowJSException(cx, "Error calling method on NPObject!");
01154 
01155     return JS_FALSE;
01156   }
01157 
01158   *rval = NPVariantToJSVal(npp, cx, &v);
01159 
01160   // *rval now owns the value, release our reference.
01161   _releasevariantvalue(&v);
01162 
01163   return ReportExceptionIfPending(cx);
01164 }
01165 
01166 
01167 JS_STATIC_DLL_CALLBACK(JSBool)
01168 NPObjWrapper_NewResolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
01169                         JSObject **objp)
01170 {
01171   NPObject *npobj = GetNPObject(cx, obj);
01172 
01173   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
01174       !npobj->_class->hasMethod) {
01175     ThrowJSException(cx, "Bad NPObject as private data!");
01176 
01177     return JS_FALSE;
01178   }
01179 
01180   if (npobj->_class->hasProperty(npobj, (NPIdentifier)id)) {
01181     JSBool ok;
01182 
01183     if (JSVAL_IS_STRING(id)) {
01184       JSString *str = JSVAL_TO_STRING(id);
01185 
01186       ok = ::JS_DefineUCProperty(cx, obj, ::JS_GetStringChars(str),
01187                                  ::JS_GetStringLength(str), JSVAL_VOID, nsnull,
01188                                  nsnull, JSPROP_ENUMERATE);
01189     } else {
01190       ok = ::JS_DefineElement(cx, obj, JSVAL_TO_INT(id), JSVAL_VOID, nsnull,
01191                               nsnull, JSPROP_ENUMERATE);
01192     }
01193 
01194     if (!ok) {
01195       return JS_FALSE;
01196     }
01197 
01198     *objp = obj;
01199   } else if (npobj->_class->hasMethod(npobj, (NPIdentifier)id)) {
01200     JSString *str = nsnull;
01201 
01202     if (JSVAL_IS_STRING(id)) {
01203       str = JSVAL_TO_STRING(id);
01204     } else {
01205       NS_ASSERTION(JSVAL_IS_INT(id), "id must be either string or int!\n");
01206 
01207       str = ::JS_ValueToString(cx, id);
01208 
01209       if (!str) {
01210         // OOM. The JS engine throws exceptions for us in this case.
01211 
01212         return JS_FALSE;
01213       }
01214     }
01215 
01216     JSFunction *fnc =
01217       ::JS_DefineUCFunction(cx, obj, ::JS_GetStringChars(str),
01218                             ::JS_GetStringLength(str), CallNPMethod, 0,
01219                             JSPROP_ENUMERATE);
01220 
01221     *objp = obj;
01222 
01223     return fnc != nsnull;
01224   }
01225 
01226   return ReportExceptionIfPending(cx);
01227 }
01228 
01229 JS_STATIC_DLL_CALLBACK(void)
01230 NPObjWrapper_Finalize(JSContext *cx, JSObject *obj)
01231 {
01232   NPObject *npobj = (NPObject *)::JS_GetPrivate(cx, obj);
01233 
01234   if (npobj) {
01235     if (sNPObjWrappers.ops) {
01236       PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
01237     }
01238 
01239     // Let go of our NPObject
01240     _releaseobject(npobj);
01241   }
01242 
01243   OnWrapperDestroyed();
01244 }
01245 
01246 JS_STATIC_DLL_CALLBACK(JSBool)
01247 NPObjWrapper_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
01248                   jsval *rval)
01249 {
01250   return CallNPMethod(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
01251 }
01252 
01253 class NPObjWrapperHashEntry : public PLDHashEntryHdr
01254 {
01255 public:
01256   NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
01257   JSObject *mJSObj;
01258   NPP mNpp;
01259 };
01260 
01261 
01262 // Look up or create a JSObject that wraps the NPObject npobj.
01263 
01264 // static
01265 JSObject *
01266 nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
01267 {
01268   if (!npobj) {
01269     NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
01270 
01271     return nsnull;
01272   }
01273 
01274   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
01275     // npobj is one of our own, return its existing JSObject.
01276 
01277     return ((nsJSObjWrapper *)npobj)->mJSObj;
01278   }
01279 
01280   if (!npp) {
01281     NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
01282 
01283     return nsnull;
01284   }
01285 
01286   if (!sNPObjWrappers.ops) {
01287     // No hash yet (or any more), initalize it.
01288 
01289     if (!PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nsnull,
01290                            sizeof(NPObjWrapperHashEntry), 16)) {
01291       NS_ERROR("Error initializing PLDHashTable!");
01292 
01293       return nsnull;
01294     }
01295   }
01296 
01297   NPObjWrapperHashEntry *entry =
01298     NS_STATIC_CAST(NPObjWrapperHashEntry *,
01299                    PL_DHashTableOperate(&sNPObjWrappers, npobj,
01300                                         PL_DHASH_ADD));
01301   if (!entry) {
01302     // Out of memory
01303     JS_ReportOutOfMemory(cx);
01304 
01305     return nsnull;
01306   }
01307 
01308   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
01309     // Found a live NPObject wrapper, return it.
01310     return entry->mJSObj;
01311   }
01312 
01313   entry->mNPObj = npobj;
01314   entry->mNpp = npp;
01315 
01316   PRUint32 generation = sNPObjWrappers.generation;
01317 
01318   // No existing JSObject, create one.
01319 
01320   JSObject *obj = ::JS_NewObject(cx, &sNPObjectJSWrapperClass, nsnull, nsnull);
01321 
01322   if (generation != sNPObjWrappers.generation) {
01323       // Reload entry if the JS_NewObject call caused a GC and reallocated
01324       // the table (see bug 445229). This is guaranteed to succeed.
01325 
01326       entry = static_cast<NPObjWrapperHashEntry *>
01327         (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
01328       NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
01329                    "Hashtable didn't find what we just added?");
01330   }
01331 
01332   if (!obj) {
01333     // OOM? Remove the stale entry from the hash.
01334 
01335     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
01336 
01337     return nsnull;
01338   }
01339 
01340   OnWrapperCreated();
01341 
01342   entry->mJSObj = obj;
01343 
01344   if (!::JS_SetPrivate(cx, obj, npobj)) {
01345     NS_ERROR("Error setting private NPObject data in JS wrapper!");
01346 
01347     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
01348 
01349     return nsnull;
01350   }
01351 
01352   // The new JSObject now holds on to npobj
01353   _retainobject(npobj);
01354 
01355   return obj;
01356 }
01357 
01358 
01359 // PLDHashTable enumeration callbacks for destruction code.
01360 PR_STATIC_CALLBACK(PLDHashOperator)
01361 JSObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
01362                                     PRUint32 number, void *arg)
01363 {
01364   JSObjWrapperHashEntry *entry = (JSObjWrapperHashEntry *)hdr;
01365 
01366   nsJSObjWrapper *npobj = entry->mJSObjWrapper;
01367 
01368   if (npobj->mNpp == arg) {
01369     // Prevent invalidate() and _releaseobject() from touching the hash
01370     // we're enumerating.
01371     const PLDHashTableOps *ops = table->ops;
01372     table->ops = nsnull;
01373 
01374     if (npobj->_class && npobj->_class->invalidate) {
01375       npobj->_class->invalidate(npobj);
01376     }
01377 
01378     _releaseobject(npobj);
01379 
01380     table->ops = ops;
01381 
01382     return PL_DHASH_REMOVE;
01383   }
01384 
01385   return PL_DHASH_NEXT;
01386 }
01387 
01388 PR_STATIC_CALLBACK(PLDHashOperator)
01389 NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
01390                                     PRUint32 number, void *arg)
01391 {
01392   NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
01393 
01394   if (entry->mNpp == arg) {
01395     NPObject *npobj = entry->mNPObj;
01396 
01397     if (npobj->_class && npobj->_class->invalidate) {
01398       npobj->_class->invalidate(npobj);
01399     }
01400 
01401     // Force deallocation of plugin objects since the plugin they came
01402     // from is being torn down.
01403     if (npobj->_class && npobj->_class->deallocate) {
01404       npobj->_class->deallocate(npobj);
01405     } else {
01406       PR_Free(npobj);
01407     }
01408 
01409     JSContext *cx = GetJSContext((NPP)arg);
01410 
01411     if (cx) {
01412       ::JS_SetPrivate(cx, entry->mJSObj, nsnull);
01413     } else {
01414       NS_ERROR("dangling entry->mJSObj JSPrivate because we can't find cx");
01415     }
01416 
01417     return PL_DHASH_REMOVE;
01418   }
01419 
01420   return PL_DHASH_NEXT;
01421 }
01422 
01423 // static
01424 void
01425 nsJSNPRuntime::OnPluginDestroy(NPP npp)
01426 {
01427   if (sJSObjWrappers.ops) {
01428     PL_DHashTableEnumerate(&sJSObjWrappers,
01429                            JSObjWrapperPluginDestroyedCallback, npp);
01430   }
01431 
01432   if (sNPObjWrappers.ops) {
01433     PL_DHashTableEnumerate(&sNPObjWrappers,
01434                            NPObjWrapperPluginDestroyedCallback, npp);
01435   }
01436 
01437   // If this plugin was scripted from a webpage, the plugin's
01438   // scriptable object will be on the DOM element's prototype
01439   // chain. Now that the plugin is being destroyed we need to pull the
01440   // plugin's scriptable object out of that prototype chain.
01441   JSContext *cx = GetJSContext(npp);
01442   if (!cx || !npp) {
01443     return;
01444   }
01445 
01446   // Find the plugin instance so that we can (eventually) get to the
01447   // DOM element
01448   ns4xPluginInstance *inst = (ns4xPluginInstance *)npp->ndata;
01449   if (!inst) {
01450     return;
01451   }
01452 
01453   nsCOMPtr<nsIPluginInstancePeer> pip;
01454   inst->GetPeer(getter_AddRefs(pip));
01455   nsCOMPtr<nsIPluginTagInfo2> pti2(do_QueryInterface(pip));
01456   if (!pti2) {
01457     return;
01458   }
01459 
01460   nsCOMPtr<nsIDOMElement> element;
01461   pti2->GetDOMElement(getter_AddRefs(element));
01462   if (!element) {
01463     return;
01464   }
01465 
01466   // Get the DOM element's JS object.
01467   nsCOMPtr<nsIXPConnect> xpc(do_GetService(nsIXPConnect::GetCID()));
01468   if (!xpc) {
01469     return;
01470   }
01471 
01472   // OK.  Now we have to get our hands on the right scope object, since
01473   // GetWrappedNativeOfNativeObject doesn't call PreCreate and hence won't get
01474   // the right scope if we pass in something bogus.  The right scope lives on
01475   // the script global of the element's document.
01476   // XXXbz we MUST have a better way of doing this... perhaps
01477   // GetWrappedNativeOfNativeObject _should_ call preCreate?
01478   nsCOMPtr<nsIContent> content(do_QueryInterface(element));
01479   if (!content) {
01480     return;
01481   }
01482 
01483   nsIDocument* doc = content->GetOwnerDoc();
01484   if (!doc) {
01485     return;
01486   }
01487 
01488   nsIScriptGlobalObject* sgo = doc->GetScriptGlobalObject();
01489   if (!sgo) {
01490     return;
01491   }
01492 
01493   JSObject* globalObj = sgo->GetGlobalJSObject();
01494 
01495 #ifdef DEBUG
01496   nsIScriptContext* scx = sgo->GetContext();
01497   if (scx) {
01498     NS_ASSERTION((JSContext *)scx->GetNativeContext() == cx,
01499                  "Unexpected JS context");
01500   }
01501 #endif
01502 
01503   nsCOMPtr<nsISupports> supp(do_QueryInterface(element));
01504   nsCOMPtr<nsIXPConnectWrappedNative> holder;
01505   xpc->GetWrappedNativeOfNativeObject(cx, globalObj, supp,
01506                                       NS_GET_IID(nsISupports),
01507                                       getter_AddRefs(holder));
01508   if (!holder) {
01509     return;
01510   }
01511 
01512   JSObject *obj, *proto;
01513   holder->GetJSObject(&obj);
01514 
01515   // Loop over the DOM element's JS object prototype chain and remove
01516   // all JS objects of the class sNPObjectJSWrapperClass (there should
01517   // be only one, but remove all instances found in case the page put
01518   // more than one of the plugin's scriptable objects on the prototype
01519   // chain).
01520   while (obj && (proto = ::JS_GetPrototype(cx, obj))) {
01521     if (JS_GET_CLASS(cx, proto) == &sNPObjectJSWrapperClass) {
01522       // We found an NPObject on the proto chain, get its prototype...
01523       proto = ::JS_GetPrototype(cx, proto);
01524 
01525       // ... and pull it out of the chain.
01526       ::JS_SetPrototype(cx, obj, proto);
01527     }
01528 
01529     obj = proto;
01530   }
01531 }
01532 
01533 
01534 // Find the NPP for a NPObject.
01535 static NPP
01536 LookupNPP(NPObject *npobj)
01537 {
01538   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
01539     NS_ERROR("NPP requested for NPObject of class "
01540              "nsJSObjWrapper::sJSObjWrapperNPClass!\n");
01541 
01542     return nsnull;
01543   }
01544 
01545 
01546 
01547   NPObjWrapperHashEntry *entry =
01548     NS_STATIC_CAST(NPObjWrapperHashEntry *,
01549                    PL_DHashTableOperate(&sNPObjWrappers, npobj,
01550                                         PL_DHASH_ADD));
01551 
01552   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
01553     return nsnull;
01554   }
01555 
01556   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
01557 
01558   return entry->mNpp;
01559 }
01560 
01561 bool
01562 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj,
01563                      NPObject* npobj, jsval id, jsval *vp)
01564 {
01565   NS_ENSURE_TRUE(vp, false);
01566 
01567   if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
01568       !npobj->_class->invoke) {
01569     ThrowJSException(cx, "Bad NPObject");
01570 
01571     return false;
01572   }
01573 
01574   NPObjectMemberPrivate *memberPrivate =
01575     (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
01576   if (!memberPrivate)
01577     return false;
01578 
01579   // Make sure to clear all members in case something fails here
01580   // during initialization.
01581   memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
01582 
01583   JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass, nsnull, nsnull);
01584   if (!memobj) {
01585     PR_Free(memberPrivate);
01586     return false;
01587   }
01588 
01589   *vp = OBJECT_TO_JSVAL(memobj);
01590   ::JS_AddRoot(cx, vp);
01591 
01592   ::JS_SetPrivate(cx, memobj, (void *)memberPrivate);
01593 
01594   jsval fieldValue;
01595   NPVariant npv;
01596   VOID_TO_NPVARIANT(npv);
01597   if (!npobj->_class->getProperty(npobj, (NPIdentifier)id, &npv)) {
01598     ::JS_RemoveRoot(cx, vp);
01599     return false;
01600   }
01601 
01602   fieldValue = NPVariantToJSVal(npp, cx, &npv);
01603 
01604   // npobjWrapper is the JSObject through which we make sure we don't
01605   // outlive the underlying NPObject, so make sure it points to the
01606   // real JSObject wrapper for the NPObject.
01607   while (JS_GET_CLASS(cx, obj) != &sNPObjectJSWrapperClass) {
01608     obj = ::JS_GetPrototype(cx, obj);
01609   }
01610 
01611   memberPrivate->npobjWrapper = obj;
01612 
01613   memberPrivate->fieldValue = fieldValue;
01614   memberPrivate->methodName = id;
01615   memberPrivate->npp = npp;
01616 
01617   ::JS_RemoveRoot(cx, vp);
01618 
01619   return true;
01620 }
01621 
01622 JS_STATIC_DLL_CALLBACK(JSBool)
01623 NPObjectMember_Convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
01624 {
01625   NPObjectMemberPrivate *memberPrivate =
01626     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
01627                                                      &sNPObjectMemberClass,
01628                                                      nsnull);
01629   NS_ASSERTION(memberPrivate, "no Ambiguous Member Private data!");
01630 
01631   switch (type) {
01632   case JSTYPE_VOID:
01633   case JSTYPE_STRING:
01634   case JSTYPE_NUMBER:
01635   case JSTYPE_BOOLEAN:
01636   case JSTYPE_OBJECT:
01637     *vp = memberPrivate->fieldValue;
01638     return JS_TRUE;
01639   case JSTYPE_FUNCTION:
01640     // Leave this to NPObjectMember_Call.
01641     return JS_TRUE;
01642   default:
01643     NS_ERROR("illegal operation on JSObject prototype object");
01644     return JS_FALSE;
01645   }
01646 }
01647 
01648 JS_STATIC_DLL_CALLBACK(void)
01649 NPObjectMember_Finalize(JSContext *cx, JSObject *obj)
01650 {
01651   NPObjectMemberPrivate *memberPrivate;
01652 
01653   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(cx, obj);
01654   if (!memberPrivate)
01655     return;
01656 
01657   PR_Free(memberPrivate);
01658 }
01659 
01660 JS_STATIC_DLL_CALLBACK(JSBool)
01661 NPObjectMember_Call(JSContext *cx, JSObject *obj,
01662                     uintN argc, jsval *argv, jsval *rval)
01663 {
01664   JSObject *memobj = JSVAL_TO_OBJECT(argv[-2]);
01665   NS_ENSURE_TRUE(memobj, JS_FALSE);
01666 
01667   NPObjectMemberPrivate *memberPrivate =
01668     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
01669                                                      &sNPObjectMemberClass,
01670                                                      nsnull);
01671   if (!memberPrivate || !memberPrivate->npobjWrapper)
01672     return JS_FALSE;
01673 
01674   NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
01675   if (!npobj) {
01676     ThrowJSException(cx, "Call on invalid member object");
01677 
01678     return JS_FALSE;
01679   }
01680 
01681   NPVariant npargs_buf[8];
01682   NPVariant *npargs = npargs_buf;
01683 
01684   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
01685     // Our stack buffer isn't large enough to hold all arguments,
01686     // malloc a buffer.
01687     npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
01688 
01689     if (!npargs) {
01690       ThrowJSException(cx, "Out of memory!");
01691 
01692       return JS_FALSE;
01693     }
01694   }
01695 
01696   // Convert arguments
01697   PRUint32 i;
01698   for (i = 0; i < argc; ++i) {
01699     if (!JSValToNPVariant(memberPrivate->npp, cx, argv[i], npargs + i)) {
01700       ThrowJSException(cx, "Error converting jsvals to NPVariants!");
01701 
01702       if (npargs != npargs_buf) {
01703         PR_Free(npargs);
01704       }
01705 
01706       return JS_FALSE;
01707     }
01708   }
01709 
01710   NPVariant npv;
01711   JSBool ok;
01712   ok = npobj->_class->invoke(npobj, (NPIdentifier)memberPrivate->methodName,
01713                              npargs, argc, &npv);
01714 
01715   // Release arguments.
01716   for (i = 0; i < argc; ++i) {
01717     _releasevariantvalue(npargs + i);
01718   }
01719 
01720   if (npargs != npargs_buf) {
01721     PR_Free(npargs);
01722   }
01723 
01724   if (!ok) {
01725     ThrowJSException(cx, "Error calling method on NPObject!");
01726 
01727     return JS_FALSE;
01728   }
01729 
01730   *rval = NPVariantToJSVal(memberPrivate->npp, cx, &npv);
01731 
01732   // *rval now owns the value, release our reference.
01733   _releasevariantvalue(&npv);
01734 
01735   return ReportExceptionIfPending(cx);
01736 }
01737 
01738 JS_STATIC_DLL_CALLBACK(uint32)
01739 NPObjectMember_Mark(JSContext *cx, JSObject *obj, void *arg)
01740 {
01741   NPObjectMemberPrivate *memberPrivate =
01742     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
01743                                                      &sNPObjectMemberClass,
01744                                                      nsnull);
01745   if (!memberPrivate)
01746     return 0;
01747 
01748   if (!JSVAL_IS_PRIMITIVE(memberPrivate->fieldValue)) {
01749     ::JS_MarkGCThing(cx, JSVAL_TO_OBJECT(memberPrivate->fieldValue),
01750                      "NPObject Member => fieldValue", arg);
01751   }
01752 
01753   // There's no strong reference from our private data to the
01754   // NPObject, so make sure to mark the NPObject wrapper to keep the
01755   // NPObject alive as long as this NPObjectMember is alive.
01756   if (memberPrivate->npobjWrapper) {
01757     ::JS_MarkGCThing(cx, memberPrivate->npobjWrapper,
01758                      "NPObject Member => npobjWrapper", arg);
01759   }
01760 
01761   return 0;
01762 }