dom/plugins/base/nsJSNPRuntime.cpp

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

mercurial