LCOV - code coverage report
Current view: directory - dom/plugins/base - nsJSNPRuntime.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 926 0 0.0 %
Date: 2012-06-02 Functions: 56 0 0.0 %

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

Generated by: LCOV version 1.7