dom/plugins/base/nsJSNPRuntime.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "base/basictypes.h"
     8 #include "jsfriendapi.h"
     9 #include "jswrapper.h"
    11 #include "nsIInterfaceRequestorUtils.h"
    12 #include "nsJSNPRuntime.h"
    13 #include "nsNPAPIPlugin.h"
    14 #include "nsNPAPIPluginInstance.h"
    15 #include "nsIScriptGlobalObject.h"
    16 #include "nsIScriptContext.h"
    17 #include "nsDOMJSUtils.h"
    18 #include "nsCxPusher.h"
    19 #include "nsIDocument.h"
    20 #include "nsIJSRuntimeService.h"
    21 #include "nsIXPConnect.h"
    22 #include "nsIDOMElement.h"
    23 #include "prmem.h"
    24 #include "nsIContent.h"
    25 #include "nsPluginInstanceOwner.h"
    26 #include "nsWrapperCacheInlines.h"
    27 #include "js/HashTable.h"
    28 #include "mozilla/HashFunctions.h"
    31 #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
    33 using namespace mozilla::plugins::parent;
    34 using namespace mozilla;
    36 #include "mozilla/plugins/PluginScriptableObjectParent.h"
    37 using mozilla::plugins::PluginScriptableObjectParent;
    38 using mozilla::plugins::ParentNPObject;
    40 struct JSObjWrapperHasher : public js::DefaultHasher<nsJSObjWrapperKey>
    41 {
    42   typedef nsJSObjWrapperKey Key;
    43   typedef Key Lookup;
    45   static uint32_t hash(const Lookup &l) {
    46     return HashGeneric(l.mJSObj, l.mNpp);
    47   }
    49   static void rekey(Key &k, const Key& newKey) {
    50     MOZ_ASSERT(k.mNpp == newKey.mNpp);
    51     k.mJSObj = newKey.mJSObj;
    52   }
    53 };
    55 // Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
    56 // will be one wrapper per JSObject per plugin instance, i.e. if two
    57 // plugins access the JSObject x, two wrappers for x will be
    58 // created. This is needed to be able to properly drop the wrappers
    59 // when a plugin is torn down in case there's a leak in the plugin (we
    60 // don't want to leak the world just because a plugin leaks an
    61 // NPObject).
    62 typedef js::HashMap<nsJSObjWrapperKey,
    63                     nsJSObjWrapper*,
    64                     JSObjWrapperHasher,
    65                     js::SystemAllocPolicy> JSObjWrapperTable;
    66 static JSObjWrapperTable sJSObjWrappers;
    68 // Whether it's safe to iterate sJSObjWrappers.  Set to true when sJSObjWrappers
    69 // has been initialized and is not currently being enumerated.
    70 static bool sJSObjWrappersAccessible = false;
    72 // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
    73 static PLDHashTable sNPObjWrappers;
    75 // Global wrapper count. This includes JSObject wrappers *and*
    76 // NPObject wrappers. When this count goes to zero, there are no more
    77 // wrappers and we can kill off hash tables etc.
    78 static int32_t sWrapperCount;
    80 // The JSRuntime. Used to unroot JSObjects when no JSContext is
    81 // reachable.
    82 static JSRuntime *sJSRuntime;
    84 static nsTArray<NPObject*>* sDelayedReleases;
    86 namespace {
    88 inline bool
    89 NPObjectIsOutOfProcessProxy(NPObject *obj)
    90 {
    91   return obj->_class == PluginScriptableObjectParent::GetClass();
    92 }
    94 } // anonymous namespace
    96 // Helper class that reports any JS exceptions that were thrown while
    97 // the plugin executed JS.
    99 class AutoJSExceptionReporter
   100 {
   101 public:
   102   AutoJSExceptionReporter(JSContext *cx)
   103     : mCx(cx)
   104   {
   105   }
   107   ~AutoJSExceptionReporter()
   108   {
   109     JS_ReportPendingException(mCx);
   110   }
   112 protected:
   113   JSContext *mCx;
   114 };
   117 NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
   118   {
   119     NP_CLASS_STRUCT_VERSION,
   120     nsJSObjWrapper::NP_Allocate,
   121     nsJSObjWrapper::NP_Deallocate,
   122     nsJSObjWrapper::NP_Invalidate,
   123     nsJSObjWrapper::NP_HasMethod,
   124     nsJSObjWrapper::NP_Invoke,
   125     nsJSObjWrapper::NP_InvokeDefault,
   126     nsJSObjWrapper::NP_HasProperty,
   127     nsJSObjWrapper::NP_GetProperty,
   128     nsJSObjWrapper::NP_SetProperty,
   129     nsJSObjWrapper::NP_RemoveProperty,
   130     nsJSObjWrapper::NP_Enumerate,
   131     nsJSObjWrapper::NP_Construct
   132   };
   134 static bool
   135 NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
   137 static bool
   138 NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded);
   140 static bool
   141 NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
   142                          JS::MutableHandle<JS::Value> vp);
   144 static bool
   145 NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
   147 static bool
   148 NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op,
   149                           JS::Value *statep, jsid *idp);
   151 static bool
   152 NPObjWrapper_NewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   153                         JS::MutableHandle<JSObject*> objp);
   155 static bool
   156 NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
   158 static void
   159 NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj);
   161 static bool
   162 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
   164 static bool
   165 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
   167 static bool
   168 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
   169                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
   170                      JS::MutableHandle<JS::Value> vp);
   172 const JSClass sNPObjectJSWrapperClass =
   173   {
   174     NPRUNTIME_JSCLASS_NAME,
   175     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
   176     NPObjWrapper_AddProperty,
   177     NPObjWrapper_DelProperty,
   178     NPObjWrapper_GetProperty,
   179     NPObjWrapper_SetProperty,
   180     (JSEnumerateOp)NPObjWrapper_newEnumerate,
   181     (JSResolveOp)NPObjWrapper_NewResolve,
   182     NPObjWrapper_Convert,
   183     NPObjWrapper_Finalize,
   184     NPObjWrapper_Call,
   185     nullptr,                                                /* hasInstance */
   186     NPObjWrapper_Construct
   187   };
   189 typedef struct NPObjectMemberPrivate {
   190     JS::Heap<JSObject *> npobjWrapper;
   191     JS::Heap<JS::Value> fieldValue;
   192     JS::Heap<jsid> methodName;
   193     NPP   npp;
   194 } NPObjectMemberPrivate;
   196 static bool
   197 NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp);
   199 static void
   200 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
   202 static bool
   203 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
   205 static void
   206 NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
   208 static const JSClass sNPObjectMemberClass =
   209   {
   210     "NPObject Ambiguous Member class", JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
   211     JS_PropertyStub, JS_DeletePropertyStub,
   212     JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub,
   213     JS_ResolveStub, NPObjectMember_Convert,
   214     NPObjectMember_Finalize, NPObjectMember_Call,
   215     nullptr, nullptr, NPObjectMember_Trace
   216   };
   218 static void
   219 OnWrapperDestroyed();
   221 static void
   222 DelayedReleaseGCCallback(JSGCStatus status)
   223 {
   224   if (JSGC_END == status) {
   225     // Take ownership of sDelayedReleases and null it out now. The
   226     // _releaseobject call below can reenter GC and double-free these objects.
   227     nsAutoPtr<nsTArray<NPObject*> > delayedReleases(sDelayedReleases);
   228     sDelayedReleases = nullptr;
   230     if (delayedReleases) {
   231       for (uint32_t i = 0; i < delayedReleases->Length(); ++i) {
   232         NPObject* obj = (*delayedReleases)[i];
   233         if (obj)
   234           _releaseobject(obj);
   235         OnWrapperDestroyed();
   236       }
   237     }
   238   }
   239 }
   241 static void
   242 OnWrapperCreated()
   243 {
   244   if (sWrapperCount++ == 0) {
   245     static const char rtsvc_id[] = "@mozilla.org/js/xpc/RuntimeService;1";
   246     nsCOMPtr<nsIJSRuntimeService> rtsvc(do_GetService(rtsvc_id));
   247     if (!rtsvc)
   248       return;
   250     rtsvc->GetRuntime(&sJSRuntime);
   251     NS_ASSERTION(sJSRuntime != nullptr, "no JSRuntime?!");
   253     // Register our GC callback to perform delayed destruction of finalized
   254     // NPObjects. Leave this callback around and don't ever unregister it.
   255     rtsvc->RegisterGCCallback(DelayedReleaseGCCallback);
   256   }
   257 }
   259 static void
   260 OnWrapperDestroyed()
   261 {
   262   NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
   264   if (--sWrapperCount == 0) {
   265     if (sJSObjWrappersAccessible) {
   266       MOZ_ASSERT(sJSObjWrappers.count() == 0);
   268       // No more wrappers, and our hash was initialized. Finish the
   269       // hash to prevent leaking it.
   270       sJSObjWrappers.finish();
   271       sJSObjWrappersAccessible = false;
   272     }
   274     if (sNPObjWrappers.ops) {
   275       MOZ_ASSERT(sNPObjWrappers.entryCount == 0);
   277       // No more wrappers, and our hash was initialized. Finish the
   278       // hash to prevent leaking it.
   279       PL_DHashTableFinish(&sNPObjWrappers);
   281       sNPObjWrappers.ops = nullptr;
   282     }
   284     // No more need for this.
   285     sJSRuntime = nullptr;
   286   }
   287 }
   289 namespace mozilla {
   290 namespace plugins {
   291 namespace parent {
   293 JSContext *
   294 GetJSContext(NPP npp)
   295 {
   296   NS_ENSURE_TRUE(npp, nullptr);
   298   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
   299   NS_ENSURE_TRUE(inst, nullptr);
   301   nsRefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
   302   NS_ENSURE_TRUE(owner, nullptr);
   304   nsCOMPtr<nsIDocument> doc;
   305   owner->GetDocument(getter_AddRefs(doc));
   306   NS_ENSURE_TRUE(doc, nullptr);
   308   nsCOMPtr<nsISupports> documentContainer = doc->GetContainer();
   309   nsCOMPtr<nsIScriptGlobalObject> sgo(do_GetInterface(documentContainer));
   310   NS_ENSURE_TRUE(sgo, nullptr);
   312   nsIScriptContext *scx = sgo->GetContext();
   313   NS_ENSURE_TRUE(scx, nullptr);
   315   return scx->GetNativeContext();
   316 }
   318 }
   319 }
   320 }
   322 static NPP
   323 LookupNPP(NPObject *npobj);
   326 static JS::Value
   327 NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
   328 {
   329   switch (variant->type) {
   330   case NPVariantType_Void :
   331     return JSVAL_VOID;
   332   case NPVariantType_Null :
   333     return JSVAL_NULL;
   334   case NPVariantType_Bool :
   335     return BOOLEAN_TO_JSVAL(NPVARIANT_TO_BOOLEAN(*variant));
   336   case NPVariantType_Int32 :
   337     {
   338       // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
   339       // with ints larger than what fits in a integer JS::Value.
   340       return ::JS_NumberValue(NPVARIANT_TO_INT32(*variant));
   341     }
   342   case NPVariantType_Double :
   343     {
   344       return ::JS_NumberValue(NPVARIANT_TO_DOUBLE(*variant));
   345     }
   346   case NPVariantType_String :
   347     {
   348       const NPString *s = &NPVARIANT_TO_STRING(*variant);
   349       NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
   351       JSString *str =
   352         ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length());
   354       if (str) {
   355         return STRING_TO_JSVAL(str);
   356       }
   358       break;
   359     }
   360   case NPVariantType_Object:
   361     {
   362       if (npp) {
   363         JSObject *obj =
   364           nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
   366         if (obj) {
   367           return OBJECT_TO_JSVAL(obj);
   368         }
   369       }
   371       NS_ERROR("Error wrapping NPObject!");
   373       break;
   374     }
   375   default:
   376     NS_ERROR("Unknown NPVariant type!");
   377   }
   379   NS_ERROR("Unable to convert NPVariant to jsval!");
   381   return JSVAL_VOID;
   382 }
   384 bool
   385 JSValToNPVariant(NPP npp, JSContext *cx, JS::Value val, NPVariant *variant)
   386 {
   387   NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
   389   if (JSVAL_IS_PRIMITIVE(val)) {
   390     if (val == JSVAL_VOID) {
   391       VOID_TO_NPVARIANT(*variant);
   392     } else if (JSVAL_IS_NULL(val)) {
   393       NULL_TO_NPVARIANT(*variant);
   394     } else if (JSVAL_IS_BOOLEAN(val)) {
   395       BOOLEAN_TO_NPVARIANT(JSVAL_TO_BOOLEAN(val), *variant);
   396     } else if (JSVAL_IS_INT(val)) {
   397       INT32_TO_NPVARIANT(JSVAL_TO_INT(val), *variant);
   398     } else if (JSVAL_IS_DOUBLE(val)) {
   399       double d = JSVAL_TO_DOUBLE(val);
   400       int i;
   401       if (JS_DoubleIsInt32(d, &i)) {
   402         INT32_TO_NPVARIANT(i, *variant);
   403       } else {
   404         DOUBLE_TO_NPVARIANT(d, *variant);
   405       }
   406     } else if (JSVAL_IS_STRING(val)) {
   407       JSString *jsstr = JSVAL_TO_STRING(val);
   408       size_t length;
   409       const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length);
   410       if (!chars) {
   411           return false;
   412       }
   414       nsDependentString str(chars, length);
   416       uint32_t len;
   417       char *p = ToNewUTF8String(str, &len);
   419       if (!p) {
   420         return false;
   421       }
   423       STRINGN_TO_NPVARIANT(p, len, *variant);
   424     } else {
   425       NS_ERROR("Unknown primitive type!");
   427       return false;
   428     }
   430     return true;
   431   }
   433   // The reflected plugin object may be in another compartment if the plugin
   434   // element has since been adopted into a new document. We don't bother
   435   // transplanting the plugin objects, and just do a unwrap with security
   436   // checks if we encounter one of them as an argument. If the unwrap fails,
   437   // we run with the original wrapped object, since sometimes there are
   438   // legitimate cases where a security wrapper ends up here (for example,
   439   // Location objects, which are _always_ behind security wrappers).
   440   JS::Rooted<JSObject*> obj(cx, val.toObjectOrNull());
   441   obj = js::CheckedUnwrap(obj);
   442   if (!obj) {
   443     obj = JSVAL_TO_OBJECT(val);
   444   }
   446   NPObject *npobj = nsJSObjWrapper::GetNewOrUsed(npp, cx, obj);
   447   if (!npobj) {
   448     return false;
   449   }
   451   // Pass over ownership of npobj to *variant
   452   OBJECT_TO_NPVARIANT(npobj, *variant);
   454   return true;
   455 }
   457 static void
   458 ThrowJSException(JSContext *cx, const char *message)
   459 {
   460   const char *ex = PeekException();
   462   if (ex) {
   463     nsAutoString ucex;
   465     if (message) {
   466       AppendASCIItoUTF16(message, ucex);
   468       AppendASCIItoUTF16(" [plugin exception: ", ucex);
   469     }
   471     AppendUTF8toUTF16(ex, ucex);
   473     if (message) {
   474       AppendASCIItoUTF16("].", ucex);
   475     }
   477     JSString *str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length());
   479     if (str) {
   480       JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
   481       ::JS_SetPendingException(cx, exn);
   482     }
   484     PopException();
   485   } else {
   486     ::JS_ReportError(cx, message);
   487   }
   488 }
   490 static bool
   491 ReportExceptionIfPending(JSContext *cx)
   492 {
   493   const char *ex = PeekException();
   495   if (!ex) {
   496     return true;
   497   }
   499   ThrowJSException(cx, nullptr);
   501   return false;
   502 }
   504 nsJSObjWrapper::nsJSObjWrapper(NPP npp)
   505   : mJSObj(GetJSContext(npp), nullptr), mNpp(npp)
   506 {
   507   MOZ_COUNT_CTOR(nsJSObjWrapper);
   508   OnWrapperCreated();
   509 }
   511 nsJSObjWrapper::~nsJSObjWrapper()
   512 {
   513   MOZ_COUNT_DTOR(nsJSObjWrapper);
   515   // Invalidate first, since it relies on sJSRuntime and sJSObjWrappers.
   516   NP_Invalidate(this);
   518   OnWrapperDestroyed();
   519 }
   521 // static
   522 NPObject *
   523 nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
   524 {
   525   NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
   526                "Huh, wrong class passed to NP_Allocate()!!!");
   528   return new nsJSObjWrapper(npp);
   529 }
   531 // static
   532 void
   533 nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
   534 {
   535   // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
   536   delete (nsJSObjWrapper *)npobj;
   537 }
   539 // static
   540 void
   541 nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
   542 {
   543   nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
   545   if (jsnpobj && jsnpobj->mJSObj) {
   547     if (sJSObjWrappersAccessible) {
   548       // Remove the wrapper from the hash
   549       nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
   550       JSObjWrapperTable::Ptr ptr = sJSObjWrappers.lookup(key);
   551       MOZ_ASSERT(ptr.found());
   552       sJSObjWrappers.remove(ptr);
   553     }
   555     // Forget our reference to the JSObject.
   556     jsnpobj->mJSObj = nullptr;
   557   }
   558 }
   560 static bool
   561 GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval)
   562 {
   563   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
   564                "id must be either string or int!\n");
   565   JS::Rooted<JSObject *> obj(cx, objArg);
   566   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   567   return ::JS_GetPropertyById(cx, obj, id, rval);
   568 }
   570 // static
   571 bool
   572 nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
   573 {
   574   NPP npp = NPPStack::Peek();
   575   JSContext *cx = GetJSContext(npp);
   577   if (!cx) {
   578     return false;
   579   }
   581   if (!npobj) {
   582     ThrowJSException(cx,
   583                      "Null npobj in nsJSObjWrapper::NP_HasMethod!");
   585     return false;
   586   }
   588   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   590   nsCxPusher pusher;
   591   pusher.Push(cx);
   592   JSAutoCompartment ac(cx, npjsobj->mJSObj);
   594   AutoJSExceptionReporter reporter(cx);
   596   JS::Rooted<JS::Value> v(cx);
   597   bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
   599   return ok && !JSVAL_IS_PRIMITIVE(v) &&
   600     ::JS_ObjectIsFunction(cx, JSVAL_TO_OBJECT(v));
   601 }
   603 static bool
   604 doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
   605          uint32_t argCount, bool ctorCall, NPVariant *result)
   606 {
   607   NPP npp = NPPStack::Peek();
   608   JSContext *cx = GetJSContext(npp);
   610   if (!cx) {
   611     return false;
   612   }
   614   if (!npobj || !result) {
   615     ThrowJSException(cx, "Null npobj, or result in doInvoke!");
   617     return false;
   618   }
   620   // Initialize *result
   621   VOID_TO_NPVARIANT(*result);
   623   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   625   nsCxPusher pusher;
   626   pusher.Push(cx);
   627   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   628   JSAutoCompartment ac(cx, jsobj);
   629   JS::Rooted<JS::Value> fv(cx);
   631   AutoJSExceptionReporter reporter(cx);
   633   if (method != NPIdentifier_VOID) {
   634     if (!GetProperty(cx, jsobj, method, &fv) ||
   635         ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
   636       return false;
   637     }
   638   } else {
   639     fv.setObject(*jsobj);
   640   }
   642   // Convert args
   643   JS::AutoValueVector jsargs(cx);
   644   if (!jsargs.reserve(argCount)) {
   645       ::JS_ReportOutOfMemory(cx);
   646       return false;
   647   }
   648   for (uint32_t i = 0; i < argCount; ++i) {
   649     jsargs.infallibleAppend(NPVariantToJSVal(npp, cx, args + i));
   650   }
   652   JS::Rooted<JS::Value> v(cx);
   653   bool ok = false;
   655   if (ctorCall) {
   656     JSObject *newObj =
   657       ::JS_New(cx, jsobj, jsargs);
   659     if (newObj) {
   660       v.setObject(*newObj);
   661       ok = true;
   662     }
   663   } else {
   664     ok = ::JS_CallFunctionValue(cx, jsobj, fv, jsargs, &v);
   665   }
   667   if (ok)
   668     ok = JSValToNPVariant(npp, cx, v, result);
   670   return ok;
   671 }
   673 // static
   674 bool
   675 nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
   676                           const NPVariant *args, uint32_t argCount,
   677                           NPVariant *result)
   678 {
   679   if (method == NPIdentifier_VOID) {
   680     return false;
   681   }
   683   return doInvoke(npobj, method, args, argCount, false, result);
   684 }
   686 // static
   687 bool
   688 nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
   689                                  uint32_t argCount, NPVariant *result)
   690 {
   691   return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false,
   692                   result);
   693 }
   695 // static
   696 bool
   697 nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid)
   698 {
   699   NPP npp = NPPStack::Peek();
   700   JSContext *cx = GetJSContext(npp);
   702   if (!cx) {
   703     return false;
   704   }
   706   if (!npobj) {
   707     ThrowJSException(cx,
   708                      "Null npobj in nsJSObjWrapper::NP_HasProperty!");
   710     return false;
   711   }
   713   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   714   bool found, ok = false;
   716   nsCxPusher pusher;
   717   pusher.Push(cx);
   718   AutoJSExceptionReporter reporter(cx);
   719   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   720   JSAutoCompartment ac(cx, jsobj);
   722   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
   723                "id must be either string or int!\n");
   724   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   725   ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
   726   return ok && found;
   727 }
   729 // static
   730 bool
   731 nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id,
   732                                NPVariant *result)
   733 {
   734   NPP npp = NPPStack::Peek();
   735   JSContext *cx = GetJSContext(npp);
   737   if (!cx) {
   738     return false;
   739   }
   741   if (!npobj) {
   742     ThrowJSException(cx,
   743                      "Null npobj in nsJSObjWrapper::NP_GetProperty!");
   745     return false;
   746   }
   748   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   750   nsCxPusher pusher;
   751   pusher.Push(cx);
   752   AutoJSExceptionReporter reporter(cx);
   753   JSAutoCompartment ac(cx, npjsobj->mJSObj);
   755   JS::Rooted<JS::Value> v(cx);
   756   return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
   757           JSValToNPVariant(npp, cx, v, result));
   758 }
   760 // static
   761 bool
   762 nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier npid,
   763                                const NPVariant *value)
   764 {
   765   NPP npp = NPPStack::Peek();
   766   JSContext *cx = GetJSContext(npp);
   768   if (!cx) {
   769     return false;
   770   }
   772   if (!npobj) {
   773     ThrowJSException(cx,
   774                      "Null npobj in nsJSObjWrapper::NP_SetProperty!");
   776     return false;
   777   }
   779   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   780   bool ok = false;
   782   nsCxPusher pusher;
   783   pusher.Push(cx);
   784   AutoJSExceptionReporter reporter(cx);
   785   JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
   786   JSAutoCompartment ac(cx, jsObj);
   788   JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
   790   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
   791                "id must be either string or int!\n");
   792   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   793   ok = ::JS_SetPropertyById(cx, jsObj, id, v);
   795   return ok;
   796 }
   798 // static
   799 bool
   800 nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
   801 {
   802   NPP npp = NPPStack::Peek();
   803   JSContext *cx = GetJSContext(npp);
   805   if (!cx) {
   806     return false;
   807   }
   809   if (!npobj) {
   810     ThrowJSException(cx,
   811                      "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
   813     return false;
   814   }
   816   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   817   bool ok = false;
   819   nsCxPusher pusher;
   820   pusher.Push(cx);
   821   AutoJSExceptionReporter reporter(cx);
   822   bool deleted = false;
   823   JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
   824   JSAutoCompartment ac(cx, obj);
   826   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
   827                "id must be either string or int!\n");
   828   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   829   ok = ::JS_DeletePropertyById2(cx, obj, id, &deleted);
   830   if (ok && deleted) {
   831     // FIXME: See bug 425823, we shouldn't need to do this, and once
   832     // that bug is fixed we can remove this code.
   834     bool hasProp;
   835     ok = ::JS_HasPropertyById(cx, obj, id, &hasProp);
   837     if (ok && hasProp) {
   838       // The property might have been deleted, but it got
   839       // re-resolved, so no, it's not really deleted.
   841       deleted = false;
   842     }
   843   }
   845   return ok && deleted;
   846 }
   848 //static
   849 bool
   850 nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
   851                              uint32_t *count)
   852 {
   853   NPP npp = NPPStack::Peek();
   854   JSContext *cx = GetJSContext(npp);
   856   *idarray = 0;
   857   *count = 0;
   859   if (!cx) {
   860     return false;
   861   }
   863   if (!npobj) {
   864     ThrowJSException(cx,
   865                      "Null npobj in nsJSObjWrapper::NP_Enumerate!");
   867     return false;
   868   }
   870   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   872   nsCxPusher pusher;
   873   pusher.Push(cx);
   874   AutoJSExceptionReporter reporter(cx);
   875   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   876   JSAutoCompartment ac(cx, jsobj);
   878   JS::AutoIdArray ida(cx, JS_Enumerate(cx, jsobj));
   879   if (!ida) {
   880     return false;
   881   }
   883   *count = ida.length();
   884   *idarray = (NPIdentifier *)PR_Malloc(*count * sizeof(NPIdentifier));
   885   if (!*idarray) {
   886     ThrowJSException(cx, "Memory allocation failed for NPIdentifier!");
   887     return false;
   888   }
   890   for (uint32_t i = 0; i < *count; i++) {
   891     JS::Rooted<JS::Value> v(cx);
   892     if (!JS_IdToValue(cx, ida[i], &v)) {
   893       PR_Free(*idarray);
   894       return false;
   895     }
   897     NPIdentifier id;
   898     if (v.isString()) {
   899       JS::Rooted<JSString*> str(cx, v.toString());
   900       str = JS_InternJSString(cx, str);
   901       if (!str) {
   902         PR_Free(*idarray);
   903         return false;
   904       }
   905       id = StringToNPIdentifier(cx, str);
   906     } else {
   907       NS_ASSERTION(JSVAL_IS_INT(v),
   908                    "The element in ida must be either string or int!\n");
   909       id = IntToNPIdentifier(JSVAL_TO_INT(v));
   910     }
   912     (*idarray)[i] = id;
   913   }
   915   return true;
   916 }
   918 //static
   919 bool
   920 nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
   921                              uint32_t argCount, NPVariant *result)
   922 {
   923   return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
   924 }
   928 /*
   929  * This function is called during minor GCs for each key in the sJSObjWrappers
   930  * table that has been moved.
   931  *
   932  * Note that the wrapper may be dead at this point, and even the table may have
   933  * been finalized if all wrappers have died.
   934  */
   935 static void
   936 JSObjWrapperKeyMarkCallback(JSTracer *trc, JSObject *obj, void *data) {
   937   NPP npp = static_cast<NPP>(data);
   938   MOZ_ASSERT(sJSObjWrappersAccessible);
   939   if (!sJSObjWrappers.initialized())
   940     return;
   942   JSObject *prior = obj;
   943   nsJSObjWrapperKey oldKey(prior, npp);
   944   JSObjWrapperTable::Ptr p = sJSObjWrappers.lookup(oldKey);
   945   if (!p)
   946     return;
   948   JS_CallObjectTracer(trc, &obj, "sJSObjWrappers key object");
   949   nsJSObjWrapperKey newKey(obj, npp);
   950   sJSObjWrappers.rekeyIfMoved(oldKey, newKey);
   951 }
   953 // Look up or create an NPObject that wraps the JSObject obj.
   955 // static
   956 NPObject *
   957 nsJSObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, JS::Handle<JSObject*> obj)
   958 {
   959   if (!npp) {
   960     NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
   962     return nullptr;
   963   }
   965   if (!cx) {
   966     cx = GetJSContext(npp);
   968     if (!cx) {
   969       NS_ERROR("Unable to find a JSContext in nsJSObjWrapper::GetNewOrUsed()!");
   971       return nullptr;
   972     }
   973   }
   975   // No need to enter the right compartment here as we only get the
   976   // class and private from the JSObject, neither of which cares about
   977   // compartments.
   979   const JSClass *clazz = JS_GetClass(obj);
   981   if (clazz == &sNPObjectJSWrapperClass) {
   982     // obj is one of our own, its private data is the NPObject we're
   983     // looking for.
   985     NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
   987     // If the private is null, that means that the object has already been torn
   988     // down, possible because the owning plugin was destroyed (there can be
   989     // multiple plugins, so the fact that it was destroyed does not prevent one
   990     // of its dead JS objects from being passed to another plugin). There's not
   991     // much use in wrapping such a dead object, so we just return null, causing
   992     // us to throw.
   993     if (!npobj)
   994       return nullptr;
   996     if (LookupNPP(npobj) == npp)
   997       return _retainobject(npobj);
   998   }
  1000   if (!sJSObjWrappers.initialized()) {
  1001     // No hash yet (or any more), initialize it.
  1002     if (!sJSObjWrappers.init(16)) {
  1003       NS_ERROR("Error initializing PLDHashTable!");
  1005       return nullptr;
  1007     sJSObjWrappersAccessible = true;
  1009   MOZ_ASSERT(sJSObjWrappersAccessible);
  1011   JSObjWrapperTable::Ptr p = sJSObjWrappers.lookupForAdd(nsJSObjWrapperKey(obj, npp));
  1012   if (p) {
  1013     MOZ_ASSERT(p->value());
  1014     // Found a live nsJSObjWrapper, return it.
  1016     return _retainobject(p->value());
  1019   // No existing nsJSObjWrapper, create one.
  1021   nsJSObjWrapper *wrapper =
  1022     (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
  1024   if (!wrapper) {
  1025     // Out of memory, entry not yet added to table.
  1026     return nullptr;
  1029   // Assign mJSObj, rooting the JSObject. Its lifetime is now tied to that of
  1030   // the NPObject.
  1031   wrapper->mJSObj = obj;
  1033   nsJSObjWrapperKey key(obj, npp);
  1034   if (!sJSObjWrappers.putNew(key, wrapper)) {
  1035     // Out of memory, free the wrapper we created.
  1036     _releaseobject(wrapper);
  1037     return nullptr;
  1040   // Add postbarrier for the hashtable key
  1041   JS_StoreObjectPostBarrierCallback(cx, JSObjWrapperKeyMarkCallback, obj, wrapper->mNpp);
  1043   return wrapper;
  1046 // Climb the prototype chain, unwrapping as necessary until we find an NP object
  1047 // wrapper.
  1048 //
  1049 // Because this function unwraps, its return value must be wrapped for the cx
  1050 // compartment for callers that plan to hold onto the result or do anything
  1051 // substantial with it.
  1052 static JSObject *
  1053 GetNPObjectWrapper(JSContext *cx, JSObject *aObj, bool wrapResult = true)
  1055   JS::Rooted<JSObject*> obj(cx, aObj);
  1056   while (obj && (obj = js::CheckedUnwrap(obj))) {
  1057     if (JS_GetClass(obj) == &sNPObjectJSWrapperClass) {
  1058       if (wrapResult && !JS_WrapObject(cx, &obj)) {
  1059         return nullptr;
  1061       return obj;
  1063     if (!::JS_GetPrototype(cx, obj, &obj)) {
  1064       return nullptr;
  1067   return nullptr;
  1070 static NPObject *
  1071 GetNPObject(JSContext *cx, JSObject *obj)
  1073   obj = GetNPObjectWrapper(cx, obj, /* wrapResult = */ false);
  1074   if (!obj) {
  1075     return nullptr;
  1078   return (NPObject *)::JS_GetPrivate(obj);
  1082 // Does not actually add a property because this is always followed by a
  1083 // SetProperty call.
  1084 static bool
  1085 NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
  1087   NPObject *npobj = GetNPObject(cx, obj);
  1089   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
  1090       !npobj->_class->hasMethod) {
  1091     ThrowJSException(cx, "Bad NPObject as private data!");
  1093     return false;
  1096   if (NPObjectIsOutOfProcessProxy(npobj)) {
  1097     return true;
  1100   PluginDestructionGuard pdg(LookupNPP(npobj));
  1102   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1103   bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
  1104   if (!ReportExceptionIfPending(cx))
  1105     return false;
  1107   if (hasProperty)
  1108     return true;
  1110   // We must permit methods here since JS_DefineUCFunction() will add
  1111   // the function as a property
  1112   bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
  1113   if (!ReportExceptionIfPending(cx))
  1114     return false;
  1116   if (!hasMethod) {
  1117     ThrowJSException(cx, "Trying to add unsupported property on NPObject!");
  1119     return false;
  1122   return true;
  1125 static bool
  1126 NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool *succeeded)
  1128   NPObject *npobj = GetNPObject(cx, obj);
  1130   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
  1131       !npobj->_class->removeProperty) {
  1132     ThrowJSException(cx, "Bad NPObject as private data!");
  1134     return false;
  1137   PluginDestructionGuard pdg(LookupNPP(npobj));
  1139   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1141   if (!NPObjectIsOutOfProcessProxy(npobj)) {
  1142     bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
  1143     if (!ReportExceptionIfPending(cx))
  1144       return false;
  1146     if (!hasProperty) {
  1147       *succeeded = true;
  1148       return true;
  1152   if (!npobj->_class->removeProperty(npobj, identifier))
  1153     *succeeded = false;
  1155   return ReportExceptionIfPending(cx);
  1158 static bool
  1159 NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, bool strict,
  1160                          JS::MutableHandle<JS::Value> vp)
  1162   NPObject *npobj = GetNPObject(cx, obj);
  1164   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
  1165       !npobj->_class->setProperty) {
  1166     ThrowJSException(cx, "Bad NPObject as private data!");
  1168     return false;
  1171   // Find out what plugin (NPP) is the owner of the object we're
  1172   // manipulating, and make it own any JSObject wrappers created here.
  1173   NPP npp = LookupNPP(npobj);
  1175   if (!npp) {
  1176     ThrowJSException(cx, "No NPP found for NPObject!");
  1178     return false;
  1181   PluginDestructionGuard pdg(npp);
  1183   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1185   if (!NPObjectIsOutOfProcessProxy(npobj)) {
  1186     bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
  1187     if (!ReportExceptionIfPending(cx))
  1188       return false;
  1190     if (!hasProperty) {
  1191       ThrowJSException(cx, "Trying to set unsupported property on NPObject!");
  1193       return false;
  1197   NPVariant npv;
  1198   if (!JSValToNPVariant(npp, cx, vp, &npv)) {
  1199     ThrowJSException(cx, "Error converting jsval to NPVariant!");
  1201     return false;
  1204   bool ok = npobj->_class->setProperty(npobj, identifier, &npv);
  1205   _releasevariantvalue(&npv); // Release the variant
  1206   if (!ReportExceptionIfPending(cx))
  1207     return false;
  1209   if (!ok) {
  1210     ThrowJSException(cx, "Error setting property on NPObject!");
  1212     return false;
  1215   return true;
  1218 static bool
  1219 NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
  1221   NPObject *npobj = GetNPObject(cx, obj);
  1223   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
  1224       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
  1225     ThrowJSException(cx, "Bad NPObject as private data!");
  1227     return false;
  1230   // Find out what plugin (NPP) is the owner of the object we're
  1231   // manipulating, and make it own any JSObject wrappers created here.
  1232   NPP npp = LookupNPP(npobj);
  1233   if (!npp) {
  1234     ThrowJSException(cx, "No NPP found for NPObject!");
  1236     return false;
  1239   PluginDestructionGuard pdg(npp);
  1241   bool hasProperty, hasMethod;
  1243   NPVariant npv;
  1244   VOID_TO_NPVARIANT(npv);
  1246   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1248   if (NPObjectIsOutOfProcessProxy(npobj)) {
  1249     PluginScriptableObjectParent* actor =
  1250       static_cast<ParentNPObject*>(npobj)->parent;
  1252     // actor may be null if the plugin crashed.
  1253     if (!actor)
  1254       return false;
  1256     bool success = actor->GetPropertyHelper(identifier, &hasProperty,
  1257                                             &hasMethod, &npv);
  1258     if (!ReportExceptionIfPending(cx)) {
  1259       if (success)
  1260         _releasevariantvalue(&npv);
  1261       return false;
  1264     if (success) {
  1265       // We return NPObject Member class here to support ambiguous members.
  1266       if (hasProperty && hasMethod)
  1267         return CreateNPObjectMember(npp, cx, obj, npobj, id, &npv, vp);
  1269       if (hasProperty) {
  1270         vp.set(NPVariantToJSVal(npp, cx, &npv));
  1271         _releasevariantvalue(&npv);
  1273         if (!ReportExceptionIfPending(cx))
  1274           return false;
  1277     return true;
  1280   hasProperty = npobj->_class->hasProperty(npobj, identifier);
  1281   if (!ReportExceptionIfPending(cx))
  1282     return false;
  1284   hasMethod = npobj->_class->hasMethod(npobj, identifier);
  1285   if (!ReportExceptionIfPending(cx))
  1286     return false;
  1288   // We return NPObject Member class here to support ambiguous members.
  1289   if (hasProperty && hasMethod)
  1290     return CreateNPObjectMember(npp, cx, obj, npobj, id, nullptr, vp);
  1292   if (hasProperty) {
  1293     if (npobj->_class->getProperty(npobj, identifier, &npv))
  1294       vp.set(NPVariantToJSVal(npp, cx, &npv));
  1296     _releasevariantvalue(&npv);
  1298     if (!ReportExceptionIfPending(cx))
  1299       return false;
  1302   return true;
  1305 static bool
  1306 CallNPMethodInternal(JSContext *cx, JS::Handle<JSObject*> obj, unsigned argc,
  1307                      JS::Value *argv, JS::Value *rval, bool ctorCall)
  1309   NPObject *npobj = GetNPObject(cx, obj);
  1311   if (!npobj || !npobj->_class) {
  1312     ThrowJSException(cx, "Bad NPObject as private data!");
  1314     return false;
  1317   // Find out what plugin (NPP) is the owner of the object we're
  1318   // manipulating, and make it own any JSObject wrappers created here.
  1319   NPP npp = LookupNPP(npobj);
  1321   if (!npp) {
  1322     ThrowJSException(cx, "Error finding NPP for NPObject!");
  1324     return false;
  1327   PluginDestructionGuard pdg(npp);
  1329   NPVariant npargs_buf[8];
  1330   NPVariant *npargs = npargs_buf;
  1332   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
  1333     // Our stack buffer isn't large enough to hold all arguments,
  1334     // malloc a buffer.
  1335     npargs = (NPVariant *)PR_Malloc(argc * sizeof(NPVariant));
  1337     if (!npargs) {
  1338       ThrowJSException(cx, "Out of memory!");
  1340       return false;
  1344   // Convert arguments
  1345   uint32_t i;
  1346   for (i = 0; i < argc; ++i) {
  1347     if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
  1348       ThrowJSException(cx, "Error converting jsvals to NPVariants!");
  1350       if (npargs != npargs_buf) {
  1351         PR_Free(npargs);
  1354       return false;
  1358   NPVariant v;
  1359   VOID_TO_NPVARIANT(v);
  1361   JSObject *funobj = JSVAL_TO_OBJECT(argv[-2]);
  1362   bool ok;
  1363   const char *msg = "Error calling method on NPObject!";
  1365   if (ctorCall) {
  1366     // construct a new NPObject based on the NPClass in npobj. Fail if
  1367     // no construct method is available.
  1369     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
  1370         npobj->_class->construct) {
  1371       ok = npobj->_class->construct(npobj, npargs, argc, &v);
  1372     } else {
  1373       ok = false;
  1375       msg = "Attempt to construct object from class with no constructor.";
  1377   } else if (funobj != obj) {
  1378     // A obj.function() style call is made, get the method name from
  1379     // the function object.
  1381     if (npobj->_class->invoke) {
  1382       JSFunction *fun = ::JS_GetObjectFunction(funobj);
  1383       JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
  1384       JSString *name = ::JS_InternJSString(cx, funId);
  1385       NPIdentifier id = StringToNPIdentifier(cx, name);
  1387       ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
  1388     } else {
  1389       ok = false;
  1391       msg = "Attempt to call a method on object with no invoke method.";
  1393   } else {
  1394     if (npobj->_class->invokeDefault) {
  1395       // obj is a callable object that is being called, no method name
  1396       // available then. Invoke the default method.
  1398       ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
  1399     } else {
  1400       ok = false;
  1402       msg = "Attempt to call a default method on object with no "
  1403         "invokeDefault method.";
  1407   // Release arguments.
  1408   for (i = 0; i < argc; ++i) {
  1409     _releasevariantvalue(npargs + i);
  1412   if (npargs != npargs_buf) {
  1413     PR_Free(npargs);
  1416   if (!ok) {
  1417     // ReportExceptionIfPending returns a return value, which is true
  1418     // if no exception was thrown. In that case, throw our own.
  1419     if (ReportExceptionIfPending(cx))
  1420       ThrowJSException(cx, msg);
  1422     return false;
  1425   *rval = NPVariantToJSVal(npp, cx, &v);
  1427   // *rval now owns the value, release our reference.
  1428   _releasevariantvalue(&v);
  1430   return ReportExceptionIfPending(cx);
  1433 static bool
  1434 CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
  1436   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  1437   JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
  1438   if (!obj)
  1439       return false;
  1441   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
  1444 struct NPObjectEnumerateState {
  1445   uint32_t     index;
  1446   uint32_t     length;
  1447   NPIdentifier *value;
  1448 };
  1450 static bool
  1451 NPObjWrapper_newEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JSIterateOp enum_op,
  1452                           JS::Value *statep, jsid *idp)
  1454   NPObject *npobj = GetNPObject(cx, obj);
  1455   NPIdentifier *enum_value;
  1456   uint32_t length;
  1457   NPObjectEnumerateState *state;
  1459   if (!npobj || !npobj->_class) {
  1460     ThrowJSException(cx, "Bad NPObject as private data!");
  1461     return false;
  1464   PluginDestructionGuard pdg(LookupNPP(npobj));
  1466   NS_ASSERTION(statep, "Must have a statep to enumerate!");
  1468   switch(enum_op) {
  1469   case JSENUMERATE_INIT:
  1470   case JSENUMERATE_INIT_ALL:
  1471     state = new NPObjectEnumerateState();
  1472     if (!state) {
  1473       ThrowJSException(cx, "Memory allocation failed for "
  1474                        "NPObjectEnumerateState!");
  1476       return false;
  1479     if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
  1480         !npobj->_class->enumerate) {
  1481       enum_value = 0;
  1482       length = 0;
  1483     } else if (!npobj->_class->enumerate(npobj, &enum_value, &length)) {
  1484       delete state;
  1486       if (ReportExceptionIfPending(cx)) {
  1487         // ReportExceptionIfPending returns a return value, which is true
  1488         // if no exception was thrown. In that case, throw our own.
  1489         ThrowJSException(cx, "Error enumerating properties on scriptable "
  1490                              "plugin object");
  1493       return false;
  1496     state->value = enum_value;
  1497     state->length = length;
  1498     state->index = 0;
  1499     *statep = PRIVATE_TO_JSVAL(state);
  1500     if (idp) {
  1501       *idp = INT_TO_JSID(length);
  1504     break;
  1506   case JSENUMERATE_NEXT:
  1507     state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
  1508     enum_value = state->value;
  1509     length = state->length;
  1510     if (state->index != length) {
  1511       *idp = NPIdentifierToJSId(enum_value[state->index++]);
  1512       return true;
  1515     // FALL THROUGH
  1517   case JSENUMERATE_DESTROY:
  1518     state = (NPObjectEnumerateState *)JSVAL_TO_PRIVATE(*statep);
  1519     if (state->value)
  1520       PR_Free(state->value);
  1521     delete state;
  1522     *statep = JSVAL_NULL;
  1524     break;
  1527   return true;
  1530 static bool
  1531 NPObjWrapper_NewResolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
  1532                         JS::MutableHandle<JSObject*> objp)
  1534   NPObject *npobj = GetNPObject(cx, obj);
  1536   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
  1537       !npobj->_class->hasMethod) {
  1538     ThrowJSException(cx, "Bad NPObject as private data!");
  1540     return false;
  1543   PluginDestructionGuard pdg(LookupNPP(npobj));
  1545   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1547   bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
  1548   if (!ReportExceptionIfPending(cx))
  1549     return false;
  1551   if (hasProperty) {
  1552     NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
  1553                  "id must be either string or int!\n");
  1554     if (!::JS_DefinePropertyById(cx, obj, id, JSVAL_VOID, nullptr,
  1555                                  nullptr, JSPROP_ENUMERATE | JSPROP_SHARED)) {
  1556         return false;
  1559     objp.set(obj);
  1561     return true;
  1564   bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
  1565   if (!ReportExceptionIfPending(cx))
  1566     return false;
  1568   if (hasMethod) {
  1569     NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
  1570                  "id must be either string or int!\n");
  1572     JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0,
  1573                                               JSPROP_ENUMERATE);
  1575     objp.set(obj);
  1577     return fnc != nullptr;
  1580   // no property or method
  1581   return true;
  1584 static bool
  1585 NPObjWrapper_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType hint, JS::MutableHandle<JS::Value> vp)
  1587   MOZ_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
  1589   // Plugins do not simply use JS_ConvertStub, and the default [[DefaultValue]]
  1590   // behavior, because that behavior involves calling toString or valueOf on
  1591   // objects which weren't designed to accommodate this.  Usually this wouldn't
  1592   // be a problem, because the absence of either property, or the presence of
  1593   // either property with a value that isn't callable, will cause that property
  1594   // to simply be ignored.  But there is a problem in one specific case: Java,
  1595   // specifically java.lang.Integer.  The Integer class has static valueOf
  1596   // methods, none of which are nullary, so the JS-reflected method will behave
  1597   // poorly when called with no arguments.  We work around this problem by
  1598   // giving plugins a [[DefaultValue]] which uses only toString and not valueOf.
  1600   JS::Rooted<JS::Value> v(cx, JSVAL_VOID);
  1601   if (!JS_GetProperty(cx, obj, "toString", &v))
  1602     return false;
  1603   if (!JSVAL_IS_PRIMITIVE(v) && JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(v))) {
  1604     if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), vp))
  1605       return false;
  1606     if (JSVAL_IS_PRIMITIVE(vp))
  1607       return true;
  1610   JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CONVERT_TO,
  1611                        JS_GetClass(obj)->name,
  1612                        hint == JSTYPE_VOID
  1613                        ? "primitive type"
  1614                        : hint == JSTYPE_NUMBER
  1615                        ? "number"
  1616                        : "string");
  1617   return false;
  1620 static void
  1621 NPObjWrapper_Finalize(JSFreeOp *fop, JSObject *obj)
  1623   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
  1624   if (npobj) {
  1625     if (sNPObjWrappers.ops) {
  1626       PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_REMOVE);
  1630   if (!sDelayedReleases)
  1631     sDelayedReleases = new nsTArray<NPObject*>;
  1632   sDelayedReleases->AppendElement(npobj);
  1635 static bool
  1636 NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
  1638   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  1639   JS::Rooted<JSObject*> obj(cx, &args.callee());
  1640   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
  1643 static bool
  1644 NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
  1646   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  1647   JS::Rooted<JSObject*> obj(cx, &args.callee());
  1648   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true);
  1651 class NPObjWrapperHashEntry : public PLDHashEntryHdr
  1653 public:
  1654   NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
  1655   JSObject *mJSObj;
  1656   NPP mNpp;
  1657 };
  1660 // An NPObject is going away, make sure we null out the JS object's
  1661 // private data in case this is an NPObject that came from a plugin
  1662 // and it's destroyed prematurely.
  1664 // static
  1665 void
  1666 nsNPObjWrapper::OnDestroy(NPObject *npobj)
  1668   if (!npobj) {
  1669     return;
  1672   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
  1673     // npobj is one of our own, no private data to clean up here.
  1675     return;
  1678   if (!sNPObjWrappers.ops) {
  1679     // No hash yet (or any more), no used wrappers available.
  1681     return;
  1684   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
  1685     (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
  1687   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
  1688     // Found a live NPObject wrapper, null out its JSObjects' private
  1689     // data.
  1691     ::JS_SetPrivate(entry->mJSObj, nullptr);
  1693     // Remove the npobj from the hash now that it went away.
  1694     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
  1696     // The finalize hook will call OnWrapperDestroyed().
  1700 // Look up or create a JSObject that wraps the NPObject npobj.
  1702 // static
  1703 JSObject *
  1704 nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
  1706   if (!npobj) {
  1707     NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
  1709     return nullptr;
  1712   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
  1713     // npobj is one of our own, return its existing JSObject.
  1715     JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper *)npobj)->mJSObj);
  1716     if (!JS_WrapObject(cx, &obj)) {
  1717       return nullptr;
  1719     return obj;
  1722   if (!npp) {
  1723     NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
  1725     return nullptr;
  1728   if (!sNPObjWrappers.ops) {
  1729     // No hash yet (or any more), initialize it.
  1730     PL_DHashTableInit(&sNPObjWrappers, PL_DHashGetStubOps(), nullptr,
  1731                       sizeof(NPObjWrapperHashEntry), 16);
  1734   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
  1735     (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
  1737   if (!entry) {
  1738     // Out of memory
  1739     JS_ReportOutOfMemory(cx);
  1741     return nullptr;
  1744   if (PL_DHASH_ENTRY_IS_BUSY(entry) && entry->mJSObj) {
  1745     // Found a live NPObject wrapper. It may not be in the same compartment
  1746     // as cx, so we need to wrap it before returning it.
  1747     JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
  1748     if (!JS_WrapObject(cx, &obj)) {
  1749       return nullptr;
  1751     return obj;
  1754   entry->mNPObj = npobj;
  1755   entry->mNpp = npp;
  1757   uint32_t generation = sNPObjWrappers.generation;
  1759   // No existing JSObject, create one.
  1761   JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, &sNPObjectJSWrapperClass, JS::NullPtr(),
  1762                                                JS::NullPtr()));
  1764   if (generation != sNPObjWrappers.generation) {
  1765       // Reload entry if the JS_NewObject call caused a GC and reallocated
  1766       // the table (see bug 445229). This is guaranteed to succeed.
  1768       entry = static_cast<NPObjWrapperHashEntry *>
  1769         (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_LOOKUP));
  1770       NS_ASSERTION(entry && PL_DHASH_ENTRY_IS_BUSY(entry),
  1771                    "Hashtable didn't find what we just added?");
  1774   if (!obj) {
  1775     // OOM? Remove the stale entry from the hash.
  1777     PL_DHashTableRawRemove(&sNPObjWrappers, entry);
  1779     return nullptr;
  1782   OnWrapperCreated();
  1784   entry->mJSObj = obj;
  1786   ::JS_SetPrivate(obj, npobj);
  1788   // The new JSObject now holds on to npobj
  1789   _retainobject(npobj);
  1791   return obj;
  1795 // Struct for passing an NPP and a JSContext to
  1796 // NPObjWrapperPluginDestroyedCallback
  1797 struct NppAndCx
  1799   NPP npp;
  1800   JSContext *cx;
  1801 };
  1803 static PLDHashOperator
  1804 NPObjWrapperPluginDestroyedCallback(PLDHashTable *table, PLDHashEntryHdr *hdr,
  1805                                     uint32_t number, void *arg)
  1807   NPObjWrapperHashEntry *entry = (NPObjWrapperHashEntry *)hdr;
  1808   NppAndCx *nppcx = reinterpret_cast<NppAndCx *>(arg);
  1810   if (entry->mNpp == nppcx->npp) {
  1811     // Prevent invalidate() and deallocate() from touching the hash
  1812     // we're enumerating.
  1813     const PLDHashTableOps *ops = table->ops;
  1814     table->ops = nullptr;
  1816     NPObject *npobj = entry->mNPObj;
  1818     if (npobj->_class && npobj->_class->invalidate) {
  1819       npobj->_class->invalidate(npobj);
  1822 #ifdef NS_BUILD_REFCNT_LOGGING
  1824       int32_t refCnt = npobj->referenceCount;
  1825       while (refCnt) {
  1826         --refCnt;
  1827         NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
  1830 #endif
  1832     // Force deallocation of plugin objects since the plugin they came
  1833     // from is being torn down.
  1834     if (npobj->_class && npobj->_class->deallocate) {
  1835       npobj->_class->deallocate(npobj);
  1836     } else {
  1837       PR_Free(npobj);
  1840     ::JS_SetPrivate(entry->mJSObj, nullptr);
  1842     table->ops = ops;
  1844     if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
  1845       OnWrapperDestroyed();
  1848     return PL_DHASH_REMOVE;
  1851   return PL_DHASH_NEXT;
  1854 // static
  1855 void
  1856 nsJSNPRuntime::OnPluginDestroy(NPP npp)
  1858   if (sJSObjWrappersAccessible) {
  1860     // Prevent modification of sJSObjWrappers table if we go reentrant.
  1861     sJSObjWrappersAccessible = false;
  1863     for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
  1864       nsJSObjWrapper *npobj = e.front().value();
  1865       MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
  1866       if (npobj->mNpp == npp) {
  1867         if (npobj->_class && npobj->_class->invalidate) {
  1868           npobj->_class->invalidate(npobj);
  1871         _releaseobject(npobj);
  1873         e.removeFront();
  1877     sJSObjWrappersAccessible = true;
  1880   // Use the safe JSContext here as we're not always able to find the
  1881   // JSContext associated with the NPP any more.
  1882   AutoSafeJSContext cx;
  1883   if (sNPObjWrappers.ops) {
  1884     NppAndCx nppcx = { npp, cx };
  1885     PL_DHashTableEnumerate(&sNPObjWrappers,
  1886                            NPObjWrapperPluginDestroyedCallback, &nppcx);
  1891 // Find the NPP for a NPObject.
  1892 static NPP
  1893 LookupNPP(NPObject *npobj)
  1895   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
  1896     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
  1897     return o->mNpp;
  1900   NPObjWrapperHashEntry *entry = static_cast<NPObjWrapperHashEntry *>
  1901     (PL_DHashTableOperate(&sNPObjWrappers, npobj, PL_DHASH_ADD));
  1903   if (PL_DHASH_ENTRY_IS_FREE(entry)) {
  1904     return nullptr;
  1907   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
  1909   return entry->mNpp;
  1912 static bool
  1913 CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
  1914                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
  1915                      JS::MutableHandle<JS::Value> vp)
  1917   if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
  1918       !npobj->_class->invoke) {
  1919     ThrowJSException(cx, "Bad NPObject");
  1921     return false;
  1924   NPObjectMemberPrivate *memberPrivate =
  1925     (NPObjectMemberPrivate *)PR_Malloc(sizeof(NPObjectMemberPrivate));
  1926   if (!memberPrivate)
  1927     return false;
  1929   // Make sure to clear all members in case something fails here
  1930   // during initialization.
  1931   memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
  1933   JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass, JS::NullPtr(), JS::NullPtr());
  1934   if (!memobj) {
  1935     PR_Free(memberPrivate);
  1936     return false;
  1939   vp.setObject(*memobj);
  1941   ::JS_SetPrivate(memobj, (void *)memberPrivate);
  1943   NPIdentifier identifier = JSIdToNPIdentifier(id);
  1945   JS::Rooted<JS::Value> fieldValue(cx);
  1946   NPVariant npv;
  1948   if (getPropertyResult) {
  1949     // Plugin has already handed us the value we want here.
  1950     npv = *getPropertyResult;
  1952   else {
  1953     VOID_TO_NPVARIANT(npv);
  1955     NPBool hasProperty = npobj->_class->getProperty(npobj, identifier,
  1956                                                     &npv);
  1957     if (!ReportExceptionIfPending(cx) || !hasProperty) {
  1958       return false;
  1962   fieldValue = NPVariantToJSVal(npp, cx, &npv);
  1964   // npobjWrapper is the JSObject through which we make sure we don't
  1965   // outlive the underlying NPObject, so make sure it points to the
  1966   // real JSObject wrapper for the NPObject.
  1967   obj = GetNPObjectWrapper(cx, obj);
  1969   memberPrivate->npobjWrapper = obj;
  1971   memberPrivate->fieldValue = fieldValue;
  1972   memberPrivate->methodName = id;
  1973   memberPrivate->npp = npp;
  1975   return true;
  1978 static bool
  1979 NPObjectMember_Convert(JSContext *cx, JS::Handle<JSObject*> obj, JSType type, JS::MutableHandle<JS::Value> vp)
  1981   NPObjectMemberPrivate *memberPrivate =
  1982     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
  1983                                                      &sNPObjectMemberClass,
  1984                                                      nullptr);
  1985   if (!memberPrivate) {
  1986     NS_ERROR("no Ambiguous Member Private data!");
  1987     return false;
  1990   switch (type) {
  1991   case JSTYPE_VOID:
  1992   case JSTYPE_STRING:
  1993   case JSTYPE_NUMBER:
  1994     vp.set(memberPrivate->fieldValue);
  1995     if (vp.isObject()) {
  1996       JS::Rooted<JSObject*> objVal(cx, &vp.toObject());
  1997       return JS_DefaultValue(cx, objVal, type, vp);
  1999     return true;
  2000   case JSTYPE_BOOLEAN:
  2001   case JSTYPE_OBJECT:
  2002     vp.set(memberPrivate->fieldValue);
  2003     return true;
  2004   case JSTYPE_FUNCTION:
  2005     // Leave this to NPObjectMember_Call.
  2006     return true;
  2007   default:
  2008     NS_ERROR("illegal operation on JSObject prototype object");
  2009     return false;
  2013 static void
  2014 NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj)
  2016   NPObjectMemberPrivate *memberPrivate;
  2018   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
  2019   if (!memberPrivate)
  2020     return;
  2022   PR_Free(memberPrivate);
  2025 static bool
  2026 NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp)
  2028   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  2029   JS::Rooted<JSObject*> memobj(cx, &args.callee());
  2030   NS_ENSURE_TRUE(memobj, false);
  2032   NPObjectMemberPrivate *memberPrivate =
  2033     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
  2034                                                      &sNPObjectMemberClass,
  2035                                                      &args);
  2036   if (!memberPrivate || !memberPrivate->npobjWrapper)
  2037     return false;
  2039   NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
  2040   if (!npobj) {
  2041     ThrowJSException(cx, "Call on invalid member object");
  2043     return false;
  2046   NPVariant npargs_buf[8];
  2047   NPVariant *npargs = npargs_buf;
  2049   if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) {
  2050     // Our stack buffer isn't large enough to hold all arguments,
  2051     // malloc a buffer.
  2052     npargs = (NPVariant *)PR_Malloc(args.length() * sizeof(NPVariant));
  2054     if (!npargs) {
  2055       ThrowJSException(cx, "Out of memory!");
  2057       return false;
  2061   // Convert arguments
  2062   for (uint32_t i = 0; i < args.length(); ++i) {
  2063     if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) {
  2064       ThrowJSException(cx, "Error converting jsvals to NPVariants!");
  2066       if (npargs != npargs_buf) {
  2067         PR_Free(npargs);
  2070       return false;
  2075   NPVariant npv;
  2076   bool ok = npobj->_class->invoke(npobj,
  2077                                   JSIdToNPIdentifier(memberPrivate->methodName),
  2078                                   npargs, args.length(), &npv);
  2080   // Release arguments.
  2081   for (uint32_t i = 0; i < args.length(); ++i) {
  2082     _releasevariantvalue(npargs + i);
  2085   if (npargs != npargs_buf) {
  2086     PR_Free(npargs);
  2089   if (!ok) {
  2090     // ReportExceptionIfPending returns a return value, which is true
  2091     // if no exception was thrown. In that case, throw our own.
  2092     if (ReportExceptionIfPending(cx))
  2093       ThrowJSException(cx, "Error calling method on NPObject!");
  2095     return false;
  2098   args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv));
  2100   // *vp now owns the value, release our reference.
  2101   _releasevariantvalue(&npv);
  2103   return ReportExceptionIfPending(cx);
  2106 static void
  2107 NPObjectMember_Trace(JSTracer *trc, JSObject *obj)
  2109   NPObjectMemberPrivate *memberPrivate =
  2110     (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
  2111   if (!memberPrivate)
  2112     return;
  2114   // Our NPIdentifier is not always interned, so we must root it explicitly.
  2115   JS_CallHeapIdTracer(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName");
  2117   if (!JSVAL_IS_PRIMITIVE(memberPrivate->fieldValue)) {
  2118     JS_CallHeapValueTracer(trc, &memberPrivate->fieldValue,
  2119                            "NPObject Member => fieldValue");
  2122   // There's no strong reference from our private data to the
  2123   // NPObject, so make sure to mark the NPObject wrapper to keep the
  2124   // NPObject alive as long as this NPObjectMember is alive.
  2125   if (memberPrivate->npobjWrapper) {
  2126     JS_CallHeapObjectTracer(trc, &memberPrivate->npobjWrapper,
  2127                             "NPObject Member => npobjWrapper");

mercurial