Coverage Report

Created: 2018-09-25 14:53

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