LCOV - code coverage report
Current view: top level - dom/plugins/base - nsJSNPRuntime.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 986 0.1 %
Date: 2017-07-14 16:53:18 Functions: 0 67 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "base/basictypes.h"
       8             : 
       9             : #include "jsfriendapi.h"
      10             : #include "jswrapper.h"
      11             : 
      12             : #include "nsAutoPtr.h"
      13             : #include "nsIInterfaceRequestorUtils.h"
      14             : #include "nsJSNPRuntime.h"
      15             : #include "nsNPAPIPlugin.h"
      16             : #include "nsNPAPIPluginInstance.h"
      17             : #include "nsIGlobalObject.h"
      18             : #include "nsIScriptGlobalObject.h"
      19             : #include "nsIScriptContext.h"
      20             : #include "nsDOMJSUtils.h"
      21             : #include "nsJSUtils.h"
      22             : #include "nsIDocument.h"
      23             : #include "nsIXPConnect.h"
      24             : #include "xpcpublic.h"
      25             : #include "nsIDOMElement.h"
      26             : #include "nsIContent.h"
      27             : #include "nsPluginInstanceOwner.h"
      28             : #include "nsWrapperCacheInlines.h"
      29             : #include "js/GCHashTable.h"
      30             : #include "js/TracingAPI.h"
      31             : #include "mozilla/HashFunctions.h"
      32             : #include "mozilla/dom/ScriptSettings.h"
      33             : 
      34             : #define NPRUNTIME_JSCLASS_NAME "NPObject JS wrapper class"
      35             : 
      36             : using namespace mozilla::plugins::parent;
      37             : using namespace mozilla;
      38             : 
      39             : #include "mozilla/plugins/PluginScriptableObjectParent.h"
      40             : using mozilla::plugins::PluginScriptableObjectParent;
      41             : using mozilla::plugins::ParentNPObject;
      42             : 
      43             : struct JSObjWrapperHasher
      44             : {
      45             :   typedef nsJSObjWrapperKey Key;
      46             :   typedef Key Lookup;
      47             : 
      48           0 :   static uint32_t hash(const Lookup &l) {
      49           0 :     return js::MovableCellHasher<JS::Heap<JSObject*>>::hash(l.mJSObj) ^
      50           0 :            HashGeneric(l.mNpp);
      51             :   }
      52             : 
      53           0 :   static bool match(const Key& k, const Lookup &l) {
      54           0 :       return js::MovableCellHasher<JS::Heap<JSObject*>>::match(k.mJSObj, l.mJSObj) &&
      55           0 :              k.mNpp == l.mNpp;
      56             :   }
      57             : };
      58             : 
      59             : namespace JS {
      60             : template <>
      61             : struct GCPolicy<nsJSObjWrapper*> {
      62           0 :     static void trace(JSTracer* trc, nsJSObjWrapper** wrapper, const char* name) {
      63           0 :         MOZ_ASSERT(wrapper);
      64           0 :         MOZ_ASSERT(*wrapper);
      65           0 :         (*wrapper)->trace(trc);
      66           0 :     }
      67             : };
      68             : } // namespace JS
      69             : 
      70             : class NPObjWrapperHashEntry : public PLDHashEntryHdr
      71             : {
      72             : public:
      73             :   NPObject *mNPObj; // Must be the first member for the PLDHash stubs to work
      74             :   JS::TenuredHeap<JSObject*> mJSObj;
      75             :   NPP mNpp;
      76             : };
      77             : 
      78             : // Hash of JSObject wrappers that wraps JSObjects as NPObjects. There
      79             : // will be one wrapper per JSObject per plugin instance, i.e. if two
      80             : // plugins access the JSObject x, two wrappers for x will be
      81             : // created. This is needed to be able to properly drop the wrappers
      82             : // when a plugin is torn down in case there's a leak in the plugin (we
      83             : // don't want to leak the world just because a plugin leaks an
      84             : // NPObject).
      85             : typedef JS::GCHashMap<nsJSObjWrapperKey,
      86             :                       nsJSObjWrapper*,
      87             :                       JSObjWrapperHasher,
      88             :                       js::SystemAllocPolicy> JSObjWrapperTable;
      89           3 : static JSObjWrapperTable sJSObjWrappers;
      90             : 
      91             : // Whether it's safe to iterate sJSObjWrappers.  Set to true when sJSObjWrappers
      92             : // has been initialized and is not currently being enumerated.
      93             : static bool sJSObjWrappersAccessible = false;
      94             : 
      95             : // Hash of NPObject wrappers that wrap NPObjects as JSObjects.
      96             : static PLDHashTable* sNPObjWrappers;
      97             : 
      98             : // Global wrapper count. This includes JSObject wrappers *and*
      99             : // NPObject wrappers. When this count goes to zero, there are no more
     100             : // wrappers and we can kill off hash tables etc.
     101             : static int32_t sWrapperCount;
     102             : 
     103             : static bool sCallbackIsRegistered = false;
     104             : 
     105             : static nsTArray<NPObject*>* sDelayedReleases;
     106             : 
     107             : namespace {
     108             : 
     109             : inline bool
     110           0 : NPObjectIsOutOfProcessProxy(NPObject *obj)
     111             : {
     112           0 :   return obj->_class == PluginScriptableObjectParent::GetClass();
     113             : }
     114             : 
     115             : } // namespace
     116             : 
     117             : // Helper class that suppresses any JS exceptions that were thrown while
     118             : // the plugin executed JS, if the nsJSObjWrapper has a destroy pending.
     119             : // Note that this class is the product (vestige?) of a long evolution in how
     120             : // error reporting worked, and hence the mIsDestroyPending check, and hence this
     121             : // class in general, may or may not actually be necessary.
     122             : 
     123             : class MOZ_STACK_CLASS AutoJSExceptionSuppressor
     124             : {
     125             : public:
     126           0 :   AutoJSExceptionSuppressor(dom::AutoEntryScript& aes, nsJSObjWrapper* aWrapper)
     127           0 :     : mAes(aes)
     128           0 :     , mIsDestroyPending(aWrapper->mDestroyPending)
     129             :   {
     130           0 :   }
     131             : 
     132           0 :   ~AutoJSExceptionSuppressor()
     133           0 :   {
     134           0 :     if (mIsDestroyPending) {
     135           0 :       mAes.ClearException();
     136             :     }
     137           0 :   }
     138             : 
     139             : protected:
     140             :   dom::AutoEntryScript& mAes;
     141             :   bool mIsDestroyPending;
     142             : };
     143             : 
     144             : 
     145             : NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
     146             :   {
     147             :     NP_CLASS_STRUCT_VERSION,
     148             :     nsJSObjWrapper::NP_Allocate,
     149             :     nsJSObjWrapper::NP_Deallocate,
     150             :     nsJSObjWrapper::NP_Invalidate,
     151             :     nsJSObjWrapper::NP_HasMethod,
     152             :     nsJSObjWrapper::NP_Invoke,
     153             :     nsJSObjWrapper::NP_InvokeDefault,
     154             :     nsJSObjWrapper::NP_HasProperty,
     155             :     nsJSObjWrapper::NP_GetProperty,
     156             :     nsJSObjWrapper::NP_SetProperty,
     157             :     nsJSObjWrapper::NP_RemoveProperty,
     158             :     nsJSObjWrapper::NP_Enumerate,
     159             :     nsJSObjWrapper::NP_Construct
     160             :   };
     161             : 
     162             : static bool
     163             : NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::Handle<JS::Value> v);
     164             : 
     165             : static bool
     166             : NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
     167             :                          JS::ObjectOpResult &result);
     168             : 
     169             : static bool
     170             : NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
     171             :                          JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result);
     172             : 
     173             : static bool
     174             : NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
     175             : 
     176             : static bool
     177             : NPObjWrapper_NewEnumerate(JSContext *cx, JS::Handle<JSObject*> obj, JS::AutoIdVector &properties,
     178             :                           bool enumerableOnly);
     179             : 
     180             : static bool
     181             : NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
     182             :                      bool *resolvedp);
     183             : 
     184             : static void
     185             : NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj);
     186             : 
     187             : static void
     188             : NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old);
     189             : 
     190             : static bool
     191             : NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp);
     192             : 
     193             : static bool
     194             : NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp);
     195             : 
     196             : static bool
     197             : NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
     198             : 
     199             : static bool
     200             : CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
     201             :                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
     202             :                      JS::MutableHandle<JS::Value> vp);
     203             : 
     204             : const static js::ClassOps sNPObjectJSWrapperClassOps = {
     205             :     NPObjWrapper_AddProperty,
     206             :     NPObjWrapper_DelProperty,
     207             :     NPObjWrapper_GetProperty,
     208             :     NPObjWrapper_SetProperty,
     209             :     nullptr,                    /* enumerate */
     210             :     NPObjWrapper_NewEnumerate,
     211             :     NPObjWrapper_Resolve,
     212             :     nullptr,                    /* mayResolve */
     213             :     NPObjWrapper_Finalize,
     214             :     NPObjWrapper_Call,
     215             :     nullptr,                    /* hasInstance */
     216             :     NPObjWrapper_Construct,
     217             :     nullptr,                    /* trace */
     218             : };
     219             : 
     220             : const static js::ClassExtension sNPObjectJSWrapperClassExtension = {
     221             :     nullptr,                    /* weakmapKeyDelegateOp */
     222             :     NPObjWrapper_ObjectMoved
     223             : };
     224             : 
     225             : const static js::Class sNPObjectJSWrapperClass = {
     226             :     NPRUNTIME_JSCLASS_NAME,
     227             :     JSCLASS_HAS_PRIVATE |
     228             :     JSCLASS_FOREGROUND_FINALIZE,
     229             :     &sNPObjectJSWrapperClassOps,
     230             :     JS_NULL_CLASS_SPEC,
     231             :     &sNPObjectJSWrapperClassExtension,
     232             :     JS_NULL_OBJECT_OPS
     233             : };
     234             : 
     235             : typedef struct NPObjectMemberPrivate {
     236             :     JS::Heap<JSObject *> npobjWrapper;
     237             :     JS::Heap<JS::Value> fieldValue;
     238             :     JS::Heap<jsid> methodName;
     239             :     NPP   npp;
     240             : } NPObjectMemberPrivate;
     241             : 
     242             : static bool
     243             : NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
     244             :                            JS::MutableHandleValue vp);
     245             : 
     246             : static void
     247             : NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj);
     248             : 
     249             : static bool
     250             : NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp);
     251             : 
     252             : static void
     253             : NPObjectMember_Trace(JSTracer *trc, JSObject *obj);
     254             : 
     255             : static bool
     256             : NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp);
     257             : 
     258             : static const JSClassOps sNPObjectMemberClassOps = {
     259             :   nullptr, nullptr, NPObjectMember_GetProperty, nullptr,
     260             :   nullptr, nullptr, nullptr, nullptr,
     261             :   NPObjectMember_Finalize, NPObjectMember_Call,
     262             :   nullptr, nullptr, NPObjectMember_Trace
     263             : };
     264             : 
     265             : static const JSClass sNPObjectMemberClass = {
     266             :   "NPObject Ambiguous Member class",
     267             :   JSCLASS_HAS_PRIVATE |
     268             :   JSCLASS_FOREGROUND_FINALIZE,
     269             :   &sNPObjectMemberClassOps
     270             : };
     271             : 
     272             : static void
     273             : OnWrapperDestroyed();
     274             : 
     275             : static void
     276           0 : TraceJSObjWrappers(JSTracer *trc, void *data)
     277             : {
     278           0 :   if (sJSObjWrappers.initialized()) {
     279           0 :     sJSObjWrappers.trace(trc);
     280             :   }
     281           0 : }
     282             : 
     283             : static void
     284           0 : DelayedReleaseGCCallback(JSGCStatus status)
     285             : {
     286           0 :   if (JSGC_END == status) {
     287             :     // Take ownership of sDelayedReleases and null it out now. The
     288             :     // _releaseobject call below can reenter GC and double-free these objects.
     289           0 :     nsAutoPtr<nsTArray<NPObject*> > delayedReleases(sDelayedReleases);
     290           0 :     sDelayedReleases = nullptr;
     291             : 
     292           0 :     if (delayedReleases) {
     293           0 :       for (uint32_t i = 0; i < delayedReleases->Length(); ++i) {
     294           0 :         NPObject* obj = (*delayedReleases)[i];
     295           0 :         if (obj)
     296           0 :           _releaseobject(obj);
     297           0 :         OnWrapperDestroyed();
     298             :       }
     299             :     }
     300             :   }
     301           0 : }
     302             : 
     303             : static bool
     304           0 : RegisterGCCallbacks()
     305             : {
     306           0 :   if (sCallbackIsRegistered) {
     307           0 :     return true;
     308             :   }
     309             : 
     310             :   // Register a callback to trace wrapped JSObjects.
     311           0 :   JSContext* cx = dom::danger::GetJSContext();
     312           0 :   if (!JS_AddExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr)) {
     313           0 :     return false;
     314             :   }
     315             : 
     316             :   // Register our GC callback to perform delayed destruction of finalized
     317             :   // NPObjects.
     318           0 :   xpc::AddGCCallback(DelayedReleaseGCCallback);
     319             : 
     320           0 :   sCallbackIsRegistered = true;
     321             : 
     322           0 :   return true;
     323             : }
     324             : 
     325             : static void
     326           0 : UnregisterGCCallbacks()
     327             : {
     328           0 :   MOZ_ASSERT(sCallbackIsRegistered);
     329             : 
     330             :   // Remove tracing callback.
     331           0 :   JSContext* cx = dom::danger::GetJSContext();
     332           0 :   JS_RemoveExtraGCRootsTracer(cx, TraceJSObjWrappers, nullptr);
     333             : 
     334             :   // Remove delayed destruction callback.
     335           0 :   if (sCallbackIsRegistered) {
     336           0 :     xpc::RemoveGCCallback(DelayedReleaseGCCallback);
     337           0 :     sCallbackIsRegistered = false;
     338             :   }
     339           0 : }
     340             : 
     341             : static bool
     342           0 : CreateJSObjWrapperTable()
     343             : {
     344           0 :   MOZ_ASSERT(!sJSObjWrappersAccessible);
     345           0 :   MOZ_ASSERT(!sJSObjWrappers.initialized());
     346             : 
     347           0 :   if (!RegisterGCCallbacks()) {
     348           0 :     return false;
     349             :   }
     350             : 
     351           0 :   if (!sJSObjWrappers.init(16)) {
     352           0 :     NS_ERROR("Error initializing PLDHashTable sJSObjWrappers!");
     353           0 :     return false;
     354             :   }
     355             : 
     356           0 :   sJSObjWrappersAccessible = true;
     357           0 :   return true;
     358             : }
     359             : 
     360             : static void
     361           0 : DestroyJSObjWrapperTable()
     362             : {
     363           0 :   MOZ_ASSERT(sJSObjWrappersAccessible);
     364           0 :   MOZ_ASSERT(sJSObjWrappers.initialized());
     365           0 :   MOZ_ASSERT(sJSObjWrappers.count() == 0);
     366             : 
     367             :   // No more wrappers, and our hash was initialized. Finish the
     368             :   // hash to prevent leaking it.
     369           0 :   sJSObjWrappers.finish();
     370           0 :   sJSObjWrappersAccessible = false;
     371           0 : }
     372             : 
     373             : static bool
     374           0 : CreateNPObjWrapperTable()
     375             : {
     376           0 :   MOZ_ASSERT(!sNPObjWrappers);
     377             : 
     378           0 :   if (!RegisterGCCallbacks()) {
     379           0 :     return false;
     380             :   }
     381             : 
     382           0 :   sNPObjWrappers =
     383           0 :     new PLDHashTable(PLDHashTable::StubOps(), sizeof(NPObjWrapperHashEntry));
     384           0 :   return true;
     385             : }
     386             : 
     387             : static void
     388           0 : DestroyNPObjWrapperTable()
     389             : {
     390           0 :   MOZ_ASSERT(sNPObjWrappers->EntryCount() == 0);
     391             : 
     392           0 :   delete sNPObjWrappers;
     393           0 :   sNPObjWrappers = nullptr;
     394           0 : }
     395             : 
     396             : static void
     397           0 : OnWrapperCreated()
     398             : {
     399           0 :   ++sWrapperCount;
     400           0 : }
     401             : 
     402             : static void
     403           0 : OnWrapperDestroyed()
     404             : {
     405           0 :   NS_ASSERTION(sWrapperCount, "Whaaa, unbalanced created/destroyed calls!");
     406             : 
     407           0 :   if (--sWrapperCount == 0) {
     408           0 :     if (sJSObjWrappersAccessible) {
     409           0 :       DestroyJSObjWrapperTable();
     410             :     }
     411             : 
     412           0 :     if (sNPObjWrappers) {
     413             :       // No more wrappers, and our hash was initialized. Finish the
     414             :       // hash to prevent leaking it.
     415           0 :       DestroyNPObjWrapperTable();
     416             :     }
     417             : 
     418           0 :     UnregisterGCCallbacks();
     419             :   }
     420           0 : }
     421             : 
     422             : namespace mozilla {
     423             : namespace plugins {
     424             : namespace parent {
     425             : 
     426             : static nsIGlobalObject*
     427           0 : GetGlobalObject(NPP npp)
     428             : {
     429           0 :   NS_ENSURE_TRUE(npp, nullptr);
     430             : 
     431           0 :   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)npp->ndata;
     432           0 :   NS_ENSURE_TRUE(inst, nullptr);
     433             : 
     434           0 :   RefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
     435           0 :   NS_ENSURE_TRUE(owner, nullptr);
     436             : 
     437           0 :   nsCOMPtr<nsIDocument> doc;
     438           0 :   owner->GetDocument(getter_AddRefs(doc));
     439           0 :   NS_ENSURE_TRUE(doc, nullptr);
     440             : 
     441           0 :   return doc->GetScopeObject();
     442             : }
     443             : 
     444             : } // namespace parent
     445             : } // namespace plugins
     446             : } // namespace mozilla
     447             : 
     448             : static NPP
     449             : LookupNPP(NPObject *npobj);
     450             : 
     451             : 
     452             : static JS::Value
     453           0 : NPVariantToJSVal(NPP npp, JSContext *cx, const NPVariant *variant)
     454             : {
     455           0 :   switch (variant->type) {
     456             :   case NPVariantType_Void :
     457           0 :     return JS::UndefinedValue();
     458             :   case NPVariantType_Null :
     459           0 :     return JS::NullValue();
     460             :   case NPVariantType_Bool :
     461           0 :     return JS::BooleanValue(NPVARIANT_TO_BOOLEAN(*variant));
     462             :   case NPVariantType_Int32 :
     463             :     {
     464             :       // Don't use INT_TO_JSVAL directly to prevent bugs when dealing
     465             :       // with ints larger than what fits in a integer JS::Value.
     466           0 :       return ::JS_NumberValue(NPVARIANT_TO_INT32(*variant));
     467             :     }
     468             :   case NPVariantType_Double :
     469             :     {
     470           0 :       return ::JS_NumberValue(NPVARIANT_TO_DOUBLE(*variant));
     471             :     }
     472             :   case NPVariantType_String :
     473             :     {
     474           0 :       const NPString *s = &NPVARIANT_TO_STRING(*variant);
     475           0 :       NS_ConvertUTF8toUTF16 utf16String(s->UTF8Characters, s->UTF8Length);
     476             : 
     477             :       JSString *str =
     478           0 :         ::JS_NewUCStringCopyN(cx, utf16String.get(), utf16String.Length());
     479             : 
     480           0 :       if (str) {
     481           0 :         return JS::StringValue(str);
     482             :       }
     483             : 
     484           0 :       break;
     485             :     }
     486             :   case NPVariantType_Object:
     487             :     {
     488           0 :       if (npp) {
     489             :         JSObject *obj =
     490           0 :           nsNPObjWrapper::GetNewOrUsed(npp, cx, NPVARIANT_TO_OBJECT(*variant));
     491             : 
     492           0 :         if (obj) {
     493           0 :           return JS::ObjectValue(*obj);
     494             :         }
     495             :       }
     496             : 
     497           0 :       NS_ERROR("Error wrapping NPObject!");
     498             : 
     499           0 :       break;
     500             :     }
     501             :   default:
     502           0 :     NS_ERROR("Unknown NPVariant type!");
     503             :   }
     504             : 
     505           0 :   NS_ERROR("Unable to convert NPVariant to jsval!");
     506             : 
     507           0 :   return JS::UndefinedValue();
     508             : }
     509             : 
     510             : bool
     511           0 : JSValToNPVariant(NPP npp, JSContext *cx, const JS::Value& val, NPVariant *variant)
     512             : {
     513           0 :   NS_ASSERTION(npp, "Must have an NPP to wrap a jsval!");
     514             : 
     515           0 :   if (val.isPrimitive()) {
     516           0 :     if (val.isUndefined()) {
     517           0 :       VOID_TO_NPVARIANT(*variant);
     518           0 :     } else if (val.isNull()) {
     519           0 :       NULL_TO_NPVARIANT(*variant);
     520           0 :     } else if (val.isBoolean()) {
     521           0 :       BOOLEAN_TO_NPVARIANT(val.toBoolean(), *variant);
     522           0 :     } else if (val.isInt32()) {
     523           0 :       INT32_TO_NPVARIANT(val.toInt32(), *variant);
     524           0 :     } else if (val.isDouble()) {
     525           0 :       double d = val.toDouble();
     526             :       int i;
     527           0 :       if (JS_DoubleIsInt32(d, &i)) {
     528           0 :         INT32_TO_NPVARIANT(i, *variant);
     529             :       } else {
     530           0 :         DOUBLE_TO_NPVARIANT(d, *variant);
     531             :       }
     532           0 :     } else if (val.isString()) {
     533           0 :       JSString *jsstr = val.toString();
     534             : 
     535           0 :       nsAutoJSString str;
     536           0 :       if (!str.init(cx, jsstr)) {
     537           0 :         return false;
     538             :       }
     539             : 
     540             :       uint32_t len;
     541           0 :       char *p = ToNewUTF8String(str, &len);
     542             : 
     543           0 :       if (!p) {
     544           0 :         return false;
     545             :       }
     546             : 
     547           0 :       STRINGN_TO_NPVARIANT(p, len, *variant);
     548             :     } else {
     549           0 :       NS_ERROR("Unknown primitive type!");
     550             : 
     551           0 :       return false;
     552             :     }
     553             : 
     554           0 :     return true;
     555             :   }
     556             : 
     557             :   // The reflected plugin object may be in another compartment if the plugin
     558             :   // element has since been adopted into a new document. We don't bother
     559             :   // transplanting the plugin objects, and just do a unwrap with security
     560             :   // checks if we encounter one of them as an argument. If the unwrap fails,
     561             :   // we run with the original wrapped object, since sometimes there are
     562             :   // legitimate cases where a security wrapper ends up here (for example,
     563             :   // Location objects, which are _always_ behind security wrappers).
     564           0 :   JS::Rooted<JSObject*> obj(cx, val.toObjectOrNull());
     565           0 :   obj = js::CheckedUnwrap(obj);
     566           0 :   if (!obj) {
     567           0 :     obj = val.toObjectOrNull();
     568             :   }
     569             : 
     570           0 :   NPObject* npobj = nsJSObjWrapper::GetNewOrUsed(npp, obj);
     571           0 :   if (!npobj) {
     572           0 :     return false;
     573             :   }
     574             : 
     575             :   // Pass over ownership of npobj to *variant
     576           0 :   OBJECT_TO_NPVARIANT(npobj, *variant);
     577             : 
     578           0 :   return true;
     579             : }
     580             : 
     581             : static void
     582           0 : ThrowJSExceptionASCII(JSContext *cx, const char *message)
     583             : {
     584           0 :   const char *ex = PeekException();
     585             : 
     586           0 :   if (ex) {
     587           0 :     nsAutoString ucex;
     588             : 
     589           0 :     if (message) {
     590           0 :       AppendASCIItoUTF16(message, ucex);
     591             : 
     592           0 :       AppendASCIItoUTF16(" [plugin exception: ", ucex);
     593             :     }
     594             : 
     595           0 :     AppendUTF8toUTF16(ex, ucex);
     596             : 
     597           0 :     if (message) {
     598           0 :       AppendASCIItoUTF16("].", ucex);
     599             :     }
     600             : 
     601           0 :     JSString *str = ::JS_NewUCStringCopyN(cx, ucex.get(), ucex.Length());
     602             : 
     603           0 :     if (str) {
     604           0 :       JS::Rooted<JS::Value> exn(cx, JS::StringValue(str));
     605           0 :       ::JS_SetPendingException(cx, exn);
     606             :     }
     607             : 
     608           0 :     PopException();
     609             :   } else {
     610           0 :     ::JS_ReportErrorASCII(cx, "%s", message);
     611             :   }
     612           0 : }
     613             : 
     614             : static bool
     615           0 : ReportExceptionIfPending(JSContext *cx)
     616             : {
     617           0 :   const char *ex = PeekException();
     618             : 
     619           0 :   if (!ex) {
     620           0 :     return true;
     621             :   }
     622             : 
     623           0 :   ThrowJSExceptionASCII(cx, nullptr);
     624             : 
     625           0 :   return false;
     626             : }
     627             : 
     628           0 : nsJSObjWrapper::nsJSObjWrapper(NPP npp)
     629           0 :   : mJSObj(nullptr), mNpp(npp), mDestroyPending(false)
     630             : {
     631           0 :   MOZ_COUNT_CTOR(nsJSObjWrapper);
     632           0 :   OnWrapperCreated();
     633           0 : }
     634             : 
     635           0 : nsJSObjWrapper::~nsJSObjWrapper()
     636             : {
     637           0 :   MOZ_COUNT_DTOR(nsJSObjWrapper);
     638             : 
     639             :   // Invalidate first, since it relies on sJSObjWrappers.
     640           0 :   NP_Invalidate(this);
     641             : 
     642           0 :   OnWrapperDestroyed();
     643           0 : }
     644             : 
     645             : // static
     646             : NPObject *
     647           0 : nsJSObjWrapper::NP_Allocate(NPP npp, NPClass *aClass)
     648             : {
     649           0 :   NS_ASSERTION(aClass == &sJSObjWrapperNPClass,
     650             :                "Huh, wrong class passed to NP_Allocate()!!!");
     651             : 
     652           0 :   return new nsJSObjWrapper(npp);
     653             : }
     654             : 
     655             : // static
     656             : void
     657           0 : nsJSObjWrapper::NP_Deallocate(NPObject *npobj)
     658             : {
     659             :   // nsJSObjWrapper::~nsJSObjWrapper() will call NP_Invalidate().
     660           0 :   delete (nsJSObjWrapper *)npobj;
     661           0 : }
     662             : 
     663             : // static
     664             : void
     665           0 : nsJSObjWrapper::NP_Invalidate(NPObject *npobj)
     666             : {
     667           0 :   nsJSObjWrapper *jsnpobj = (nsJSObjWrapper *)npobj;
     668             : 
     669           0 :   if (jsnpobj && jsnpobj->mJSObj) {
     670             : 
     671           0 :     if (sJSObjWrappersAccessible) {
     672             :       // Remove the wrapper from the hash
     673           0 :       nsJSObjWrapperKey key(jsnpobj->mJSObj, jsnpobj->mNpp);
     674           0 :       JSObjWrapperTable::Ptr ptr = sJSObjWrappers.lookup(key);
     675           0 :       MOZ_ASSERT(ptr.found());
     676           0 :       sJSObjWrappers.remove(ptr);
     677             :     }
     678             : 
     679             :     // Forget our reference to the JSObject.
     680           0 :     jsnpobj->mJSObj = nullptr;
     681             :   }
     682           0 : }
     683             : 
     684             : static bool
     685           0 : GetProperty(JSContext *cx, JSObject *objArg, NPIdentifier npid, JS::MutableHandle<JS::Value> rval)
     686             : {
     687           0 :   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
     688             :                "id must be either string or int!\n");
     689           0 :   JS::Rooted<JSObject *> obj(cx, objArg);
     690           0 :   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
     691           0 :   return ::JS_GetPropertyById(cx, obj, id, rval);
     692             : }
     693             : 
     694             : static void
     695           0 : MarkCrossZoneNPIdentifier(JSContext* cx, NPIdentifier npid)
     696             : {
     697           0 :   JS_MarkCrossZoneId(cx, NPIdentifierToJSId(npid));
     698           0 : }
     699             : 
     700             : // static
     701             : bool
     702           0 : nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
     703             : {
     704           0 :   NPP npp = NPPStack::Peek();
     705           0 :   nsIGlobalObject* globalObject = GetGlobalObject(npp);
     706           0 :   if (NS_WARN_IF(!globalObject)) {
     707           0 :     return false;
     708             :   }
     709             : 
     710           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI HasMethod");
     711           0 :   JSContext *cx = aes.cx();
     712             : 
     713           0 :   if (!npobj) {
     714             :     ThrowJSExceptionASCII(cx,
     715           0 :                           "Null npobj in nsJSObjWrapper::NP_HasMethod!");
     716             : 
     717           0 :     return false;
     718             :   }
     719             : 
     720           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     721             : 
     722           0 :   JSAutoCompartment ac(cx, npjsobj->mJSObj);
     723           0 :   MarkCrossZoneNPIdentifier(cx, id);
     724             : 
     725           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     726             : 
     727           0 :   JS::Rooted<JS::Value> v(cx);
     728           0 :   bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
     729             : 
     730           0 :   return ok && !v.isPrimitive() &&
     731           0 :     ::JS_ObjectIsFunction(cx, v.toObjectOrNull());
     732             : }
     733             : 
     734             : static bool
     735           0 : doInvoke(NPObject *npobj, NPIdentifier method, const NPVariant *args,
     736             :          uint32_t argCount, bool ctorCall, NPVariant *result)
     737             : {
     738           0 :   NPP npp = NPPStack::Peek();
     739             : 
     740           0 :   nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
     741           0 :   if (NS_WARN_IF(!globalObject)) {
     742           0 :     return false;
     743             :   }
     744             : 
     745             :   // We're about to run script via JS_CallFunctionValue, so we need an
     746             :   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
     747           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI doInvoke");
     748           0 :   JSContext *cx = aes.cx();
     749             : 
     750           0 :   if (!npobj || !result) {
     751           0 :     ThrowJSExceptionASCII(cx, "Null npobj, or result in doInvoke!");
     752             : 
     753           0 :     return false;
     754             :   }
     755             : 
     756             :   // Initialize *result
     757           0 :   VOID_TO_NPVARIANT(*result);
     758             : 
     759           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     760             : 
     761           0 :   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
     762           0 :   JSAutoCompartment ac(cx, jsobj);
     763           0 :   MarkCrossZoneNPIdentifier(cx, method);
     764           0 :   JS::Rooted<JS::Value> fv(cx);
     765             : 
     766           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     767             : 
     768           0 :   if (method != NPIdentifier_VOID) {
     769           0 :     if (!GetProperty(cx, jsobj, method, &fv) ||
     770           0 :         ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
     771           0 :       return false;
     772             :     }
     773             :   } else {
     774           0 :     fv.setObject(*jsobj);
     775             :   }
     776             : 
     777             :   // Convert args
     778           0 :   JS::AutoValueVector jsargs(cx);
     779           0 :   if (!jsargs.reserve(argCount)) {
     780           0 :       ::JS_ReportOutOfMemory(cx);
     781           0 :       return false;
     782             :   }
     783           0 :   for (uint32_t i = 0; i < argCount; ++i) {
     784           0 :     jsargs.infallibleAppend(NPVariantToJSVal(npp, cx, args + i));
     785             :   }
     786             : 
     787           0 :   JS::Rooted<JS::Value> v(cx);
     788           0 :   bool ok = false;
     789             : 
     790           0 :   if (ctorCall) {
     791             :     JSObject *newObj =
     792           0 :       ::JS_New(cx, jsobj, jsargs);
     793             : 
     794           0 :     if (newObj) {
     795           0 :       v.setObject(*newObj);
     796           0 :       ok = true;
     797             :     }
     798             :   } else {
     799           0 :     ok = ::JS_CallFunctionValue(cx, jsobj, fv, jsargs, &v);
     800             :   }
     801             : 
     802           0 :   if (ok)
     803           0 :     ok = JSValToNPVariant(npp, cx, v, result);
     804             : 
     805           0 :   return ok;
     806             : }
     807             : 
     808             : // static
     809             : bool
     810           0 : nsJSObjWrapper::NP_Invoke(NPObject *npobj, NPIdentifier method,
     811             :                           const NPVariant *args, uint32_t argCount,
     812             :                           NPVariant *result)
     813             : {
     814           0 :   if (method == NPIdentifier_VOID) {
     815           0 :     return false;
     816             :   }
     817             : 
     818           0 :   return doInvoke(npobj, method, args, argCount, false, result);
     819             : }
     820             : 
     821             : // static
     822             : bool
     823           0 : nsJSObjWrapper::NP_InvokeDefault(NPObject *npobj, const NPVariant *args,
     824             :                                  uint32_t argCount, NPVariant *result)
     825             : {
     826           0 :   return doInvoke(npobj, NPIdentifier_VOID, args, argCount, false,
     827           0 :                   result);
     828             : }
     829             : 
     830             : // static
     831             : bool
     832           0 : nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid)
     833             : {
     834           0 :   NPP npp = NPPStack::Peek();
     835           0 :   nsIGlobalObject* globalObject = GetGlobalObject(npp);
     836           0 :   if (NS_WARN_IF(!globalObject)) {
     837           0 :     return false;
     838             :   }
     839             : 
     840           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI HasProperty");
     841           0 :   JSContext *cx = aes.cx();
     842             : 
     843           0 :   if (!npobj) {
     844             :     ThrowJSExceptionASCII(cx,
     845           0 :                           "Null npobj in nsJSObjWrapper::NP_HasProperty!");
     846             : 
     847           0 :     return false;
     848             :   }
     849             : 
     850           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     851           0 :   bool found, ok = false;
     852             : 
     853           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     854           0 :   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
     855           0 :   JSAutoCompartment ac(cx, jsobj);
     856           0 :   MarkCrossZoneNPIdentifier(cx, npid);
     857             : 
     858           0 :   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
     859             :                "id must be either string or int!\n");
     860           0 :   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
     861           0 :   ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
     862           0 :   return ok && found;
     863             : }
     864             : 
     865             : // static
     866             : bool
     867           0 : nsJSObjWrapper::NP_GetProperty(NPObject *npobj, NPIdentifier id,
     868             :                                NPVariant *result)
     869             : {
     870           0 :   NPP npp = NPPStack::Peek();
     871             : 
     872           0 :   nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
     873           0 :   if (NS_WARN_IF(!globalObject)) {
     874           0 :     return false;
     875             :   }
     876             : 
     877             :   // We're about to run script via JS_CallFunctionValue, so we need an
     878             :   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
     879           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI get");
     880           0 :   JSContext *cx = aes.cx();
     881             : 
     882           0 :   if (!npobj) {
     883             :     ThrowJSExceptionASCII(cx,
     884           0 :                           "Null npobj in nsJSObjWrapper::NP_GetProperty!");
     885             : 
     886           0 :     return false;
     887             :   }
     888             : 
     889           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     890             : 
     891           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     892           0 :   JSAutoCompartment ac(cx, npjsobj->mJSObj);
     893           0 :   MarkCrossZoneNPIdentifier(cx, id);
     894             : 
     895           0 :   JS::Rooted<JS::Value> v(cx);
     896           0 :   return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
     897           0 :           JSValToNPVariant(npp, cx, v, result));
     898             : }
     899             : 
     900             : // static
     901             : bool
     902           0 : nsJSObjWrapper::NP_SetProperty(NPObject *npobj, NPIdentifier npid,
     903             :                                const NPVariant *value)
     904             : {
     905           0 :   NPP npp = NPPStack::Peek();
     906             : 
     907           0 :   nsCOMPtr<nsIGlobalObject> globalObject = GetGlobalObject(npp);
     908           0 :   if (NS_WARN_IF(!globalObject)) {
     909           0 :     return false;
     910             :   }
     911             : 
     912             :   // We're about to run script via JS_CallFunctionValue, so we need an
     913             :   // AutoEntryScript. NPAPI plugins are Gecko-specific and not in any spec.
     914           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI set");
     915           0 :   JSContext *cx = aes.cx();
     916             : 
     917           0 :   if (!npobj) {
     918             :     ThrowJSExceptionASCII(cx,
     919           0 :                           "Null npobj in nsJSObjWrapper::NP_SetProperty!");
     920             : 
     921           0 :     return false;
     922             :   }
     923             : 
     924           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     925           0 :   bool ok = false;
     926             : 
     927           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     928           0 :   JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
     929           0 :   JSAutoCompartment ac(cx, jsObj);
     930           0 :   MarkCrossZoneNPIdentifier(cx, npid);
     931             : 
     932           0 :   JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
     933             : 
     934           0 :   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
     935             :                "id must be either string or int!\n");
     936           0 :   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
     937           0 :   ok = ::JS_SetPropertyById(cx, jsObj, id, v);
     938             : 
     939           0 :   return ok;
     940             : }
     941             : 
     942             : // static
     943             : bool
     944           0 : nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
     945             : {
     946           0 :   NPP npp = NPPStack::Peek();
     947           0 :   nsIGlobalObject* globalObject = GetGlobalObject(npp);
     948           0 :   if (NS_WARN_IF(!globalObject)) {
     949           0 :     return false;
     950             :   }
     951             : 
     952           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI RemoveProperty");
     953           0 :   JSContext *cx = aes.cx();
     954             : 
     955           0 :   if (!npobj) {
     956             :     ThrowJSExceptionASCII(cx,
     957           0 :                           "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
     958             : 
     959           0 :     return false;
     960             :   }
     961             : 
     962           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
     963             : 
     964           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
     965           0 :   JS::ObjectOpResult result;
     966           0 :   JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
     967           0 :   JSAutoCompartment ac(cx, obj);
     968           0 :   MarkCrossZoneNPIdentifier(cx, npid);
     969             : 
     970           0 :   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
     971             :                "id must be either string or int!\n");
     972           0 :   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
     973           0 :   if (!::JS_DeletePropertyById(cx, obj, id, result))
     974           0 :     return false;
     975             : 
     976           0 :   if (result) {
     977             :     // FIXME: See bug 425823, we shouldn't need to do this, and once
     978             :     // that bug is fixed we can remove this code.
     979             :     bool hasProp;
     980           0 :     if (!::JS_HasPropertyById(cx, obj, id, &hasProp))
     981           0 :       return false;
     982           0 :     if (!hasProp)
     983           0 :       return true;
     984             : 
     985             :     // The property might have been deleted, but it got
     986             :     // re-resolved, so no, it's not really deleted.
     987           0 :     result.failCantDelete();
     988             :   }
     989             : 
     990           0 :   return result.reportError(cx, obj, id);
     991             : }
     992             : 
     993             : //static
     994             : bool
     995           0 : nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
     996             :                              uint32_t *count)
     997             : {
     998           0 :   NPP npp = NPPStack::Peek();
     999           0 :   nsIGlobalObject* globalObject = GetGlobalObject(npp);
    1000           0 :   if (NS_WARN_IF(!globalObject)) {
    1001           0 :     return false;
    1002             :   }
    1003             : 
    1004           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI Enumerate");
    1005           0 :   JSContext *cx = aes.cx();
    1006             : 
    1007           0 :   *idarray = 0;
    1008           0 :   *count = 0;
    1009             : 
    1010           0 :   if (!npobj) {
    1011             :     ThrowJSExceptionASCII(cx,
    1012           0 :                           "Null npobj in nsJSObjWrapper::NP_Enumerate!");
    1013             : 
    1014           0 :     return false;
    1015             :   }
    1016             : 
    1017           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
    1018             : 
    1019           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
    1020           0 :   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
    1021           0 :   JSAutoCompartment ac(cx, jsobj);
    1022             : 
    1023           0 :   JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
    1024           0 :   if (!JS_Enumerate(cx, jsobj, &ida)) {
    1025           0 :     return false;
    1026             :   }
    1027             : 
    1028           0 :   *count = ida.length();
    1029           0 :   *idarray = (NPIdentifier*) malloc(*count * sizeof(NPIdentifier));
    1030           0 :   if (!*idarray) {
    1031           0 :     ThrowJSExceptionASCII(cx, "Memory allocation failed for NPIdentifier!");
    1032           0 :     return false;
    1033             :   }
    1034             : 
    1035           0 :   for (uint32_t i = 0; i < *count; i++) {
    1036           0 :     JS::Rooted<JS::Value> v(cx);
    1037           0 :     if (!JS_IdToValue(cx, ida[i], &v)) {
    1038           0 :       free(*idarray);
    1039           0 :       return false;
    1040             :     }
    1041             : 
    1042             :     NPIdentifier id;
    1043           0 :     if (v.isString()) {
    1044           0 :       JS::Rooted<JSString*> str(cx, v.toString());
    1045           0 :       str = JS_AtomizeAndPinJSString(cx, str);
    1046           0 :       if (!str) {
    1047           0 :         free(*idarray);
    1048           0 :         return false;
    1049             :       }
    1050           0 :       id = StringToNPIdentifier(cx, str);
    1051             :     } else {
    1052           0 :       NS_ASSERTION(v.isInt32(),
    1053             :                    "The element in ida must be either string or int!\n");
    1054           0 :       id = IntToNPIdentifier(v.toInt32());
    1055             :     }
    1056             : 
    1057           0 :     (*idarray)[i] = id;
    1058             :   }
    1059             : 
    1060           0 :   return true;
    1061             : }
    1062             : 
    1063             : //static
    1064             : bool
    1065           0 : nsJSObjWrapper::NP_Construct(NPObject *npobj, const NPVariant *args,
    1066             :                              uint32_t argCount, NPVariant *result)
    1067             : {
    1068           0 :   return doInvoke(npobj, NPIdentifier_VOID, args, argCount, true, result);
    1069             : }
    1070             : 
    1071             : // Look up or create an NPObject that wraps the JSObject obj.
    1072             : 
    1073             : // static
    1074             : NPObject *
    1075           0 : nsJSObjWrapper::GetNewOrUsed(NPP npp, JS::Handle<JSObject*> obj)
    1076             : {
    1077           0 :   if (!npp) {
    1078           0 :     NS_ERROR("Null NPP passed to nsJSObjWrapper::GetNewOrUsed()!");
    1079             : 
    1080           0 :     return nullptr;
    1081             :   }
    1082             : 
    1083             :   // No need to enter the right compartment here as we only get the
    1084             :   // class and private from the JSObject, neither of which cares about
    1085             :   // compartments.
    1086             : 
    1087           0 :   if (nsNPObjWrapper::IsWrapper(obj)) {
    1088             :     // obj is one of our own, its private data is the NPObject we're
    1089             :     // looking for.
    1090             : 
    1091           0 :     NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
    1092             : 
    1093             :     // If the private is null, that means that the object has already been torn
    1094             :     // down, possible because the owning plugin was destroyed (there can be
    1095             :     // multiple plugins, so the fact that it was destroyed does not prevent one
    1096             :     // of its dead JS objects from being passed to another plugin). There's not
    1097             :     // much use in wrapping such a dead object, so we just return null, causing
    1098             :     // us to throw.
    1099           0 :     if (!npobj)
    1100           0 :       return nullptr;
    1101             : 
    1102           0 :     if (LookupNPP(npobj) == npp)
    1103           0 :       return _retainobject(npobj);
    1104             :   }
    1105             : 
    1106           0 :   if (!sJSObjWrappers.initialized()) {
    1107             :     // No hash yet (or any more), initialize it.
    1108           0 :     if (!CreateJSObjWrapperTable())
    1109           0 :       return nullptr;
    1110             :   }
    1111           0 :   MOZ_ASSERT(sJSObjWrappersAccessible);
    1112             : 
    1113           0 :   JSObjWrapperTable::Ptr p = sJSObjWrappers.lookupForAdd(nsJSObjWrapperKey(obj, npp));
    1114           0 :   if (p) {
    1115           0 :     MOZ_ASSERT(p->value());
    1116             :     // Found a live nsJSObjWrapper, return it.
    1117             : 
    1118           0 :     return _retainobject(p->value());
    1119             :   }
    1120             : 
    1121             :   // No existing nsJSObjWrapper, create one.
    1122             : 
    1123             :   nsJSObjWrapper *wrapper =
    1124           0 :     (nsJSObjWrapper *)_createobject(npp, &sJSObjWrapperNPClass);
    1125             : 
    1126           0 :   if (!wrapper) {
    1127             :     // Out of memory, entry not yet added to table.
    1128           0 :     return nullptr;
    1129             :   }
    1130             : 
    1131           0 :   wrapper->mJSObj = obj;
    1132             : 
    1133             :   // Insert the new wrapper into the hashtable, rooting the JSObject. Its
    1134             :   // lifetime is now tied to that of the NPObject.
    1135           0 :   if (!sJSObjWrappers.putNew(nsJSObjWrapperKey(obj, npp), wrapper)) {
    1136             :     // Out of memory, free the wrapper we created.
    1137           0 :     _releaseobject(wrapper);
    1138           0 :     return nullptr;
    1139             :   }
    1140             : 
    1141           0 :   return wrapper;
    1142             : }
    1143             : 
    1144             : // Climb the prototype chain, unwrapping as necessary until we find an NP object
    1145             : // wrapper.
    1146             : //
    1147             : // Because this function unwraps, its return value must be wrapped for the cx
    1148             : // compartment for callers that plan to hold onto the result or do anything
    1149             : // substantial with it.
    1150             : static JSObject *
    1151           0 : GetNPObjectWrapper(JSContext *cx, JSObject *aObj, bool wrapResult = true)
    1152             : {
    1153           0 :   JS::Rooted<JSObject*> obj(cx, aObj);
    1154           0 :   while (obj && (obj = js::CheckedUnwrap(obj))) {
    1155           0 :     if (nsNPObjWrapper::IsWrapper(obj)) {
    1156           0 :       if (wrapResult && !JS_WrapObject(cx, &obj)) {
    1157           0 :         return nullptr;
    1158             :       }
    1159           0 :       return obj;
    1160             :     }
    1161             : 
    1162           0 :     JSAutoCompartment ac(cx, obj);
    1163           0 :     if (!::JS_GetPrototype(cx, obj, &obj)) {
    1164           0 :       return nullptr;
    1165             :     }
    1166             :   }
    1167           0 :   return nullptr;
    1168             : }
    1169             : 
    1170             : static NPObject *
    1171           0 : GetNPObject(JSContext *cx, JSObject *obj)
    1172             : {
    1173           0 :   obj = GetNPObjectWrapper(cx, obj, /* wrapResult = */ false);
    1174           0 :   if (!obj) {
    1175           0 :     return nullptr;
    1176             :   }
    1177             : 
    1178           0 :   return (NPObject *)::JS_GetPrivate(obj);
    1179             : }
    1180             : 
    1181             : 
    1182             : // Does not actually add a property because this is always followed by a
    1183             : // SetProperty call.
    1184             : static bool
    1185           0 : NPObjWrapper_AddProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::Handle<JS::Value> v)
    1186             : {
    1187           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1188             : 
    1189           0 :   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
    1190           0 :       !npobj->_class->hasMethod) {
    1191           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1192             : 
    1193           0 :     return false;
    1194             :   }
    1195             : 
    1196           0 :   if (NPObjectIsOutOfProcessProxy(npobj)) {
    1197           0 :     return true;
    1198             :   }
    1199             : 
    1200           0 :   PluginDestructionGuard pdg(LookupNPP(npobj));
    1201             : 
    1202           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    1203           0 :   bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
    1204           0 :   if (!ReportExceptionIfPending(cx))
    1205           0 :     return false;
    1206             : 
    1207           0 :   if (hasProperty)
    1208           0 :     return true;
    1209             : 
    1210             :   // We must permit methods here since JS_DefineUCFunction() will add
    1211             :   // the function as a property
    1212           0 :   bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
    1213           0 :   if (!ReportExceptionIfPending(cx))
    1214           0 :     return false;
    1215             : 
    1216           0 :   if (!hasMethod) {
    1217           0 :     ThrowJSExceptionASCII(cx, "Trying to add unsupported property on NPObject!");
    1218             : 
    1219           0 :     return false;
    1220             :   }
    1221             : 
    1222           0 :   return true;
    1223             : }
    1224             : 
    1225             : static bool
    1226           0 : NPObjWrapper_DelProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
    1227             :                          JS::ObjectOpResult &result)
    1228             : {
    1229           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1230             : 
    1231           0 :   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
    1232           0 :       !npobj->_class->removeProperty) {
    1233           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1234             : 
    1235           0 :     return false;
    1236             :   }
    1237             : 
    1238           0 :   PluginDestructionGuard pdg(LookupNPP(npobj));
    1239             : 
    1240           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    1241             : 
    1242           0 :   if (!NPObjectIsOutOfProcessProxy(npobj)) {
    1243           0 :     bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
    1244           0 :     if (!ReportExceptionIfPending(cx))
    1245           0 :       return false;
    1246             : 
    1247           0 :     if (!hasProperty)
    1248           0 :       return result.succeed();
    1249             :   }
    1250             : 
    1251             :   // This removeProperty hook may throw an exception and return false; or just
    1252             :   // return false without an exception pending, which behaves like `delete
    1253             :   // obj.prop` returning false: in strict mode it becomes a TypeError. Legacy
    1254             :   // code---nothing else that uses the JSAPI works this way anymore.
    1255           0 :   bool succeeded = npobj->_class->removeProperty(npobj, identifier);
    1256           0 :   if (!ReportExceptionIfPending(cx))
    1257           0 :     return false;
    1258           0 :   return succeeded ? result.succeed() : result.failCantDelete();
    1259             : }
    1260             : 
    1261             : static bool
    1262           0 : NPObjWrapper_SetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
    1263             :                          JS::MutableHandle<JS::Value> vp, JS::ObjectOpResult &result)
    1264             : {
    1265           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1266             : 
    1267           0 :   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
    1268           0 :       !npobj->_class->setProperty) {
    1269           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1270             : 
    1271           0 :     return false;
    1272             :   }
    1273             : 
    1274             :   // Find out what plugin (NPP) is the owner of the object we're
    1275             :   // manipulating, and make it own any JSObject wrappers created here.
    1276           0 :   NPP npp = LookupNPP(npobj);
    1277             : 
    1278           0 :   if (!npp) {
    1279           0 :     ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
    1280             : 
    1281           0 :     return false;
    1282             :   }
    1283             : 
    1284           0 :   PluginDestructionGuard pdg(npp);
    1285             : 
    1286           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    1287             : 
    1288           0 :   if (!NPObjectIsOutOfProcessProxy(npobj)) {
    1289           0 :     bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
    1290           0 :     if (!ReportExceptionIfPending(cx))
    1291           0 :       return false;
    1292             : 
    1293           0 :     if (!hasProperty) {
    1294           0 :       ThrowJSExceptionASCII(cx, "Trying to set unsupported property on NPObject!");
    1295             : 
    1296           0 :       return false;
    1297             :     }
    1298             :   }
    1299             : 
    1300             :   NPVariant npv;
    1301           0 :   if (!JSValToNPVariant(npp, cx, vp, &npv)) {
    1302           0 :     ThrowJSExceptionASCII(cx, "Error converting jsval to NPVariant!");
    1303             : 
    1304           0 :     return false;
    1305             :   }
    1306             : 
    1307           0 :   bool ok = npobj->_class->setProperty(npobj, identifier, &npv);
    1308           0 :   _releasevariantvalue(&npv); // Release the variant
    1309           0 :   if (!ReportExceptionIfPending(cx))
    1310           0 :     return false;
    1311             : 
    1312           0 :   if (!ok) {
    1313           0 :     ThrowJSExceptionASCII(cx, "Error setting property on NPObject!");
    1314             : 
    1315           0 :     return false;
    1316             :   }
    1317             : 
    1318           0 :   return result.succeed();
    1319             : }
    1320             : 
    1321             : static bool
    1322           0 : NPObjWrapper_GetProperty(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp)
    1323             : {
    1324           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1325             : 
    1326           0 :   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
    1327           0 :       !npobj->_class->hasMethod || !npobj->_class->getProperty) {
    1328           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1329             : 
    1330           0 :     return false;
    1331             :   }
    1332             : 
    1333           0 :   if (JSID_IS_SYMBOL(id)) {
    1334           0 :     JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
    1335           0 :     if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
    1336           0 :       JS::RootedObject obj(cx, JS_GetFunctionObject(
    1337             :                                  JS_NewFunction(
    1338             :                                    cx, NPObjWrapper_toPrimitive, 1, 0,
    1339           0 :                                    "Symbol.toPrimitive")));
    1340           0 :       if (!obj)
    1341           0 :         return false;
    1342           0 :       vp.setObject(*obj);
    1343           0 :       return true;
    1344             :     }
    1345             : 
    1346           0 :     if (JS::GetSymbolCode(sym) == JS::SymbolCode::toStringTag) {
    1347           0 :       JS::RootedString tag(cx, JS_NewStringCopyZ(cx, NPRUNTIME_JSCLASS_NAME));
    1348           0 :       if (!tag) {
    1349           0 :         return false;
    1350             :       }
    1351             : 
    1352           0 :       vp.setString(tag);
    1353           0 :       return true;
    1354             :     }
    1355             : 
    1356           0 :     vp.setUndefined();
    1357           0 :     return true;
    1358             :   }
    1359             : 
    1360             :   // Find out what plugin (NPP) is the owner of the object we're
    1361             :   // manipulating, and make it own any JSObject wrappers created here.
    1362           0 :   NPP npp = LookupNPP(npobj);
    1363           0 :   if (!npp) {
    1364           0 :     ThrowJSExceptionASCII(cx, "No NPP found for NPObject!");
    1365             : 
    1366           0 :     return false;
    1367             :   }
    1368             : 
    1369           0 :   PluginDestructionGuard pdg(npp);
    1370             : 
    1371             :   bool hasProperty, hasMethod;
    1372             : 
    1373             :   NPVariant npv;
    1374           0 :   VOID_TO_NPVARIANT(npv);
    1375             : 
    1376           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    1377             : 
    1378           0 :   if (NPObjectIsOutOfProcessProxy(npobj)) {
    1379             :     PluginScriptableObjectParent* actor =
    1380           0 :       static_cast<ParentNPObject*>(npobj)->parent;
    1381             : 
    1382             :     // actor may be null if the plugin crashed.
    1383           0 :     if (!actor)
    1384           0 :       return false;
    1385             : 
    1386             :     bool success = actor->GetPropertyHelper(identifier, &hasProperty,
    1387           0 :                                             &hasMethod, &npv);
    1388             : 
    1389           0 :     if (!ReportExceptionIfPending(cx)) {
    1390           0 :       if (success)
    1391           0 :         _releasevariantvalue(&npv);
    1392           0 :       return false;
    1393             :     }
    1394             : 
    1395           0 :     if (success) {
    1396             :       // We return NPObject Member class here to support ambiguous members.
    1397           0 :       if (hasProperty && hasMethod)
    1398           0 :         return CreateNPObjectMember(npp, cx, obj, npobj, id, &npv, vp);
    1399             : 
    1400           0 :       if (hasProperty) {
    1401           0 :         vp.set(NPVariantToJSVal(npp, cx, &npv));
    1402           0 :         _releasevariantvalue(&npv);
    1403             : 
    1404           0 :         if (!ReportExceptionIfPending(cx))
    1405           0 :           return false;
    1406             :       }
    1407             :     }
    1408           0 :     return true;
    1409             :   }
    1410             : 
    1411           0 :   hasProperty = npobj->_class->hasProperty(npobj, identifier);
    1412           0 :   if (!ReportExceptionIfPending(cx))
    1413           0 :     return false;
    1414             : 
    1415           0 :   hasMethod = npobj->_class->hasMethod(npobj, identifier);
    1416           0 :   if (!ReportExceptionIfPending(cx))
    1417           0 :     return false;
    1418             : 
    1419             :   // We return NPObject Member class here to support ambiguous members.
    1420           0 :   if (hasProperty && hasMethod)
    1421           0 :     return CreateNPObjectMember(npp, cx, obj, npobj, id, nullptr, vp);
    1422             : 
    1423           0 :   if (hasProperty) {
    1424           0 :     if (npobj->_class->getProperty(npobj, identifier, &npv))
    1425           0 :       vp.set(NPVariantToJSVal(npp, cx, &npv));
    1426             : 
    1427           0 :     _releasevariantvalue(&npv);
    1428             : 
    1429           0 :     if (!ReportExceptionIfPending(cx))
    1430           0 :       return false;
    1431             :   }
    1432             : 
    1433           0 :   return true;
    1434             : }
    1435             : 
    1436             : static bool
    1437           0 : CallNPMethodInternal(JSContext *cx, JS::Handle<JSObject*> obj, unsigned argc,
    1438             :                      JS::Value *argv, JS::Value *rval, bool ctorCall)
    1439             : {
    1440           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1441             : 
    1442           0 :   if (!npobj || !npobj->_class) {
    1443           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1444             : 
    1445           0 :     return false;
    1446             :   }
    1447             : 
    1448             :   // Find out what plugin (NPP) is the owner of the object we're
    1449             :   // manipulating, and make it own any JSObject wrappers created here.
    1450           0 :   NPP npp = LookupNPP(npobj);
    1451             : 
    1452           0 :   if (!npp) {
    1453           0 :     ThrowJSExceptionASCII(cx, "Error finding NPP for NPObject!");
    1454             : 
    1455           0 :     return false;
    1456             :   }
    1457             : 
    1458           0 :   PluginDestructionGuard pdg(npp);
    1459             : 
    1460             :   NPVariant npargs_buf[8];
    1461           0 :   NPVariant *npargs = npargs_buf;
    1462             : 
    1463           0 :   if (argc > (sizeof(npargs_buf) / sizeof(NPVariant))) {
    1464             :     // Our stack buffer isn't large enough to hold all arguments,
    1465             :     // malloc a buffer.
    1466           0 :     npargs = (NPVariant*) malloc(argc * sizeof(NPVariant));
    1467             : 
    1468           0 :     if (!npargs) {
    1469           0 :       ThrowJSExceptionASCII(cx, "Out of memory!");
    1470             : 
    1471           0 :       return false;
    1472             :     }
    1473             :   }
    1474             : 
    1475             :   // Convert arguments
    1476             :   uint32_t i;
    1477           0 :   for (i = 0; i < argc; ++i) {
    1478           0 :     if (!JSValToNPVariant(npp, cx, argv[i], npargs + i)) {
    1479           0 :       ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
    1480             : 
    1481           0 :       if (npargs != npargs_buf) {
    1482           0 :         free(npargs);
    1483             :       }
    1484             : 
    1485           0 :       return false;
    1486             :     }
    1487             :   }
    1488             : 
    1489             :   NPVariant v;
    1490           0 :   VOID_TO_NPVARIANT(v);
    1491             : 
    1492           0 :   JSObject *funobj = argv[-2].toObjectOrNull();
    1493             :   bool ok;
    1494           0 :   const char *msg = "Error calling method on NPObject!";
    1495             : 
    1496           0 :   if (ctorCall) {
    1497             :     // construct a new NPObject based on the NPClass in npobj. Fail if
    1498             :     // no construct method is available.
    1499             : 
    1500           0 :     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(npobj->_class) &&
    1501           0 :         npobj->_class->construct) {
    1502           0 :       ok = npobj->_class->construct(npobj, npargs, argc, &v);
    1503             :     } else {
    1504           0 :       ok = false;
    1505             : 
    1506           0 :       msg = "Attempt to construct object from class with no constructor.";
    1507             :     }
    1508           0 :   } else if (funobj != obj) {
    1509             :     // A obj.function() style call is made, get the method name from
    1510             :     // the function object.
    1511             : 
    1512           0 :     if (npobj->_class->invoke) {
    1513           0 :       JSFunction *fun = ::JS_GetObjectFunction(funobj);
    1514           0 :       JS::Rooted<JSString*> funId(cx, ::JS_GetFunctionId(fun));
    1515           0 :       JSString *name = ::JS_AtomizeAndPinJSString(cx, funId);
    1516           0 :       NPIdentifier id = StringToNPIdentifier(cx, name);
    1517             : 
    1518           0 :       ok = npobj->_class->invoke(npobj, id, npargs, argc, &v);
    1519             :     } else {
    1520           0 :       ok = false;
    1521             : 
    1522           0 :       msg = "Attempt to call a method on object with no invoke method.";
    1523             :     }
    1524             :   } else {
    1525           0 :     if (npobj->_class->invokeDefault) {
    1526             :       // obj is a callable object that is being called, no method name
    1527             :       // available then. Invoke the default method.
    1528             : 
    1529           0 :       ok = npobj->_class->invokeDefault(npobj, npargs, argc, &v);
    1530             :     } else {
    1531           0 :       ok = false;
    1532             : 
    1533           0 :       msg = "Attempt to call a default method on object with no "
    1534             :         "invokeDefault method.";
    1535             :     }
    1536             :   }
    1537             : 
    1538             :   // Release arguments.
    1539           0 :   for (i = 0; i < argc; ++i) {
    1540           0 :     _releasevariantvalue(npargs + i);
    1541             :   }
    1542             : 
    1543           0 :   if (npargs != npargs_buf) {
    1544           0 :     free(npargs);
    1545             :   }
    1546             : 
    1547           0 :   if (!ok) {
    1548             :     // ReportExceptionIfPending returns a return value, which is true
    1549             :     // if no exception was thrown. In that case, throw our own.
    1550           0 :     if (ReportExceptionIfPending(cx))
    1551           0 :       ThrowJSExceptionASCII(cx, msg);
    1552             : 
    1553           0 :     return false;
    1554             :   }
    1555             : 
    1556           0 :   *rval = NPVariantToJSVal(npp, cx, &v);
    1557             : 
    1558             :   // *rval now owns the value, release our reference.
    1559           0 :   _releasevariantvalue(&v);
    1560             : 
    1561           0 :   return ReportExceptionIfPending(cx);
    1562             : }
    1563             : 
    1564             : static bool
    1565           0 : CallNPMethod(JSContext *cx, unsigned argc, JS::Value *vp)
    1566             : {
    1567           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1568           0 :   JS::Rooted<JSObject*> obj(cx, JS_THIS_OBJECT(cx, vp));
    1569           0 :   if (!obj)
    1570           0 :       return false;
    1571             : 
    1572           0 :   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
    1573             : }
    1574             : 
    1575             : static bool
    1576           0 : NPObjWrapper_NewEnumerate(JSContext *cx, JS::Handle<JSObject*> obj,
    1577             :                           JS::AutoIdVector &properties, bool enumerableOnly)
    1578             : {
    1579           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1580           0 :   if (!npobj || !npobj->_class) {
    1581           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1582           0 :     return false;
    1583             :   }
    1584             : 
    1585           0 :   PluginDestructionGuard pdg(LookupNPP(npobj));
    1586             : 
    1587           0 :   if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(npobj->_class) ||
    1588           0 :       !npobj->_class->enumerate) {
    1589           0 :     return true;
    1590             :   }
    1591             : 
    1592             :   NPIdentifier *identifiers;
    1593             :   uint32_t length;
    1594           0 :   if (!npobj->_class->enumerate(npobj, &identifiers, &length)) {
    1595           0 :     if (ReportExceptionIfPending(cx)) {
    1596             :       // ReportExceptionIfPending returns a return value, which is true
    1597             :       // if no exception was thrown. In that case, throw our own.
    1598             :       ThrowJSExceptionASCII(cx, "Error enumerating properties on scriptable "
    1599           0 :                             "plugin object");
    1600             :     }
    1601           0 :     return false;
    1602             :   }
    1603             : 
    1604           0 :   if (!properties.reserve(length))
    1605           0 :     return false;
    1606             : 
    1607           0 :   JS::Rooted<jsid> id(cx);
    1608           0 :   for (uint32_t i = 0; i < length; i++) {
    1609           0 :     id = NPIdentifierToJSId(identifiers[i]);
    1610           0 :     properties.infallibleAppend(id);
    1611             :   }
    1612             : 
    1613           0 :   free(identifiers);
    1614           0 :   return true;
    1615             : }
    1616             : 
    1617             : static bool
    1618           0 : NPObjWrapper_Resolve(JSContext *cx, JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
    1619             :                      bool *resolvedp)
    1620             : {
    1621           0 :   if (JSID_IS_SYMBOL(id))
    1622           0 :     return true;
    1623             : 
    1624           0 :   AUTO_PROFILER_LABEL("NPObjWrapper_Resolve", JS);
    1625             : 
    1626           0 :   NPObject *npobj = GetNPObject(cx, obj);
    1627             : 
    1628           0 :   if (!npobj || !npobj->_class || !npobj->_class->hasProperty ||
    1629           0 :       !npobj->_class->hasMethod) {
    1630           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject as private data!");
    1631             : 
    1632           0 :     return false;
    1633             :   }
    1634             : 
    1635           0 :   PluginDestructionGuard pdg(LookupNPP(npobj));
    1636             : 
    1637           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    1638             : 
    1639           0 :   bool hasProperty = npobj->_class->hasProperty(npobj, identifier);
    1640           0 :   if (!ReportExceptionIfPending(cx))
    1641           0 :     return false;
    1642             : 
    1643           0 :   if (hasProperty) {
    1644           0 :     NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
    1645             :                  "id must be either string or int!\n");
    1646           0 :     if (!::JS_DefinePropertyById(cx, obj, id, JS::UndefinedHandleValue,
    1647             :                                  JSPROP_ENUMERATE | JSPROP_SHARED)) {
    1648           0 :         return false;
    1649             :     }
    1650             : 
    1651           0 :     *resolvedp = true;
    1652             : 
    1653           0 :     return true;
    1654             :   }
    1655             : 
    1656           0 :   bool hasMethod = npobj->_class->hasMethod(npobj, identifier);
    1657           0 :   if (!ReportExceptionIfPending(cx))
    1658           0 :     return false;
    1659             : 
    1660           0 :   if (hasMethod) {
    1661           0 :     NS_ASSERTION(JSID_IS_STRING(id) || JSID_IS_INT(id),
    1662             :                  "id must be either string or int!\n");
    1663             : 
    1664             :     JSFunction *fnc = ::JS_DefineFunctionById(cx, obj, id, CallNPMethod, 0,
    1665           0 :                                               JSPROP_ENUMERATE);
    1666             : 
    1667           0 :     *resolvedp = true;
    1668             : 
    1669           0 :     return fnc != nullptr;
    1670             :   }
    1671             : 
    1672             :   // no property or method
    1673           0 :   return true;
    1674             : }
    1675             : 
    1676             : static void
    1677           0 : NPObjWrapper_Finalize(js::FreeOp *fop, JSObject *obj)
    1678             : {
    1679           0 :   JS::AutoAssertGCCallback inCallback;
    1680             : 
    1681           0 :   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
    1682           0 :   if (npobj) {
    1683           0 :     if (sNPObjWrappers) {
    1684             :       // If the sNPObjWrappers map contains an entry that refers to this
    1685             :       // wrapper, remove it.
    1686             :       auto entry =
    1687           0 :         static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
    1688           0 :       if (entry && entry->mJSObj == obj) {
    1689           0 :         sNPObjWrappers->Remove(npobj);
    1690             :       }
    1691             :     }
    1692             :   }
    1693             : 
    1694           0 :   if (!sDelayedReleases)
    1695           0 :     sDelayedReleases = new nsTArray<NPObject*>;
    1696           0 :   sDelayedReleases->AppendElement(npobj);
    1697           0 : }
    1698             : 
    1699             : static void
    1700           0 : NPObjWrapper_ObjectMoved(JSObject *obj, const JSObject *old)
    1701             : {
    1702             :   // The wrapper JSObject has been moved, so we need to update the entry in the
    1703             :   // sNPObjWrappers hash table, if present.
    1704             : 
    1705           0 :   if (!sNPObjWrappers) {
    1706           0 :     return;
    1707             :   }
    1708             : 
    1709           0 :   NPObject *npobj = (NPObject *)::JS_GetPrivate(obj);
    1710           0 :   if (!npobj) {
    1711           0 :     return;
    1712             :   }
    1713             : 
    1714             :   // Calling PLDHashTable::Search() will not result in GC.
    1715           0 :   JS::AutoSuppressGCAnalysis nogc;
    1716             : 
    1717             :   auto entry =
    1718           0 :     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
    1719           0 :   MOZ_ASSERT(entry && entry->mJSObj);
    1720           0 :   MOZ_ASSERT(entry->mJSObj == old);
    1721           0 :   entry->mJSObj = obj;
    1722             : }
    1723             : 
    1724             : static bool
    1725           0 : NPObjWrapper_Call(JSContext *cx, unsigned argc, JS::Value *vp)
    1726             : {
    1727           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1728           0 :   JS::Rooted<JSObject*> obj(cx, &args.callee());
    1729           0 :   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, false);
    1730             : }
    1731             : 
    1732             : static bool
    1733           0 : NPObjWrapper_Construct(JSContext *cx, unsigned argc, JS::Value *vp)
    1734             : {
    1735           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1736           0 :   JS::Rooted<JSObject*> obj(cx, &args.callee());
    1737           0 :   return CallNPMethodInternal(cx, obj, args.length(), args.array(), vp, true);
    1738             : }
    1739             : 
    1740             : static bool
    1741           0 : NPObjWrapper_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
    1742             : {
    1743             :   // Plugins do not simply use the default OrdinaryToPrimitive behavior,
    1744             :   // because that behavior involves calling toString or valueOf on objects
    1745             :   // which weren't designed to accommodate this.  Usually this wouldn't be a
    1746             :   // problem, because the absence of either property, or the presence of either
    1747             :   // property with a value that isn't callable, will cause that property to
    1748             :   // simply be ignored.  But there is a problem in one specific case: Java,
    1749             :   // specifically java.lang.Integer.  The Integer class has static valueOf
    1750             :   // methods, none of which are nullary, so the JS-reflected method will behave
    1751             :   // poorly when called with no arguments.  We work around this problem by
    1752             :   // giving plugins a [Symbol.toPrimitive]() method which uses only toString
    1753             :   // and not valueOf.
    1754             : 
    1755           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    1756           0 :   JS::RootedValue thisv(cx, args.thisv());
    1757           0 :   if (thisv.isPrimitive())
    1758           0 :     return true;
    1759             : 
    1760           0 :   JS::RootedObject obj(cx, &thisv.toObject());
    1761           0 :   JS::RootedValue v(cx);
    1762           0 :   if (!JS_GetProperty(cx, obj, "toString", &v))
    1763           0 :     return false;
    1764           0 :   if (v.isObject() && JS::IsCallable(&v.toObject())) {
    1765           0 :     if (!JS_CallFunctionValue(cx, obj, v, JS::HandleValueArray::empty(), args.rval()))
    1766           0 :       return false;
    1767           0 :     if (args.rval().isPrimitive())
    1768           0 :       return true;
    1769             :   }
    1770             : 
    1771             :   JS_ReportErrorNumberASCII(cx, js::GetErrorMessage, nullptr,
    1772             :                             JSMSG_CANT_CONVERT_TO,
    1773           0 :                             JS_GetClass(obj)->name, "primitive type");
    1774           0 :   return false;
    1775             : }
    1776             : 
    1777             : bool
    1778           0 : nsNPObjWrapper::IsWrapper(JSObject *obj)
    1779             : {
    1780           0 :   return js::GetObjectClass(obj) == &sNPObjectJSWrapperClass;
    1781             : }
    1782             : 
    1783             : // An NPObject is going away, make sure we null out the JS object's
    1784             : // private data in case this is an NPObject that came from a plugin
    1785             : // and it's destroyed prematurely.
    1786             : 
    1787             : // static
    1788             : void
    1789           0 : nsNPObjWrapper::OnDestroy(NPObject *npobj)
    1790             : {
    1791           0 :   if (!npobj) {
    1792           0 :     return;
    1793             :   }
    1794             : 
    1795           0 :   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
    1796             :     // npobj is one of our own, no private data to clean up here.
    1797             : 
    1798           0 :     return;
    1799             :   }
    1800             : 
    1801           0 :   if (!sNPObjWrappers) {
    1802             :     // No hash yet (or any more), no used wrappers available.
    1803             : 
    1804           0 :     return;
    1805             :   }
    1806             : 
    1807             :   auto entry =
    1808           0 :     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
    1809             : 
    1810           0 :   if (entry && entry->mJSObj) {
    1811             :     // Found a live NPObject wrapper, null out its JSObjects' private
    1812             :     // data.
    1813             : 
    1814           0 :     ::JS_SetPrivate(entry->mJSObj, nullptr);
    1815             : 
    1816             :     // Remove the npobj from the hash now that it went away.
    1817           0 :     sNPObjWrappers->RawRemove(entry);
    1818             : 
    1819             :     // The finalize hook will call OnWrapperDestroyed().
    1820             :   }
    1821             : }
    1822             : 
    1823             : // Look up or create a JSObject that wraps the NPObject npobj.
    1824             : 
    1825             : // static
    1826             : JSObject *
    1827           0 : nsNPObjWrapper::GetNewOrUsed(NPP npp, JSContext *cx, NPObject *npobj)
    1828             : {
    1829           0 :   if (!npobj) {
    1830           0 :     NS_ERROR("Null NPObject passed to nsNPObjWrapper::GetNewOrUsed()!");
    1831             : 
    1832           0 :     return nullptr;
    1833             :   }
    1834             : 
    1835           0 :   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
    1836             :     // npobj is one of our own, return its existing JSObject.
    1837             : 
    1838           0 :     JS::Rooted<JSObject*> obj(cx, ((nsJSObjWrapper *)npobj)->mJSObj);
    1839           0 :     if (!JS_WrapObject(cx, &obj)) {
    1840           0 :       return nullptr;
    1841             :     }
    1842           0 :     return obj;
    1843             :   }
    1844             : 
    1845           0 :   if (!npp) {
    1846           0 :     NS_ERROR("No npp passed to nsNPObjWrapper::GetNewOrUsed()!");
    1847             : 
    1848           0 :     return nullptr;
    1849             :   }
    1850             : 
    1851           0 :   if (!sNPObjWrappers) {
    1852             :     // No hash yet (or any more), initialize it.
    1853           0 :     if (!CreateNPObjWrapperTable()) {
    1854           0 :       return nullptr;
    1855             :     }
    1856             :   }
    1857             : 
    1858             :   auto entry =
    1859           0 :     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
    1860             : 
    1861           0 :   if (!entry) {
    1862             :     // Out of memory
    1863           0 :     JS_ReportOutOfMemory(cx);
    1864             : 
    1865           0 :     return nullptr;
    1866             :   }
    1867             : 
    1868           0 :   if (entry->mJSObj) {
    1869             :     // Found a NPObject wrapper. First check it is still alive.
    1870           0 :     JSObject* obj = entry->mJSObj;
    1871           0 :     if (js::gc::EdgeNeedsSweepUnbarriered(&obj)) {
    1872             :       // The object is dead (finalization will happen at a later time). By the
    1873             :       // time we leave this function, this entry will either be updated with a
    1874             :       // new wrapper or removed if that fails. Clear it anyway to make sure
    1875             :       // nothing touches the dead object.
    1876           0 :       entry->mJSObj = nullptr;
    1877             :     } else {
    1878             :       // It may not be in the same compartment as cx, so we need to wrap it
    1879             :       // before returning it.
    1880           0 :       JS::Rooted<JSObject*> obj(cx, entry->mJSObj);
    1881           0 :       if (!JS_WrapObject(cx, &obj)) {
    1882           0 :         return nullptr;
    1883             :       }
    1884           0 :       return obj;
    1885             :     }
    1886             :   }
    1887             : 
    1888           0 :   entry->mNPObj = npobj;
    1889           0 :   entry->mNpp = npp;
    1890             : 
    1891           0 :   uint32_t generation = sNPObjWrappers->Generation();
    1892             : 
    1893             :   // No existing JSObject, create one.
    1894             : 
    1895           0 :   JS::Rooted<JSObject*> obj(cx, ::JS_NewObject(cx, js::Jsvalify(&sNPObjectJSWrapperClass)));
    1896             : 
    1897           0 :   if (generation != sNPObjWrappers->Generation()) {
    1898             :       // Reload entry if the JS_NewObject call caused a GC and reallocated
    1899             :       // the table (see bug 445229). This is guaranteed to succeed.
    1900             : 
    1901             :       entry =
    1902           0 :          static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Search(npobj));
    1903           0 :       NS_ASSERTION(entry, "Hashtable didn't find what we just added?");
    1904             :   }
    1905             : 
    1906           0 :   if (!obj) {
    1907             :     // OOM? Remove the stale entry from the hash.
    1908             : 
    1909           0 :     sNPObjWrappers->RawRemove(entry);
    1910             : 
    1911           0 :     return nullptr;
    1912             :   }
    1913             : 
    1914           0 :   OnWrapperCreated();
    1915             : 
    1916           0 :   entry->mJSObj = obj;
    1917             : 
    1918           0 :   ::JS_SetPrivate(obj, npobj);
    1919             : 
    1920             :   // The new JSObject now holds on to npobj
    1921           0 :   _retainobject(npobj);
    1922             : 
    1923           0 :   return obj;
    1924             : }
    1925             : 
    1926             : // static
    1927             : void
    1928           0 : nsJSNPRuntime::OnPluginDestroy(NPP npp)
    1929             : {
    1930           0 :   if (sJSObjWrappersAccessible) {
    1931             : 
    1932             :     // Prevent modification of sJSObjWrappers table if we go reentrant.
    1933           0 :     sJSObjWrappersAccessible = false;
    1934             : 
    1935           0 :     for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
    1936           0 :       nsJSObjWrapper *npobj = e.front().value();
    1937           0 :       MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
    1938           0 :       if (npobj->mNpp == npp) {
    1939           0 :         if (npobj->_class && npobj->_class->invalidate) {
    1940           0 :           npobj->_class->invalidate(npobj);
    1941             :         }
    1942             : 
    1943           0 :         _releaseobject(npobj);
    1944             : 
    1945           0 :         e.removeFront();
    1946             :       }
    1947             :     }
    1948             : 
    1949           0 :     sJSObjWrappersAccessible = true;
    1950             :   }
    1951             : 
    1952           0 :   if (sNPObjWrappers) {
    1953           0 :     for (auto i = sNPObjWrappers->Iter(); !i.Done(); i.Next()) {
    1954           0 :       auto entry = static_cast<NPObjWrapperHashEntry*>(i.Get());
    1955             : 
    1956           0 :       if (entry->mNpp == npp) {
    1957             :         // HACK: temporarily hide the table we're enumerating so that
    1958             :         // invalidate() and deallocate() don't touch it.
    1959           0 :         PLDHashTable *tmp = sNPObjWrappers;
    1960           0 :         sNPObjWrappers = nullptr;
    1961             : 
    1962           0 :         NPObject *npobj = entry->mNPObj;
    1963             : 
    1964           0 :         if (npobj->_class && npobj->_class->invalidate) {
    1965           0 :           npobj->_class->invalidate(npobj);
    1966             :         }
    1967             : 
    1968             : #ifdef NS_BUILD_REFCNT_LOGGING
    1969             :         {
    1970           0 :           int32_t refCnt = npobj->referenceCount;
    1971           0 :           while (refCnt) {
    1972           0 :             --refCnt;
    1973           0 :             NS_LOG_RELEASE(npobj, refCnt, "BrowserNPObject");
    1974             :           }
    1975             :         }
    1976             : #endif
    1977             : 
    1978             :         // Force deallocation of plugin objects since the plugin they came
    1979             :         // from is being torn down.
    1980           0 :         if (npobj->_class && npobj->_class->deallocate) {
    1981           0 :           npobj->_class->deallocate(npobj);
    1982             :         } else {
    1983           0 :           free(npobj);
    1984             :         }
    1985             : 
    1986           0 :         ::JS_SetPrivate(entry->mJSObj, nullptr);
    1987             : 
    1988           0 :         sNPObjWrappers = tmp;
    1989             : 
    1990           0 :         if (sDelayedReleases && sDelayedReleases->RemoveElement(npobj)) {
    1991           0 :           OnWrapperDestroyed();
    1992             :         }
    1993             : 
    1994           0 :         i.Remove();
    1995             :       }
    1996             :     }
    1997             :   }
    1998           0 : }
    1999             : 
    2000             : // static
    2001             : void
    2002           0 : nsJSNPRuntime::OnPluginDestroyPending(NPP npp)
    2003             : {
    2004           0 :   if (sJSObjWrappersAccessible) {
    2005             :     // Prevent modification of sJSObjWrappers table if we go reentrant.
    2006           0 :     sJSObjWrappersAccessible = false;
    2007           0 :     for (JSObjWrapperTable::Enum e(sJSObjWrappers); !e.empty(); e.popFront()) {
    2008           0 :       nsJSObjWrapper *npobj = e.front().value();
    2009           0 :       MOZ_ASSERT(npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass);
    2010           0 :       if (npobj->mNpp == npp) {
    2011           0 :         npobj->mDestroyPending = true;
    2012             :       }
    2013             :     }
    2014           0 :     sJSObjWrappersAccessible = true;
    2015             :   }
    2016           0 : }
    2017             : 
    2018             : // Find the NPP for a NPObject.
    2019             : static NPP
    2020           0 : LookupNPP(NPObject *npobj)
    2021             : {
    2022           0 :   if (npobj->_class == &nsJSObjWrapper::sJSObjWrapperNPClass) {
    2023           0 :     nsJSObjWrapper* o = static_cast<nsJSObjWrapper*>(npobj);
    2024           0 :     return o->mNpp;
    2025             :   }
    2026             : 
    2027             :   auto entry =
    2028           0 :     static_cast<NPObjWrapperHashEntry*>(sNPObjWrappers->Add(npobj, fallible));
    2029             : 
    2030           0 :   if (!entry) {
    2031           0 :     return nullptr;
    2032             :   }
    2033             : 
    2034           0 :   NS_ASSERTION(entry->mNpp, "Live NPObject entry w/o an NPP!");
    2035             : 
    2036           0 :   return entry->mNpp;
    2037             : }
    2038             : 
    2039             : static bool
    2040           0 : CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject* npobj,
    2041             :                      JS::Handle<jsid> id,  NPVariant* getPropertyResult,
    2042             :                      JS::MutableHandle<JS::Value> vp)
    2043             : {
    2044           0 :   if (!npobj || !npobj->_class || !npobj->_class->getProperty ||
    2045           0 :       !npobj->_class->invoke) {
    2046           0 :     ThrowJSExceptionASCII(cx, "Bad NPObject");
    2047             : 
    2048           0 :     return false;
    2049             :   }
    2050             : 
    2051             :   NPObjectMemberPrivate* memberPrivate =
    2052           0 :     (NPObjectMemberPrivate*) malloc(sizeof(NPObjectMemberPrivate));
    2053           0 :   if (!memberPrivate)
    2054           0 :     return false;
    2055             : 
    2056             :   // Make sure to clear all members in case something fails here
    2057             :   // during initialization.
    2058           0 :   memset(memberPrivate, 0, sizeof(NPObjectMemberPrivate));
    2059             : 
    2060           0 :   JSObject *memobj = ::JS_NewObject(cx, &sNPObjectMemberClass);
    2061           0 :   if (!memobj) {
    2062           0 :     free(memberPrivate);
    2063           0 :     return false;
    2064             :   }
    2065             : 
    2066           0 :   vp.setObject(*memobj);
    2067             : 
    2068           0 :   ::JS_SetPrivate(memobj, (void *)memberPrivate);
    2069             : 
    2070           0 :   NPIdentifier identifier = JSIdToNPIdentifier(id);
    2071             : 
    2072           0 :   JS::Rooted<JS::Value> fieldValue(cx);
    2073             :   NPVariant npv;
    2074             : 
    2075           0 :   if (getPropertyResult) {
    2076             :     // Plugin has already handed us the value we want here.
    2077           0 :     npv = *getPropertyResult;
    2078             :   }
    2079             :   else {
    2080           0 :     VOID_TO_NPVARIANT(npv);
    2081             : 
    2082           0 :     NPBool hasProperty = npobj->_class->getProperty(npobj, identifier,
    2083           0 :                                                     &npv);
    2084           0 :     if (!ReportExceptionIfPending(cx) || !hasProperty) {
    2085           0 :       return false;
    2086             :     }
    2087             :   }
    2088             : 
    2089           0 :   fieldValue = NPVariantToJSVal(npp, cx, &npv);
    2090             : 
    2091             :   // npobjWrapper is the JSObject through which we make sure we don't
    2092             :   // outlive the underlying NPObject, so make sure it points to the
    2093             :   // real JSObject wrapper for the NPObject.
    2094           0 :   obj = GetNPObjectWrapper(cx, obj);
    2095             : 
    2096           0 :   memberPrivate->npobjWrapper = obj;
    2097             : 
    2098           0 :   memberPrivate->fieldValue = fieldValue;
    2099           0 :   memberPrivate->methodName = id;
    2100           0 :   memberPrivate->npp = npp;
    2101             : 
    2102           0 :   return true;
    2103             : }
    2104             : 
    2105             : static bool
    2106           0 : NPObjectMember_GetProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id,
    2107             :                            JS::MutableHandleValue vp)
    2108             : {
    2109           0 :   AUTO_PROFILER_LABEL("NPObjectMember_GetProperty", OTHER);
    2110             : 
    2111           0 :   if (JSID_IS_SYMBOL(id)) {
    2112           0 :     JS::RootedSymbol sym(cx, JSID_TO_SYMBOL(id));
    2113           0 :     if (JS::GetSymbolCode(sym) == JS::SymbolCode::toPrimitive) {
    2114           0 :       JS::RootedObject obj(cx, JS_GetFunctionObject(
    2115             :                                  JS_NewFunction(
    2116             :                                    cx, NPObjectMember_toPrimitive, 1, 0,
    2117           0 :                                    "Symbol.toPrimitive")));
    2118           0 :       if (!obj)
    2119           0 :         return false;
    2120           0 :       vp.setObject(*obj);
    2121           0 :       return true;
    2122             :     }
    2123             :   }
    2124             : 
    2125           0 :   return true;
    2126             : }
    2127             : 
    2128             : static void
    2129           0 : NPObjectMember_Finalize(JSFreeOp *fop, JSObject *obj)
    2130             : {
    2131             :   NPObjectMemberPrivate *memberPrivate;
    2132             : 
    2133           0 :   memberPrivate = (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
    2134           0 :   if (!memberPrivate)
    2135           0 :     return;
    2136             : 
    2137           0 :   free(memberPrivate);
    2138             : }
    2139             : 
    2140             : static bool
    2141           0 : NPObjectMember_Call(JSContext *cx, unsigned argc, JS::Value *vp)
    2142             : {
    2143           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    2144           0 :   JS::Rooted<JSObject*> memobj(cx, &args.callee());
    2145           0 :   NS_ENSURE_TRUE(memobj, false);
    2146             : 
    2147             :   NPObjectMemberPrivate *memberPrivate =
    2148           0 :     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, memobj,
    2149             :                                                      &sNPObjectMemberClass,
    2150           0 :                                                      &args);
    2151           0 :   if (!memberPrivate || !memberPrivate->npobjWrapper)
    2152           0 :     return false;
    2153             : 
    2154           0 :   NPObject *npobj = GetNPObject(cx, memberPrivate->npobjWrapper);
    2155           0 :   if (!npobj) {
    2156           0 :     ThrowJSExceptionASCII(cx, "Call on invalid member object");
    2157             : 
    2158           0 :     return false;
    2159             :   }
    2160             : 
    2161             :   NPVariant npargs_buf[8];
    2162           0 :   NPVariant *npargs = npargs_buf;
    2163             : 
    2164           0 :   if (args.length() > (sizeof(npargs_buf) / sizeof(NPVariant))) {
    2165             :     // Our stack buffer isn't large enough to hold all arguments,
    2166             :     // malloc a buffer.
    2167           0 :     npargs = (NPVariant*) malloc(args.length() * sizeof(NPVariant));
    2168             : 
    2169           0 :     if (!npargs) {
    2170           0 :       ThrowJSExceptionASCII(cx, "Out of memory!");
    2171             : 
    2172           0 :       return false;
    2173             :     }
    2174             :   }
    2175             : 
    2176             :   // Convert arguments
    2177           0 :   for (uint32_t i = 0; i < args.length(); ++i) {
    2178           0 :     if (!JSValToNPVariant(memberPrivate->npp, cx, args[i], npargs + i)) {
    2179           0 :       ThrowJSExceptionASCII(cx, "Error converting jsvals to NPVariants!");
    2180             : 
    2181           0 :       if (npargs != npargs_buf) {
    2182           0 :         free(npargs);
    2183             :       }
    2184             : 
    2185           0 :       return false;
    2186             :     }
    2187             :   }
    2188             : 
    2189             : 
    2190             :   NPVariant npv;
    2191           0 :   bool ok = npobj->_class->invoke(npobj,
    2192           0 :                                   JSIdToNPIdentifier(memberPrivate->methodName),
    2193           0 :                                   npargs, args.length(), &npv);
    2194             : 
    2195             :   // Release arguments.
    2196           0 :   for (uint32_t i = 0; i < args.length(); ++i) {
    2197           0 :     _releasevariantvalue(npargs + i);
    2198             :   }
    2199             : 
    2200           0 :   if (npargs != npargs_buf) {
    2201           0 :     free(npargs);
    2202             :   }
    2203             : 
    2204           0 :   if (!ok) {
    2205             :     // ReportExceptionIfPending returns a return value, which is true
    2206             :     // if no exception was thrown. In that case, throw our own.
    2207           0 :     if (ReportExceptionIfPending(cx))
    2208           0 :       ThrowJSExceptionASCII(cx, "Error calling method on NPObject!");
    2209             : 
    2210           0 :     return false;
    2211             :   }
    2212             : 
    2213           0 :   args.rval().set(NPVariantToJSVal(memberPrivate->npp, cx, &npv));
    2214             : 
    2215             :   // *vp now owns the value, release our reference.
    2216           0 :   _releasevariantvalue(&npv);
    2217             : 
    2218           0 :   return ReportExceptionIfPending(cx);
    2219             : }
    2220             : 
    2221             : static void
    2222           0 : NPObjectMember_Trace(JSTracer *trc, JSObject *obj)
    2223             : {
    2224             :   NPObjectMemberPrivate *memberPrivate =
    2225           0 :     (NPObjectMemberPrivate *)::JS_GetPrivate(obj);
    2226           0 :   if (!memberPrivate)
    2227           0 :     return;
    2228             : 
    2229             :   // Our NPIdentifier is not always interned, so we must trace it.
    2230           0 :   JS::TraceEdge(trc, &memberPrivate->methodName, "NPObjectMemberPrivate.methodName");
    2231             : 
    2232           0 :   JS::TraceEdge(trc, &memberPrivate->fieldValue, "NPObject Member => fieldValue");
    2233             : 
    2234             :   // There's no strong reference from our private data to the
    2235             :   // NPObject, so make sure to mark the NPObject wrapper to keep the
    2236             :   // NPObject alive as long as this NPObjectMember is alive.
    2237           0 :   JS::TraceEdge(trc, &memberPrivate->npobjWrapper,
    2238           0 :                 "NPObject Member => npobjWrapper");
    2239             : }
    2240             : 
    2241             : static bool
    2242           0 : NPObjectMember_toPrimitive(JSContext *cx, unsigned argc, JS::Value *vp)
    2243             : {
    2244           0 :   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    2245           0 :   JS::RootedValue thisv(cx, args.thisv());
    2246           0 :   if (thisv.isPrimitive()) {
    2247           0 :     args.rval().set(thisv);
    2248           0 :     return true;
    2249             :   }
    2250             : 
    2251           0 :   JS::RootedObject obj(cx, &thisv.toObject());
    2252             :   NPObjectMemberPrivate *memberPrivate =
    2253           0 :     (NPObjectMemberPrivate *)::JS_GetInstancePrivate(cx, obj,
    2254             :                                                      &sNPObjectMemberClass,
    2255           0 :                                                      &args);
    2256           0 :   if (!memberPrivate)
    2257           0 :     return false;
    2258             : 
    2259             :   JSType hint;
    2260           0 :   if (!JS::GetFirstArgumentAsTypeHint(cx, args, &hint))
    2261           0 :     return false;
    2262             : 
    2263           0 :   args.rval().set(memberPrivate->fieldValue);
    2264           0 :   if (args.rval().isObject()) {
    2265           0 :     JS::Rooted<JSObject*> objVal(cx, &args.rval().toObject());
    2266           0 :     return JS::ToPrimitive(cx, objVal, hint, args.rval());
    2267             :   }
    2268           0 :   return true;
    2269             : }
    2270             : 
    2271             : // static
    2272             : bool
    2273           0 : nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
    2274             : {
    2275           0 :   NPP npp = NPPStack::Peek();
    2276           0 :   nsIGlobalObject* globalObject = GetGlobalObject(npp);
    2277           0 :   if (NS_WARN_IF(!globalObject)) {
    2278           0 :     return false;
    2279             :   }
    2280             : 
    2281           0 :   dom::AutoEntryScript aes(globalObject, "NPAPI HasOwnProperty");
    2282           0 :   JSContext *cx = aes.cx();
    2283             : 
    2284           0 :   if (!npobj) {
    2285             :     ThrowJSExceptionASCII(cx,
    2286           0 :                           "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
    2287             : 
    2288           0 :     return false;
    2289             :   }
    2290             : 
    2291           0 :   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
    2292           0 :   bool found, ok = false;
    2293             : 
    2294           0 :   AutoJSExceptionSuppressor suppressor(aes, npjsobj);
    2295           0 :   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
    2296           0 :   JSAutoCompartment ac(cx, jsobj);
    2297           0 :   MarkCrossZoneNPIdentifier(cx, npid);
    2298             : 
    2299           0 :   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
    2300             :                "id must be either string or int!\n");
    2301           0 :   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
    2302           0 :   ok = ::JS_AlreadyHasOwnPropertyById(cx, jsobj, id, &found);
    2303           0 :   return ok && found;
    2304             : }

Generated by: LCOV version 1.13