dom/plugins/base/nsJSNPRuntime.cpp

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

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

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

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

mercurial