dom/bindings/BindingUtils.h
author Carsten "Tomcat" Book <cbook@mozilla.com>
Fri, 09 May 2014 14:13:29 +0200
changeset 182330 420f4c65a67f
parent 180729 6ed9e9e160f2
child 182934 056788c50647
permissions -rw-r--r--
merge b2g-inbound to mozilla-central
peterv@90770
     1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
peterv@90770
     2
/* vim: set ts=2 sw=2 et tw=79: */
peterv@90770
     3
/* This Source Code Form is subject to the terms of the Mozilla Public
peterv@90770
     4
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
peterv@90770
     5
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
peterv@90770
     6
bzbarsky@92914
     7
#ifndef mozilla_dom_BindingUtils_h__
bzbarsky@92914
     8
#define mozilla_dom_BindingUtils_h__
peterv@90770
     9
ms2ger@139653
    10
#include "jsfriendapi.h"
ms2ger@139653
    11
#include "jswrapper.h"
birunthan@159472
    12
#include "mozilla/ArrayUtils.h"
ms2ger@142495
    13
#include "mozilla/Alignment.h"
froydnj@156565
    14
#include "mozilla/Array.h"
ms2ger@139653
    15
#include "mozilla/dom/BindingDeclarations.h"
ms2ger@139653
    16
#include "mozilla/dom/CallbackObject.h"
bzbarsky@92914
    17
#include "mozilla/dom/DOMJSClass.h"
peterv@103142
    18
#include "mozilla/dom/DOMJSProxyHandler.h"
khuey@146113
    19
#include "mozilla/dom/Exceptions.h"
peterv@108739
    20
#include "mozilla/dom/NonRefcountedDOMObject.h"
ms2ger@139653
    21
#include "mozilla/dom/Nullable.h"
bzbarsky@148754
    22
#include "mozilla/dom/RootedDictionary.h"
VYV03354@92429
    23
#include "mozilla/dom/workers/Workers.h"
bzbarsky@93311
    24
#include "mozilla/ErrorResult.h"
ms2ger@139653
    25
#include "mozilla/Likely.h"
froydnj@159072
    26
#include "mozilla/MemoryReporting.h"
peterv@140097
    27
#include "nsCycleCollector.h"
ms2ger@94325
    28
#include "nsIXPConnect.h"
bjacob@147933
    29
#include "MainThreadUtils.h"
dbaron@170835
    30
#include "nsISupportsImpl.h"
ms2ger@94325
    31
#include "qsObjectHelper.h"
ms2ger@94325
    32
#include "xpcpublic.h"
ehsan@146019
    33
#include "nsIVariant.h"
ms2ger@139653
    34
peterv@90770
    35
#include "nsWrapperCacheInlines.h"
peterv@90770
    36
peterv@177573
    37
class nsIJSID;
bzbarsky@132439
    38
class nsPIDOMWindow;
bzbarsky@132439
    39
khuey@133399
    40
extern nsresult
sfink@151157
    41
xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg,
sfink@151157
    42
                    nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp);
khuey@133399
    43
peterv@90770
    44
namespace mozilla {
peterv@90770
    45
namespace dom {
peterv@90770
    46
khuey@133399
    47
struct SelfRef
khuey@133399
    48
{
khuey@133399
    49
  SelfRef() : ptr(nullptr) {}
khuey@133399
    50
  explicit SelfRef(nsISupports *p) : ptr(p) {}
khuey@133399
    51
  ~SelfRef() { NS_IF_RELEASE(ptr); }
khuey@133399
    52
khuey@133399
    53
  nsISupports* ptr;
khuey@133399
    54
};
khuey@133399
    55
khuey@133399
    56
/** Convert a jsval to an XPCOM pointer. */
khuey@133399
    57
template <class Interface, class StrongRefType>
khuey@133399
    58
inline nsresult
sfink@151157
    59
UnwrapArg(JSContext* cx, JS::Handle<JS::Value> v, Interface** ppArg,
sfink@151157
    60
          StrongRefType** ppArgRef, JS::MutableHandle<JS::Value> vp)
khuey@133399
    61
{
khuey@133399
    62
  nsISupports* argRef = *ppArgRef;
khuey@133399
    63
  nsresult rv = xpc_qsUnwrapArgImpl(cx, v, NS_GET_TEMPLATE_IID(Interface),
khuey@133399
    64
                                    reinterpret_cast<void**>(ppArg), &argRef,
khuey@133399
    65
                                    vp);
khuey@133399
    66
  *ppArgRef = static_cast<StrongRefType*>(argRef);
khuey@133399
    67
  return rv;
khuey@133399
    68
}
khuey@133399
    69
peterv@153018
    70
inline const ErrNum
peterv@153018
    71
GetInvalidThisErrorForMethod(bool aSecurityError)
peterv@153018
    72
{
peterv@153018
    73
  return aSecurityError ? MSG_METHOD_THIS_UNWRAPPING_DENIED :
peterv@153018
    74
                          MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
peterv@153018
    75
}
peterv@153018
    76
peterv@153018
    77
inline const ErrNum
peterv@153018
    78
GetInvalidThisErrorForGetter(bool aSecurityError)
peterv@153018
    79
{
peterv@153018
    80
  return aSecurityError ? MSG_GETTER_THIS_UNWRAPPING_DENIED :
peterv@153018
    81
                          MSG_GETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
peterv@153018
    82
}
peterv@153018
    83
peterv@153018
    84
inline const ErrNum
peterv@153018
    85
GetInvalidThisErrorForSetter(bool aSecurityError)
peterv@153018
    86
{
peterv@153018
    87
  return aSecurityError ? MSG_SETTER_THIS_UNWRAPPING_DENIED :
peterv@153018
    88
                          MSG_SETTER_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
peterv@153018
    89
}
peterv@153018
    90
ms2ger@99526
    91
bool
bzbarsky@135404
    92
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
bzbarsky@135404
    93
                 const ErrNum aErrorNumber,
bzbarsky@135404
    94
                 const char* aInterfaceName);
ms2ger@99526
    95
bzbarsky@167663
    96
bool
bzbarsky@167663
    97
ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
bzbarsky@167663
    98
                 const ErrNum aErrorNumber,
bzbarsky@167663
    99
                 prototypes::ID aProtoId);
bzbarsky@167663
   100
peterv@90770
   101
inline bool
VYV03354@112492
   102
ThrowMethodFailedWithDetails(JSContext* cx, ErrorResult& rv,
ehsan@137446
   103
                             const char* ifaceName,
peterv@145936
   104
                             const char* memberName,
peterv@145936
   105
                             bool reportJSContentExceptions = false)
peterv@90770
   106
{
VYV03354@112492
   107
  if (rv.IsTypeError()) {
VYV03354@112492
   108
    rv.ReportTypeError(cx);
VYV03354@112492
   109
    return false;
VYV03354@112492
   110
  }
bzbarsky@122335
   111
  if (rv.IsJSException()) {
peterv@145934
   112
    if (reportJSContentExceptions) {
peterv@145934
   113
      rv.ReportJSExceptionFromJSImplementation(cx);
peterv@145934
   114
    } else {
peterv@145934
   115
      rv.ReportJSException(cx);
peterv@145934
   116
    }
bzbarsky@122335
   117
    return false;
bzbarsky@122335
   118
  }
ehsan@137446
   119
  if (rv.IsNotEnoughArgsError()) {
ehsan@137446
   120
    rv.ReportNotEnoughArgsError(cx, ifaceName, memberName);
bzbarsky@172864
   121
    return false;
ehsan@137446
   122
  }
khuey@146113
   123
  return Throw(cx, rv.ErrorCode());
peterv@90770
   124
}
peterv@90770
   125
peterv@111695
   126
// Returns true if the JSClass is used for DOM objects.
peterv@90770
   127
inline bool
peterv@90770
   128
IsDOMClass(const JSClass* clasp)
peterv@90770
   129
{
peterv@90770
   130
  return clasp->flags & JSCLASS_IS_DOMJSCLASS;
peterv@90770
   131
}
peterv@90770
   132
bzbarsky@96011
   133
inline bool
bzbarsky@96011
   134
IsDOMClass(const js::Class* clasp)
bzbarsky@96011
   135
{
peterv@96690
   136
  return IsDOMClass(Jsvalify(clasp));
bzbarsky@96011
   137
}
bzbarsky@96011
   138
efaustbmo@166510
   139
// Return true if the JSClass is used for non-proxy DOM objects.
efaustbmo@166510
   140
inline bool
efaustbmo@166510
   141
IsNonProxyDOMClass(const js::Class* clasp)
efaustbmo@166510
   142
{
efaustbmo@166510
   143
  return IsDOMClass(clasp) && !clasp->isProxy();
efaustbmo@166510
   144
}
efaustbmo@166510
   145
efaustbmo@166510
   146
inline bool
efaustbmo@166510
   147
IsNonProxyDOMClass(const JSClass* clasp)
efaustbmo@166510
   148
{
efaustbmo@166510
   149
  return IsNonProxyDOMClass(js::Valueify(clasp));
efaustbmo@166510
   150
}
efaustbmo@166510
   151
peterv@111695
   152
// Returns true if the JSClass is used for DOM interface and interface 
peterv@111695
   153
// prototype objects.
peterv@111695
   154
inline bool
peterv@111695
   155
IsDOMIfaceAndProtoClass(const JSClass* clasp)
peterv@111695
   156
{
peterv@111695
   157
  return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
peterv@111695
   158
}
peterv@111695
   159
peterv@111695
   160
inline bool
peterv@111695
   161
IsDOMIfaceAndProtoClass(const js::Class* clasp)
peterv@111695
   162
{
peterv@111695
   163
  return IsDOMIfaceAndProtoClass(Jsvalify(clasp));
peterv@111695
   164
}
peterv@111695
   165
ehsan@140595
   166
static_assert(DOM_OBJECT_SLOT == js::PROXY_PRIVATE_SLOT,
ehsan@140595
   167
              "js::PROXY_PRIVATE_SLOT doesn't match DOM_OBJECT_SLOT.  "
ehsan@140595
   168
              "Expect bad things");
peterv@90770
   169
template <class T>
peterv@90770
   170
inline T*
bzbarsky@115238
   171
UnwrapDOMObject(JSObject* obj)
peterv@90770
   172
{
efaustbmo@166510
   173
  MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
peterv@103142
   174
             "Don't pass non-DOM objects to this function");
ryanvm@96331
   175
bzbarsky@115238
   176
  JS::Value val = js::GetReservedSlot(obj, DOM_OBJECT_SLOT);
peterv@90770
   177
  return static_cast<T*>(val.toPrivate());
peterv@90770
   178
}
peterv@90770
   179
peterv@103142
   180
inline const DOMClass*
peterv@103142
   181
GetDOMClass(JSObject* obj)
peterv@103142
   182
{
sunfish@146555
   183
  const js::Class* clasp = js::GetObjectClass(obj);
peterv@103142
   184
  if (IsDOMClass(clasp)) {
peterv@103142
   185
    return &DOMJSClass::FromJSClass(clasp)->mClass;
peterv@103142
   186
  }
bzbarsky@115238
   187
  return nullptr;
peterv@103142
   188
}
peterv@103142
   189
ms2ger@133647
   190
inline nsISupports*
ms2ger@133647
   191
UnwrapDOMObjectToISupports(JSObject* aObject)
peterv@103142
   192
{
ms2ger@133647
   193
  const DOMClass* clasp = GetDOMClass(aObject);
bzbarsky@115238
   194
  if (!clasp || !clasp->mDOMObjectIsISupports) {
ms2ger@133647
   195
    return nullptr;
peterv@103142
   196
  }
peterv@103142
   197
 
ms2ger@133647
   198
  return UnwrapDOMObject<nsISupports>(aObject);
peterv@103142
   199
}
peterv@103142
   200
peterv@103142
   201
inline bool
peterv@103142
   202
IsDOMObject(JSObject* obj)
peterv@103142
   203
{
efaustbmo@166510
   204
  return IsDOMClass(js::GetObjectClass(obj));
peterv@103142
   205
}
peterv@103142
   206
bzbarsky@156810
   207
#define UNWRAP_OBJECT(Interface, obj, value)                                 \
peterv@145934
   208
  mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
bzbarsky@156809
   209
    mozilla::dom::Interface##Binding::NativeType>(obj, value)
bzbarsky@144214
   210
bzbarsky@106905
   211
// Some callers don't want to set an exception when unwrapping fails
peterv@90770
   212
// (for example, overload resolution uses unwrapping to tell what sort
peterv@90770
   213
// of thing it's looking at).
bzbarsky@94815
   214
// U must be something that a T* can be assigned to (e.g. T* or an nsRefPtr<T>).
bzbarsky@167663
   215
template <class T, typename U>
peterv@114348
   216
MOZ_ALWAYS_INLINE nsresult
bzbarsky@167663
   217
UnwrapObject(JSObject* obj, U& value, prototypes::ID protoID,
bzbarsky@167663
   218
             uint32_t protoDepth)
peterv@90770
   219
{
peterv@90770
   220
  /* First check to see whether we have a DOM object */
bzbarsky@115238
   221
  const DOMClass* domClass = GetDOMClass(obj);
bzbarsky@115238
   222
  if (!domClass) {
peterv@90770
   223
    /* Maybe we have a security wrapper or outer window? */
peterv@90770
   224
    if (!js::IsWrapper(obj)) {
peterv@90770
   225
      /* Not a DOM object, not a wrapper, just bail */
peterv@90770
   226
      return NS_ERROR_XPC_BAD_CONVERT_JS;
peterv@90770
   227
    }
peterv@90770
   228
maligree@128491
   229
    obj = js::CheckedUnwrap(obj, /* stopAtOuter = */ false);
peterv@90770
   230
    if (!obj) {
peterv@90770
   231
      return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
peterv@90770
   232
    }
peterv@90770
   233
    MOZ_ASSERT(!js::IsWrapper(obj));
bzbarsky@115238
   234
    domClass = GetDOMClass(obj);
bzbarsky@115238
   235
    if (!domClass) {
peterv@90770
   236
      /* We don't have a DOM object */
peterv@90770
   237
      return NS_ERROR_XPC_BAD_CONVERT_JS;
peterv@90770
   238
    }
peterv@90770
   239
  }
peterv@90770
   240
peterv@90770
   241
  /* This object is a DOM object.  Double-check that it is safely
peterv@90770
   242
     castable to T by checking whether it claims to inherit from the
peterv@90770
   243
     class identified by protoID. */
bzbarsky@167663
   244
  if (domClass->mInterfaceChain[protoDepth] == protoID) {
bzbarsky@115238
   245
    value = UnwrapDOMObject<T>(obj);
peterv@90770
   246
    return NS_OK;
peterv@90770
   247
  }
peterv@90770
   248
peterv@90770
   249
  /* It's the wrong sort of DOM object */
peterv@90770
   250
  return NS_ERROR_XPC_BAD_CONVERT_JS;
peterv@90770
   251
}
peterv@90770
   252
bzbarsky@167663
   253
template <prototypes::ID PrototypeID, class T, typename U>
bzbarsky@167663
   254
MOZ_ALWAYS_INLINE nsresult
bzbarsky@167663
   255
UnwrapObject(JSObject* obj, U& value)
bzbarsky@167663
   256
{
bzbarsky@167663
   257
  return UnwrapObject<T>(obj, value, PrototypeID,
bzbarsky@167663
   258
                         PrototypeTraits<PrototypeID>::Depth);
bzbarsky@167663
   259
}
bzbarsky@167663
   260
peterv@90770
   261
inline bool
bzbarsky@132049
   262
IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj)
peterv@90770
   263
{
peterv@90770
   264
  MOZ_ASSERT(obj);
bzbarsky@117408
   265
  return !JS_ObjectIsDate(cx, obj) && !JS_ObjectIsRegExp(cx, obj);
peterv@90770
   266
}
peterv@90770
   267
bzbarsky@117476
   268
MOZ_ALWAYS_INLINE bool
bzbarsky@132049
   269
IsObjectValueConvertibleToDictionary(JSContext* cx,
bzbarsky@132049
   270
                                     JS::Handle<JS::Value> objVal)
bzbarsky@117476
   271
{
bzbarsky@132049
   272
  JS::Rooted<JSObject*> obj(cx, &objVal.toObject());
bzbarsky@117476
   273
  return IsNotDateOrRegExp(cx, obj);
bzbarsky@117476
   274
}
bzbarsky@117476
   275
bzbarsky@117476
   276
MOZ_ALWAYS_INLINE bool
bzbarsky@132049
   277
IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val)
bzbarsky@117476
   278
{
bzbarsky@117476
   279
  return val.isNullOrUndefined() ||
bzbarsky@132049
   280
    (val.isObject() && IsObjectValueConvertibleToDictionary(cx, val));
bzbarsky@117476
   281
}
bzbarsky@117476
   282
bzbarsky@117478
   283
MOZ_ALWAYS_INLINE bool
bzbarsky@132049
   284
IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj)
bzbarsky@117478
   285
{
bzbarsky@117478
   286
  return IsNotDateOrRegExp(cx, obj);
bzbarsky@117478
   287
}
bzbarsky@117478
   288
froydnj@175558
   289
// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID and
peterv@111695
   290
// constructors::id::ID enums, in that order. The end of the prototype objects
peterv@111695
   291
// should be the start of the interface objects.
ehsan@140595
   292
static_assert((size_t)constructors::id::_ID_Start ==
ehsan@140595
   293
              (size_t)prototypes::id::_ID_Count,
ehsan@140595
   294
              "Overlapping or discontiguous indexes.");
peterv@111695
   295
const size_t kProtoAndIfaceCacheCount = constructors::id::_ID_Count;
peterv@90770
   296
froydnj@175558
   297
class ProtoAndIfaceCache
froydnj@156565
   298
{
froydnj@175557
   299
  // The caching strategy we use depends on what sort of global we're dealing
froydnj@175557
   300
  // with.  For a window-like global, we want everything to be as fast as
froydnj@175557
   301
  // possible, so we use a flat array, indexed by prototype/constructor ID.
froydnj@175557
   302
  // For everything else (e.g. globals for JSMs), space is more important than
froydnj@175557
   303
  // speed, so we use a two-level lookup table.
froydnj@175557
   304
froydnj@175557
   305
  class ArrayCache : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount>
froydnj@175557
   306
  {
froydnj@175557
   307
  public:
froydnj@175557
   308
    JSObject* EntrySlotIfExists(size_t i) {
froydnj@175557
   309
      return (*this)[i];
froydnj@175557
   310
    }
froydnj@175557
   311
froydnj@175557
   312
    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
froydnj@175557
   313
      return (*this)[i];
froydnj@175557
   314
    }
froydnj@175557
   315
froydnj@175557
   316
    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
froydnj@175557
   317
      MOZ_ASSERT((*this)[i]);
froydnj@175557
   318
      return (*this)[i];
froydnj@175557
   319
    }
froydnj@175557
   320
froydnj@175557
   321
    void Trace(JSTracer* aTracer) {
froydnj@175557
   322
      for (size_t i = 0; i < ArrayLength(*this); ++i) {
froydnj@175557
   323
        if ((*this)[i]) {
froydnj@175558
   324
          JS_CallHeapObjectTracer(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
froydnj@175557
   325
        }
froydnj@175557
   326
      }
froydnj@175557
   327
    }
froydnj@175557
   328
froydnj@175557
   329
    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
froydnj@175557
   330
      return aMallocSizeOf(this);
froydnj@175557
   331
    }
froydnj@175557
   332
  };
froydnj@175557
   333
froydnj@175557
   334
  class PageTableCache
froydnj@175557
   335
  {
froydnj@175557
   336
  public:
froydnj@175557
   337
    PageTableCache() {
froydnj@175557
   338
      memset(&mPages, 0, sizeof(mPages));
froydnj@175557
   339
    }
froydnj@175557
   340
froydnj@175557
   341
    ~PageTableCache() {
froydnj@175557
   342
      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
froydnj@175557
   343
        delete mPages[i];
froydnj@175557
   344
      }
froydnj@175557
   345
    }
froydnj@175557
   346
froydnj@175557
   347
    JSObject* EntrySlotIfExists(size_t i) {
froydnj@175557
   348
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
froydnj@175557
   349
      size_t pageIndex = i / kPageSize;
froydnj@175557
   350
      size_t leafIndex = i % kPageSize;
froydnj@175557
   351
      Page* p = mPages[pageIndex];
froydnj@175557
   352
      if (!p) {
froydnj@175557
   353
        return nullptr;
froydnj@175557
   354
      }
froydnj@175557
   355
      return (*p)[leafIndex];
froydnj@175557
   356
    }
froydnj@175557
   357
froydnj@175557
   358
    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
froydnj@175557
   359
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
froydnj@175557
   360
      size_t pageIndex = i / kPageSize;
froydnj@175557
   361
      size_t leafIndex = i % kPageSize;
froydnj@175557
   362
      Page* p = mPages[pageIndex];
froydnj@175557
   363
      if (!p) {
froydnj@175557
   364
        p = new Page;
froydnj@175557
   365
        mPages[pageIndex] = p;
froydnj@175557
   366
      }
froydnj@175557
   367
      return (*p)[leafIndex];
froydnj@175557
   368
    }
froydnj@175557
   369
froydnj@175557
   370
    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
froydnj@175557
   371
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
froydnj@175557
   372
      size_t pageIndex = i / kPageSize;
froydnj@175557
   373
      size_t leafIndex = i % kPageSize;
froydnj@175557
   374
      Page* p = mPages[pageIndex];
froydnj@175557
   375
      MOZ_ASSERT(p);
froydnj@175557
   376
      return (*p)[leafIndex];
froydnj@175557
   377
    }
froydnj@175557
   378
froydnj@175557
   379
    void Trace(JSTracer* trc) {
froydnj@175557
   380
      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
froydnj@175557
   381
        Page* p = mPages[i];
froydnj@175557
   382
        if (p) {
froydnj@175557
   383
          for (size_t j = 0; j < ArrayLength(*p); ++j) {
froydnj@175557
   384
            if ((*p)[j]) {
froydnj@175558
   385
              JS_CallHeapObjectTracer(trc, &(*p)[j], "protoAndIfaceCache[i]");
froydnj@175557
   386
            }
froydnj@175557
   387
          }
froydnj@175557
   388
        }
froydnj@175557
   389
      }
froydnj@175557
   390
    }
froydnj@175557
   391
froydnj@175557
   392
    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
froydnj@175557
   393
      size_t n = aMallocSizeOf(this);
froydnj@175557
   394
      for (size_t i = 0; i < ArrayLength(mPages); ++i) {
froydnj@175557
   395
        n += aMallocSizeOf(mPages[i]);
froydnj@175557
   396
      }
froydnj@175557
   397
      return n;
froydnj@175557
   398
    }
froydnj@175557
   399
froydnj@175557
   400
  private:
froydnj@175557
   401
    static const size_t kPageSize = 16;
froydnj@175557
   402
    typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
froydnj@175557
   403
    static const size_t kNPages = kProtoAndIfaceCacheCount / kPageSize +
froydnj@175557
   404
      size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
froydnj@175557
   405
    Array<Page*, kNPages> mPages;
froydnj@175557
   406
  };
froydnj@175557
   407
froydnj@156565
   408
public:
froydnj@175557
   409
  enum Kind {
froydnj@175557
   410
    WindowLike,
froydnj@175557
   411
    NonWindowLike
froydnj@175557
   412
  };
froydnj@175557
   413
froydnj@175558
   414
  ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
froydnj@175558
   415
    MOZ_COUNT_CTOR(ProtoAndIfaceCache);
froydnj@175557
   416
    if (aKind == WindowLike) {
froydnj@175557
   417
      mArrayCache = new ArrayCache();
froydnj@175557
   418
    } else {
froydnj@175557
   419
      mPageTableCache = new PageTableCache();
froydnj@175557
   420
    }
froydnj@156565
   421
  }
froydnj@156565
   422
froydnj@175558
   423
  ~ProtoAndIfaceCache() {
froydnj@175557
   424
    if (mKind == WindowLike) {
froydnj@175557
   425
      delete mArrayCache;
froydnj@175557
   426
    } else {
froydnj@175557
   427
      delete mPageTableCache;
froydnj@175557
   428
    }
froydnj@175558
   429
    MOZ_COUNT_DTOR(ProtoAndIfaceCache);
froydnj@156565
   430
  }
froydnj@159072
   431
froydnj@175557
   432
#define FORWARD_OPERATION(opName, args)              \
froydnj@175557
   433
  do {                                               \
froydnj@175557
   434
    if (mKind == WindowLike) {                       \
froydnj@175557
   435
      return mArrayCache->opName args;               \
froydnj@175557
   436
    } else {                                         \
froydnj@175557
   437
      return mPageTableCache->opName args;           \
froydnj@175557
   438
    }                                                \
froydnj@175557
   439
  } while(0)
froydnj@175557
   440
froydnj@175557
   441
  JSObject* EntrySlotIfExists(size_t i) {
froydnj@175557
   442
    FORWARD_OPERATION(EntrySlotIfExists, (i));
froydnj@175557
   443
  }
froydnj@175557
   444
froydnj@175557
   445
  JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
froydnj@175557
   446
    FORWARD_OPERATION(EntrySlotOrCreate, (i));
froydnj@175557
   447
  }
froydnj@175557
   448
froydnj@175557
   449
  JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
froydnj@175557
   450
    FORWARD_OPERATION(EntrySlotMustExist, (i));
froydnj@175557
   451
  }
froydnj@175557
   452
froydnj@175557
   453
  void Trace(JSTracer *aTracer) {
froydnj@175557
   454
    FORWARD_OPERATION(Trace, (aTracer));
froydnj@175557
   455
  }
froydnj@175557
   456
froydnj@159072
   457
  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
froydnj@175557
   458
    size_t n = aMallocSizeOf(this);
froydnj@175557
   459
    n += (mKind == WindowLike
froydnj@175557
   460
          ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
froydnj@175557
   461
          : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
froydnj@175557
   462
    return n;
froydnj@159072
   463
  }
froydnj@175557
   464
#undef FORWARD_OPERATION
froydnj@175557
   465
froydnj@175557
   466
private:
froydnj@175557
   467
  union {
froydnj@175557
   468
    ArrayCache *mArrayCache;
froydnj@175557
   469
    PageTableCache *mPageTableCache;
froydnj@175557
   470
  };
froydnj@175557
   471
  Kind mKind;
froydnj@156565
   472
};
froydnj@156565
   473
peterv@90770
   474
inline void
froydnj@175558
   475
AllocateProtoAndIfaceCache(JSObject* obj, ProtoAndIfaceCache::Kind aKind)
peterv@90770
   476
{
peterv@90770
   477
  MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
peterv@90770
   478
  MOZ_ASSERT(js::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
peterv@90770
   479
froydnj@175558
   480
  ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
peterv@90770
   481
peterv@90770
   482
  js::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
froydnj@175558
   483
                      JS::PrivateValue(protoAndIfaceCache));
peterv@90770
   484
}
peterv@90770
   485
peterv@90770
   486
inline void
peterv@111667
   487
TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj)
peterv@92998
   488
{
peterv@92998
   489
  MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
peterv@92998
   490
froydnj@175558
   491
  if (!HasProtoAndIfaceCache(obj))
wmccloskey@96975
   492
    return;
froydnj@175558
   493
  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
froydnj@175558
   494
  protoAndIfaceCache->Trace(trc);
peterv@92998
   495
}
peterv@92998
   496
peterv@92998
   497
inline void
peterv@111667
   498
DestroyProtoAndIfaceCache(JSObject* obj)
peterv@90770
   499
{
peterv@90770
   500
  MOZ_ASSERT(js::GetObjectClass(obj)->flags & JSCLASS_DOM_GLOBAL);
peterv@90770
   501
froydnj@175558
   502
  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
peterv@90770
   503
froydnj@175558
   504
  delete protoAndIfaceCache;
peterv@90770
   505
}
peterv@90770
   506
dteller@93650
   507
/**
dteller@93650
   508
 * Add constants to an object.
dteller@93650
   509
 */
dteller@93650
   510
bool
bzbarsky@131211
   511
DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
bzbarsky@131211
   512
                const ConstantSpec* cs);
dteller@93650
   513
peterv@111695
   514
struct JSNativeHolder
peterv@111695
   515
{
peterv@111695
   516
  JSNative mNative;
peterv@111695
   517
  const NativePropertyHooks* mPropertyHooks;
peterv@111695
   518
};
peterv@111695
   519
peterv@122641
   520
struct NamedConstructor
peterv@122641
   521
{
peterv@122641
   522
  const char* mName;
peterv@122641
   523
  const JSNativeHolder mHolder;
peterv@122641
   524
  unsigned mNargs;
peterv@122641
   525
};
peterv@122641
   526
peterv@90770
   527
/*
peterv@90770
   528
 * Create a DOM interface object (if constructorClass is non-null) and/or a
peterv@90770
   529
 * DOM interface prototype object (if protoClass is non-null).
peterv@90770
   530
 *
peterv@92619
   531
 * global is used as the parent of the interface object and the interface
peterv@92619
   532
 *        prototype object
peterv@92619
   533
 * protoProto is the prototype to use for the interface prototype object.
bzbarsky@130640
   534
 * interfaceProto is the prototype to use for the interface object.
peterv@90770
   535
 * protoClass is the JSClass to use for the interface prototype object.
peterv@90770
   536
 *            This is null if we should not create an interface prototype
peterv@90770
   537
 *            object.
peterv@111695
   538
 * protoCache a pointer to a JSObject pointer where we should cache the
peterv@111695
   539
 *            interface prototype object. This must be null if protoClass is and
peterv@111695
   540
 *            vice versa.
peterv@90770
   541
 * constructorClass is the JSClass to use for the interface object.
bzbarsky@92915
   542
 *                  This is null if we should not create an interface object or
bzbarsky@92915
   543
 *                  if it should be a function object.
peterv@111695
   544
 * constructor holds the JSNative to back the interface object which should be a
peterv@111695
   545
 *             Function, unless constructorClass is non-null in which case it is
peterv@111695
   546
 *             ignored. If this is null and constructorClass is also null then
peterv@111695
   547
 *             we should not create an interface object at all.
bzbarsky@92915
   548
 * ctorNargs is the length of the constructor function; 0 if no constructor
peterv@111695
   549
 * constructorCache a pointer to a JSObject pointer where we should cache the
peterv@111695
   550
 *                  interface object. This must be null if both constructorClass
peterv@111695
   551
 *                  and constructor are null, and non-null otherwise.
peterv@110224
   552
 * domClass is the DOMClass of instance objects for this class.  This can be
peterv@110224
   553
 *          null if this is not a concrete proto.
peterv@110224
   554
 * properties contains the methods, attributes and constants to be defined on
peterv@110224
   555
 *            objects in any compartment.
peterv@110224
   556
 * chromeProperties contains the methods, attributes and constants to be defined
peterv@110224
   557
 *                  on objects in chrome compartments. This must be null if the
peterv@110224
   558
 *                  interface doesn't have any ChromeOnly properties or if the
peterv@110224
   559
 *                  object is being created in non-chrome compartment.
bzbarsky@141794
   560
 * defineOnGlobal controls whether properties should be defined on the given
bzbarsky@141794
   561
 *                global for the interface object (if any) and named
bzbarsky@141794
   562
 *                constructors (if any) for this interface.  This can be
bzbarsky@141794
   563
 *                false in situations where we want the properties to only
bzbarsky@141794
   564
 *                appear on privileged Xrays but not on the unprivileged
bzbarsky@141794
   565
 *                underlying global.
peterv@90770
   566
 *
peterv@111695
   567
 * At least one of protoClass, constructorClass or constructor should be
peterv@111695
   568
 * non-null. If constructorClass or constructor are non-null, the resulting
peterv@111695
   569
 * interface object will be defined on the given global with property name
peterv@111695
   570
 * |name|, which must also be non-null.
peterv@90770
   571
 */
peterv@111695
   572
void
bzbarsky@130575
   573
CreateInterfaceObjects(JSContext* cx, JS::Handle<JSObject*> global,
bzbarsky@130574
   574
                       JS::Handle<JSObject*> protoProto,
sunfish@146555
   575
                       const JSClass* protoClass, JS::Heap<JSObject*>* protoCache,
bzbarsky@130640
   576
                       JS::Handle<JSObject*> interfaceProto,
sunfish@146555
   577
                       const JSClass* constructorClass, const JSNativeHolder* constructor,
peterv@122641
   578
                       unsigned ctorNargs, const NamedConstructor* namedConstructors,
jcoppeard@139581
   579
                       JS::Heap<JSObject*>* constructorCache, const DOMClass* domClass,
peterv@111695
   580
                       const NativeProperties* regularProperties,
peterv@111695
   581
                       const NativeProperties* chromeOnlyProperties,
bzbarsky@141794
   582
                       const char* name, bool defineOnGlobal);
peterv@90770
   583
bzbarsky@111284
   584
/*
bzbarsky@111284
   585
 * Define the unforgeable attributes on an object.
bzbarsky@111284
   586
 */
bzbarsky@111284
   587
bool
bzbarsky@130812
   588
DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
bzbarsky@127005
   589
                            const Prefable<const JSPropertySpec>* props);
bzbarsky@111284
   590
peterv@114001
   591
bool
peterv@153617
   592
DefineWebIDLBindingPropertiesOnXPCObject(JSContext* cx,
peterv@153617
   593
                                         JS::Handle<JSObject*> obj,
peterv@153617
   594
                                         const NativeProperties* properties,
peterv@153617
   595
                                         bool defineUnforgeableAttributes);
peterv@114001
   596
bzbarsky@112790
   597
#ifdef _MSC_VER
bzbarsky@112790
   598
#define HAS_MEMBER_CHECK(_name)                                           \
bzbarsky@112790
   599
  template<typename V> static yes& Check(char (*)[(&V::_name == 0) + 1])
bzbarsky@112790
   600
#else
bzbarsky@112790
   601
#define HAS_MEMBER_CHECK(_name)                                           \
bzbarsky@112790
   602
  template<typename V> static yes& Check(char (*)[sizeof(&V::_name) + 1])
bzbarsky@112790
   603
#endif
bzbarsky@112790
   604
bzbarsky@112790
   605
#define HAS_MEMBER(_name)                                                 \
bzbarsky@112790
   606
template<typename T>                                                      \
bzbarsky@112790
   607
class Has##_name##Member {                                                \
bzbarsky@112790
   608
  typedef char yes[1];                                                    \
bzbarsky@112790
   609
  typedef char no[2];                                                     \
bzbarsky@112790
   610
  HAS_MEMBER_CHECK(_name);                                                \
bzbarsky@112790
   611
  template<typename V> static no& Check(...);                             \
bzbarsky@112790
   612
                                                                          \
bzbarsky@112790
   613
public:                                                                   \
bzbarsky@112790
   614
  static bool const Value = sizeof(Check<T>(nullptr)) == sizeof(yes);     \
bzbarsky@112790
   615
};
bzbarsky@112790
   616
bzbarsky@112790
   617
HAS_MEMBER(WrapObject)
bzbarsky@112790
   618
bzbarsky@112790
   619
// HasWrapObject<T>::Value will be true if T has a WrapObject member but it's
bzbarsky@112790
   620
// not nsWrapperCache::WrapObject.
bzbarsky@112790
   621
template<typename T>
bzbarsky@112790
   622
struct HasWrapObject
bzbarsky@112790
   623
{
bzbarsky@112790
   624
private:
bzbarsky@112790
   625
  typedef char yes[1];
bzbarsky@112790
   626
  typedef char no[2];
bzbarsky@129891
   627
  typedef JSObject* (nsWrapperCache::*WrapObject)(JSContext*,
bzbarsky@129891
   628
                                                  JS::Handle<JSObject*>);
bzbarsky@112790
   629
  template<typename U, U> struct SFINAE;
bzbarsky@112790
   630
  template <typename V> static no& Check(SFINAE<WrapObject, &V::WrapObject>*);
bzbarsky@112790
   631
  template <typename V> static yes& Check(...);
bzbarsky@112790
   632
bzbarsky@112790
   633
public:
bzbarsky@112790
   634
  static bool const Value = HasWrapObjectMember<T>::Value &&
bzbarsky@112790
   635
                            sizeof(Check<T>(nullptr)) == sizeof(yes);
bzbarsky@112790
   636
};
bzbarsky@112790
   637
peterv@114000
   638
#ifdef DEBUG
peterv@140097
   639
template <class T, bool isISupports=IsBaseOf<nsISupports, T>::value>
peterv@114000
   640
struct
peterv@114000
   641
CheckWrapperCacheCast
peterv@114000
   642
{
peterv@114000
   643
  static bool Check()
peterv@114000
   644
  {
peterv@114000
   645
    return reinterpret_cast<uintptr_t>(
peterv@114000
   646
      static_cast<nsWrapperCache*>(
peterv@114000
   647
        reinterpret_cast<T*>(1))) == 1;
peterv@114000
   648
  }
peterv@114000
   649
};
peterv@114000
   650
template <class T>
peterv@114000
   651
struct
peterv@114000
   652
CheckWrapperCacheCast<T, true>
peterv@114000
   653
{
peterv@114000
   654
  static bool Check()
peterv@114000
   655
  {
peterv@114000
   656
    return true;
peterv@114000
   657
  }
peterv@114000
   658
};
peterv@114000
   659
#endif
peterv@114000
   660
peterv@114346
   661
MOZ_ALWAYS_INLINE bool
peterv@114346
   662
CouldBeDOMBinding(void*)
peterv@114346
   663
{
peterv@114346
   664
  return true;
peterv@114346
   665
}
peterv@114346
   666
peterv@114346
   667
MOZ_ALWAYS_INLINE bool
peterv@114346
   668
CouldBeDOMBinding(nsWrapperCache* aCache)
peterv@114346
   669
{
peterv@114346
   670
  return aCache->IsDOMBinding();
peterv@114346
   671
}
peterv@114346
   672
peterv@176195
   673
inline bool
peterv@176195
   674
TryToOuterize(JSContext* cx, JS::MutableHandle<JS::Value> rval)
peterv@176195
   675
{
peterv@176195
   676
  if (js::IsInnerObject(&rval.toObject())) {
peterv@176195
   677
    JS::Rooted<JSObject*> obj(cx, &rval.toObject());
peterv@176195
   678
    obj = JS_ObjectToOuterObject(cx, obj);
peterv@176195
   679
    if (!obj) {
peterv@176195
   680
      return false;
peterv@176195
   681
    }
peterv@176195
   682
peterv@176195
   683
    rval.set(JS::ObjectValue(*obj));
peterv@176195
   684
  }
peterv@176195
   685
peterv@176195
   686
  return true;
peterv@176195
   687
}
peterv@176195
   688
bzbarsky@138186
   689
// Make sure to wrap the given string value into the right compartment, as
bzbarsky@138186
   690
// needed.
bzbarsky@138186
   691
MOZ_ALWAYS_INLINE
bzbarsky@138186
   692
bool
bzbarsky@138186
   693
MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@117411
   694
{
bzbarsky@138186
   695
  MOZ_ASSERT(rval.isString());
bzbarsky@138186
   696
  JSString* str = rval.toString();
bzbarsky@138186
   697
  if (JS::GetGCThingZone(str) != js::GetContextZone(cx)) {
evilpies@152347
   698
    return JS_WrapValue(cx, rval);
wmccloskey@125084
   699
  }
bzbarsky@138186
   700
  return true;
bzbarsky@138186
   701
}
wmccloskey@125084
   702
bzbarsky@138186
   703
// Make sure to wrap the given object value into the right compartment as
bzbarsky@138186
   704
// needed.  This will work correctly, but possibly slowly, on all objects.
bzbarsky@138186
   705
MOZ_ALWAYS_INLINE
bzbarsky@138186
   706
bool
bzbarsky@138186
   707
MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@138186
   708
{
bzbarsky@138186
   709
  MOZ_ASSERT(rval.isObject());
bzbarsky@117411
   710
bobbyholley@175514
   711
  // Cross-compartment always requires wrapping.
peterv@135082
   712
  JSObject* obj = &rval.toObject();
peterv@135082
   713
  if (js::GetObjectCompartment(obj) != js::GetContextCompartment(cx)) {
evilpies@152347
   714
    return JS_WrapValue(cx, rval);
peterv@135082
   715
  }
peterv@135082
   716
peterv@176195
   717
  // We're same-compartment, but even then we might need to wrap
peterv@176195
   718
  // objects specially.  Check for that.
bobbyholley@175514
   719
  if (IsDOMObject(obj)) {
peterv@176195
   720
    return TryToOuterize(cx, rval);
peterv@135082
   721
  }
peterv@135082
   722
bzbarsky@138186
   723
  // It's not a WebIDL object.  But it might be an XPConnect one, in which case
bzbarsky@138186
   724
  // we may need to outerize here, so make sure to call JS_WrapValue.
evilpies@152347
   725
  return JS_WrapValue(cx, rval);
bzbarsky@117411
   726
}
bzbarsky@117411
   727
bzbarsky@138186
   728
// Like MaybeWrapObjectValue, but also allows null
bzbarsky@138186
   729
MOZ_ALWAYS_INLINE
bzbarsky@138186
   730
bool
bzbarsky@138186
   731
MaybeWrapObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@138186
   732
{
bzbarsky@138186
   733
  MOZ_ASSERT(rval.isObjectOrNull());
bzbarsky@138186
   734
  if (rval.isNull()) {
bzbarsky@138186
   735
    return true;
bzbarsky@138186
   736
  }
bzbarsky@138186
   737
  return MaybeWrapObjectValue(cx, rval);
bzbarsky@138186
   738
}
bzbarsky@138186
   739
bzbarsky@138186
   740
// Wrapping for objects that are known to not be DOM or XPConnect objects
bzbarsky@138186
   741
MOZ_ALWAYS_INLINE
bzbarsky@138186
   742
bool
bzbarsky@138186
   743
MaybeWrapNonDOMObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@138186
   744
{
bzbarsky@138186
   745
  MOZ_ASSERT(rval.isObject());
bzbarsky@138186
   746
  MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
bzbarsky@138186
   747
  MOZ_ASSERT(!(js::GetObjectClass(&rval.toObject())->flags &
bzbarsky@138186
   748
               JSCLASS_PRIVATE_IS_NSISUPPORTS));
bzbarsky@138186
   749
bzbarsky@138186
   750
  JSObject* obj = &rval.toObject();
bzbarsky@138186
   751
  if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
bzbarsky@138186
   752
    return true;
bzbarsky@138186
   753
  }
evilpies@152347
   754
  return JS_WrapValue(cx, rval);
bzbarsky@138186
   755
}
bzbarsky@138186
   756
bzbarsky@138186
   757
// Like MaybeWrapNonDOMObjectValue but allows null
bzbarsky@138186
   758
MOZ_ALWAYS_INLINE
bzbarsky@138186
   759
bool
bzbarsky@138186
   760
MaybeWrapNonDOMObjectOrNullValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@138186
   761
{
bzbarsky@138186
   762
  MOZ_ASSERT(rval.isObjectOrNull());
bzbarsky@138186
   763
  if (rval.isNull()) {
bzbarsky@138186
   764
    return true;
bzbarsky@138186
   765
  }
bzbarsky@138186
   766
  return MaybeWrapNonDOMObjectValue(cx, rval);
bzbarsky@138186
   767
}
bzbarsky@138186
   768
bzbarsky@138186
   769
// If rval is a gcthing and is not in the compartment of cx, wrap rval
bzbarsky@138186
   770
// into the compartment of cx (typically by replacing it with an Xray or
bzbarsky@138186
   771
// cross-compartment wrapper around the original object).
bzbarsky@138186
   772
MOZ_ALWAYS_INLINE bool
bzbarsky@138186
   773
MaybeWrapValue(JSContext* cx, JS::MutableHandle<JS::Value> rval)
bzbarsky@138186
   774
{
bzbarsky@138186
   775
  if (rval.isString()) {
bzbarsky@138186
   776
    return MaybeWrapStringValue(cx, rval);
bzbarsky@138186
   777
  }
bzbarsky@138186
   778
bzbarsky@138186
   779
  if (!rval.isObject()) {
bzbarsky@138186
   780
    return true;
bzbarsky@138186
   781
  }
bzbarsky@138186
   782
bzbarsky@138186
   783
  return MaybeWrapObjectValue(cx, rval);
bzbarsky@138186
   784
}
bzbarsky@138186
   785
peterv@114346
   786
// Create a JSObject wrapping "value", if there isn't one already, and store it
bzbarsky@134439
   787
// in rval.  "value" must be a concrete class that implements a
peterv@114346
   788
// GetWrapperPreserveColor() which can return its existing wrapper, if any, and
peterv@114346
   789
// a WrapObject() which will try to create a wrapper. Typically, this is done by
peterv@114346
   790
// having "value" inherit from nsWrapperCache.
peterv@90770
   791
template <class T>
bzbarsky@113792
   792
MOZ_ALWAYS_INLINE bool
bzbarsky@177629
   793
WrapNewBindingObject(JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval)
peterv@90770
   794
{
bzbarsky@129789
   795
  MOZ_ASSERT(value);
bzbarsky@113791
   796
  JSObject* obj = value->GetWrapperPreserveColor();
bobbyholley@174333
   797
  // We can get rid of this when we remove support for hasXPConnectImpls.
peterv@116779
   798
  bool couldBeDOMBinding = CouldBeDOMBinding(value);
bzbarsky@113791
   799
  if (obj) {
khuey@146107
   800
    JS::ExposeObjectToActiveJS(obj);
peterv@114346
   801
  } else {
peterv@114346
   802
    // Inline this here while we have non-dom objects in wrapper caches.
peterv@116779
   803
    if (!couldBeDOMBinding) {
peterv@114346
   804
      return false;
peterv@114346
   805
    }
peterv@90770
   806
bzbarsky@177628
   807
    obj = value->WrapObject(cx);
peterv@90770
   808
    if (!obj) {
VYV03354@124439
   809
      // At this point, obj is null, so just return false.
VYV03354@124439
   810
      // Callers seem to be testing JS_IsExceptionPending(cx) to
VYV03354@124439
   811
      // figure out whether WrapObject() threw.
peterv@90770
   812
      return false;
peterv@90770
   813
    }
peterv@90770
   814
  }
peterv@90770
   815
bzbarsky@112790
   816
#ifdef DEBUG
bzbarsky@115238
   817
  const DOMClass* clasp = GetDOMClass(obj);
bzbarsky@177629
   818
  // clasp can be null if the cache contained a non-DOM object.
bzbarsky@115238
   819
  if (clasp) {
peterv@114000
   820
    // Some sanity asserts about our object.  Specifically:
peterv@114000
   821
    // 1)  If our class claims we're nsISupports, we better be nsISupports
peterv@114000
   822
    //     XXXbz ideally, we could assert that reinterpret_cast to nsISupports
peterv@114000
   823
    //     does the right thing, but I don't see a way to do it.  :(
peterv@114000
   824
    // 2)  If our class doesn't claim we're nsISupports we better be
peterv@114000
   825
    //     reinterpret_castable to nsWrapperCache.
peterv@114000
   826
    MOZ_ASSERT(clasp, "What happened here?");
peterv@140097
   827
    MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports, (IsBaseOf<nsISupports, T>::value));
peterv@114000
   828
    MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
peterv@114000
   829
  }
peterv@116779
   830
#endif
peterv@116779
   831
peterv@176195
   832
  rval.set(JS::ObjectValue(*obj));
peterv@176195
   833
peterv@116779
   834
  bool sameCompartment =
peterv@116779
   835
    js::GetObjectCompartment(obj) == js::GetContextCompartment(cx);
peterv@116779
   836
  if (sameCompartment && couldBeDOMBinding) {
peterv@176195
   837
    // We only need to outerize Window objects, so anything inheriting from
peterv@176195
   838
    // nsGlobalWindow (which inherits from EventTarget itself).
peterv@176195
   839
    return IsBaseOf<nsGlobalWindow, T>::value || IsSame<EventTarget, T>::value ?
peterv@176195
   840
           TryToOuterize(cx, rval) : true;
peterv@116779
   841
  }
peterv@116779
   842
evilpies@152347
   843
  return JS_WrapValue(cx, rval);
peterv@90770
   844
}
peterv@90770
   845
peterv@114346
   846
// Create a JSObject wrapping "value", for cases when "value" is a
peterv@114346
   847
// non-wrapper-cached object using WebIDL bindings.  "value" must implement a
peterv@114346
   848
// WrapObject() method taking a JSContext and a scope.
bzbarsky@96857
   849
template <class T>
bzbarsky@96857
   850
inline bool
bzbarsky@129890
   851
WrapNewBindingNonWrapperCachedObject(JSContext* cx,
bzbarsky@129890
   852
                                     JS::Handle<JSObject*> scopeArg,
bzbarsky@134439
   853
                                     T* value,
bzbarsky@134439
   854
                                     JS::MutableHandle<JS::Value> rval)
bzbarsky@96857
   855
{
bzbarsky@129789
   856
  MOZ_ASSERT(value);
bzbarsky@96857
   857
  // We try to wrap in the compartment of the underlying object of "scope"
bzbarsky@130815
   858
  JS::Rooted<JSObject*> obj(cx);
bzbarsky@96857
   859
  {
luke@103263
   860
    // scope for the JSAutoCompartment so that we restore the compartment
luke@103263
   861
    // before we call JS_WrapValue.
luke@103263
   862
    Maybe<JSAutoCompartment> ac;
bzbarsky@129890
   863
    // Maybe<Handle> doesn't so much work, and in any case, adding
bzbarsky@129890
   864
    // more Maybe (one for a Rooted and one for a Handle) adds more
bzbarsky@129890
   865
    // code (and branches!) than just adding a single rooted.
bzbarsky@129890
   866
    JS::Rooted<JSObject*> scope(cx, scopeArg);
bzbarsky@96857
   867
    if (js::IsWrapper(scope)) {
maligree@128491
   868
      scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
luke@103263
   869
      if (!scope)
bzbarsky@96857
   870
        return false;
luke@103263
   871
      ac.construct(cx, scope);
bzbarsky@96857
   872
    }
bzbarsky@96857
   873
bzbarsky@177628
   874
    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
bzbarsky@177628
   875
    obj = value->WrapObject(cx);
bzbarsky@96857
   876
  }
bzbarsky@96857
   877
dzbarsky@129381
   878
  if (!obj) {
dzbarsky@129381
   879
    return false;
dzbarsky@129381
   880
  }
dzbarsky@129381
   881
bzbarsky@96857
   882
  // We can end up here in all sorts of compartments, per above.  Make
bzbarsky@96857
   883
  // sure to JS_WrapValue!
bzbarsky@134439
   884
  rval.set(JS::ObjectValue(*obj));
evilpies@152347
   885
  return JS_WrapValue(cx, rval);
bzbarsky@96857
   886
}
bzbarsky@96857
   887
dzbarsky@129308
   888
// Create a JSObject wrapping "value", for cases when "value" is a
dzbarsky@129308
   889
// non-wrapper-cached owned object using WebIDL bindings.  "value" must implement a
dzbarsky@129308
   890
// WrapObject() method taking a JSContext, a scope, and a boolean outparam that
dzbarsky@129308
   891
// is true if the JSObject took ownership
dzbarsky@129308
   892
template <class T>
dzbarsky@129308
   893
inline bool
bzbarsky@129890
   894
WrapNewBindingNonWrapperCachedOwnedObject(JSContext* cx,
bzbarsky@129890
   895
                                          JS::Handle<JSObject*> scopeArg,
bzbarsky@134439
   896
                                          nsAutoPtr<T>& value,
bzbarsky@134439
   897
                                          JS::MutableHandle<JS::Value> rval)
dzbarsky@129308
   898
{
bzbarsky@129789
   899
  // We do a runtime check on value, because otherwise we might in
bzbarsky@129789
   900
  // fact end up wrapping a null and invoking methods on it later.
bzbarsky@129789
   901
  if (!value) {
bzbarsky@129789
   902
    NS_RUNTIMEABORT("Don't try to wrap null objects");
bzbarsky@129789
   903
  }
dzbarsky@129308
   904
  // We try to wrap in the compartment of the underlying object of "scope"
bzbarsky@130815
   905
  JS::Rooted<JSObject*> obj(cx);
dzbarsky@129308
   906
  {
dzbarsky@129308
   907
    // scope for the JSAutoCompartment so that we restore the compartment
dzbarsky@129308
   908
    // before we call JS_WrapValue.
dzbarsky@129308
   909
    Maybe<JSAutoCompartment> ac;
bzbarsky@129890
   910
    // Maybe<Handle> doesn't so much work, and in any case, adding
bzbarsky@129890
   911
    // more Maybe (one for a Rooted and one for a Handle) adds more
bzbarsky@129890
   912
    // code (and branches!) than just adding a single rooted.
bzbarsky@129890
   913
    JS::Rooted<JSObject*> scope(cx, scopeArg);
dzbarsky@129308
   914
    if (js::IsWrapper(scope)) {
dzbarsky@129308
   915
      scope = js::CheckedUnwrap(scope, /* stopAtOuter = */ false);
dzbarsky@129308
   916
      if (!scope)
dzbarsky@129308
   917
        return false;
dzbarsky@129308
   918
      ac.construct(cx, scope);
dzbarsky@129308
   919
    }
dzbarsky@129308
   920
dzbarsky@129308
   921
    bool tookOwnership = false;
bzbarsky@177628
   922
    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
bzbarsky@177628
   923
    obj = value->WrapObject(cx, &tookOwnership);
bzbarsky@130815
   924
    MOZ_ASSERT_IF(obj, tookOwnership);
dzbarsky@129308
   925
    if (tookOwnership) {
dzbarsky@129308
   926
      value.forget();
dzbarsky@129308
   927
    }
dzbarsky@129308
   928
  }
dzbarsky@129308
   929
dzbarsky@129381
   930
  if (!obj) {
dzbarsky@129381
   931
    return false;
dzbarsky@129381
   932
  }
dzbarsky@129381
   933
dzbarsky@129308
   934
  // We can end up here in all sorts of compartments, per above.  Make
dzbarsky@129308
   935
  // sure to JS_WrapValue!
bzbarsky@134439
   936
  rval.set(JS::ObjectValue(*obj));
evilpies@152347
   937
  return JS_WrapValue(cx, rval);
dzbarsky@129308
   938
}
dzbarsky@129308
   939
bzbarsky@96857
   940
// Helper for smart pointers (nsAutoPtr/nsRefPtr/nsCOMPtr).
bzbarsky@96857
   941
template <template <typename> class SmartPtr, typename T>
bzbarsky@96857
   942
inline bool
bzbarsky@129890
   943
WrapNewBindingNonWrapperCachedObject(JSContext* cx, JS::Handle<JSObject*> scope,
bzbarsky@134439
   944
                                     const SmartPtr<T>& value,
bzbarsky@134439
   945
                                     JS::MutableHandle<JS::Value> rval)
bzbarsky@96857
   946
{
bzbarsky@134439
   947
  return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval);
bzbarsky@96857
   948
}
bzbarsky@96857
   949
peterv@114345
   950
// Only set allowNativeWrapper to false if you really know you need it, if in
peterv@114345
   951
// doubt use true. Setting it to false disables security wrappers.
peterv@114345
   952
bool
peterv@114345
   953
NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
bzbarsky@130279
   954
                                         JS::Handle<JSObject*> aScope,
sfink@151157
   955
                                         JS::MutableHandle<JS::Value> aRetval,
peterv@114345
   956
                                         xpcObjectHelper& aHelper,
peterv@114345
   957
                                         const nsIID* aIID,
peterv@114345
   958
                                         bool aAllowNativeWrapper);
peterv@114345
   959
peterv@90770
   960
/**
peterv@90770
   961
 * A method to handle new-binding wrap failure, by possibly falling back to
peterv@90770
   962
 * wrapping as a non-new-binding object.
peterv@90770
   963
 */
peterv@90770
   964
template <class T>
peterv@114345
   965
MOZ_ALWAYS_INLINE bool
bzbarsky@130279
   966
HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
bzbarsky@134439
   967
                                T* value, JS::MutableHandle<JS::Value> rval)
peterv@90770
   968
{
peterv@114345
   969
  if (JS_IsExceptionPending(cx)) {
peterv@114345
   970
    return false;
peterv@114345
   971
  }
peterv@114345
   972
peterv@114345
   973
  qsObjectHelper helper(value, GetWrapperCache(value));
sfink@151157
   974
  return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval,
bzbarsky@134439
   975
                                                  helper, nullptr, true);
peterv@90770
   976
}
peterv@90770
   977
bzbarsky@120074
   978
// Helper for calling HandleNewBindingWrappingFailure with smart pointers
bzbarsky@120074
   979
// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
bzbarsky@120074
   980
HAS_MEMBER(get)
bzbarsky@120074
   981
bzbarsky@120074
   982
template <class T, bool isSmartPtr=HasgetMember<T>::Value>
bzbarsky@120074
   983
struct HandleNewBindingWrappingFailureHelper
peterv@90770
   984
{
bzbarsky@130279
   985
  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope,
bzbarsky@134439
   986
                          const T& value, JS::MutableHandle<JS::Value> rval)
bzbarsky@120074
   987
  {
bzbarsky@134439
   988
    return HandleNewBindingWrappingFailure(cx, scope, value.get(), rval);
bzbarsky@120074
   989
  }
bzbarsky@120074
   990
};
bzbarsky@120074
   991
bzbarsky@120074
   992
template <class T>
bzbarsky@120074
   993
struct HandleNewBindingWrappingFailureHelper<T, false>
bzbarsky@120074
   994
{
bzbarsky@130279
   995
  static inline bool Wrap(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
bzbarsky@134439
   996
                          JS::MutableHandle<JS::Value> rval)
bzbarsky@120074
   997
  {
bzbarsky@134439
   998
    return HandleNewBindingWrappingFailure(cx, scope, &value, rval);
bzbarsky@120074
   999
  }
bzbarsky@120074
  1000
};
bzbarsky@120074
  1001
bzbarsky@120074
  1002
template<class T>
bzbarsky@120074
  1003
inline bool
bzbarsky@130279
  1004
HandleNewBindingWrappingFailure(JSContext* cx, JS::Handle<JSObject*> scope,
bzbarsky@134439
  1005
                                T& value, JS::MutableHandle<JS::Value> rval)
bzbarsky@120074
  1006
{
bzbarsky@134439
  1007
  return HandleNewBindingWrappingFailureHelper<T>::Wrap(cx, scope, value, rval);
peterv@90770
  1008
}
peterv@90770
  1009
ms2ger@99526
  1010
template<bool Fatal>
ms2ger@99526
  1011
inline bool
ms2ger@99526
  1012
EnumValueNotFound(JSContext* cx, const jschar* chars, size_t length,
bzbarsky@135314
  1013
                  const char* type, const char* sourceDescription)
ms2ger@99526
  1014
{
ms2ger@99526
  1015
  return false;
ms2ger@99526
  1016
}
ms2ger@99526
  1017
ms2ger@99526
  1018
template<>
ms2ger@99526
  1019
inline bool
ms2ger@99526
  1020
EnumValueNotFound<false>(JSContext* cx, const jschar* chars, size_t length,
bzbarsky@135314
  1021
                         const char* type, const char* sourceDescription)
ms2ger@99526
  1022
{
ms2ger@99526
  1023
  // TODO: Log a warning to the console.
ms2ger@99526
  1024
  return true;
ms2ger@99526
  1025
}
ms2ger@99526
  1026
ms2ger@99526
  1027
template<>
ms2ger@99526
  1028
inline bool
ms2ger@99526
  1029
EnumValueNotFound<true>(JSContext* cx, const jschar* chars, size_t length,
bzbarsky@135314
  1030
                        const char* type, const char* sourceDescription)
ms2ger@99526
  1031
{
ehsan@162099
  1032
  NS_LossyConvertUTF16toASCII deflated(static_cast<const char16_t*>(chars),
ms2ger@99526
  1033
                                       length);
bzbarsky@135314
  1034
  return ThrowErrorMessage(cx, MSG_INVALID_ENUM_VALUE, sourceDescription,
bzbarsky@135314
  1035
                           deflated.get(), type);
ms2ger@99526
  1036
}
ms2ger@99526
  1037
ms2ger@99526
  1038
ms2ger@99526
  1039
template<bool InvalidValueFatal>
peterv@90770
  1040
inline int
evilpies@155063
  1041
FindEnumStringIndex(JSContext* cx, JS::Handle<JS::Value> v, const EnumEntry* values,
bzbarsky@135314
  1042
                    const char* type, const char* sourceDescription, bool* ok)
peterv@90770
  1043
{
peterv@90770
  1044
  // JS_StringEqualsAscii is slow as molasses, so don't use it here.
evilpies@155063
  1045
  JSString* str = JS::ToString(cx, v);
peterv@90770
  1046
  if (!str) {
peterv@90770
  1047
    *ok = false;
peterv@90770
  1048
    return 0;
peterv@90770
  1049
  }
peterv@90770
  1050
  JS::Anchor<JSString*> anchor(str);
peterv@90770
  1051
  size_t length;
peterv@90770
  1052
  const jschar* chars = JS_GetStringCharsAndLength(cx, str, &length);
peterv@90770
  1053
  if (!chars) {
peterv@90770
  1054
    *ok = false;
peterv@90770
  1055
    return 0;
peterv@90770
  1056
  }
peterv@90770
  1057
  int i = 0;
peterv@90770
  1058
  for (const EnumEntry* value = values; value->value; ++value, ++i) {
peterv@90770
  1059
    if (length != value->length) {
peterv@90770
  1060
      continue;
peterv@90770
  1061
    }
peterv@90770
  1062
peterv@90770
  1063
    bool equal = true;
peterv@90770
  1064
    const char* val = value->value;
peterv@90770
  1065
    for (size_t j = 0; j != length; ++j) {
peterv@90770
  1066
      if (unsigned(val[j]) != unsigned(chars[j])) {
peterv@90770
  1067
        equal = false;
peterv@90770
  1068
        break;
peterv@90770
  1069
      }
peterv@90770
  1070
    }
peterv@90770
  1071
peterv@90770
  1072
    if (equal) {
peterv@90770
  1073
      *ok = true;
peterv@90770
  1074
      return i;
peterv@90770
  1075
    }
peterv@90770
  1076
  }
peterv@90770
  1077
bzbarsky@135314
  1078
  *ok = EnumValueNotFound<InvalidValueFatal>(cx, chars, length, type,
bzbarsky@135314
  1079
                                             sourceDescription);
ms2ger@97133
  1080
  return -1;
peterv@90770
  1081
}
peterv@90770
  1082
bzbarsky@93761
  1083
inline nsWrapperCache*
bzbarsky@93761
  1084
GetWrapperCache(const ParentObject& aParentObject)
bzbarsky@93761
  1085
{
bzbarsky@93761
  1086
  return aParentObject.mWrapperCache;
bzbarsky@93761
  1087
}
bzbarsky@93761
  1088
bzbarsky@93761
  1089
template<class T>
peterv@110222
  1090
inline T*
peterv@110084
  1091
GetParentPointer(T* aObject)
bzbarsky@93761
  1092
{
peterv@110222
  1093
  return aObject;
bzbarsky@93761
  1094
}
bzbarsky@93761
  1095
bzbarsky@93761
  1096
inline nsISupports*
bzbarsky@93761
  1097
GetParentPointer(const ParentObject& aObject)
bzbarsky@93761
  1098
{
peterv@110222
  1099
  return aObject.mObject;
bzbarsky@93761
  1100
}
bzbarsky@93761
  1101
bobbyholley@174332
  1102
template <typename T>
bobbyholley@174332
  1103
inline bool
bobbyholley@174332
  1104
GetUseXBLScope(T* aParentObject)
bobbyholley@174332
  1105
{
bobbyholley@174332
  1106
  return false;
bobbyholley@174332
  1107
}
bobbyholley@174332
  1108
bobbyholley@174332
  1109
inline bool
bobbyholley@174332
  1110
GetUseXBLScope(const ParentObject& aParentObject)
bobbyholley@174332
  1111
{
bobbyholley@174332
  1112
  return aParentObject.mUseXBLScope;
bobbyholley@174332
  1113
}
bobbyholley@174332
  1114
peterv@103142
  1115
template<class T>
peterv@103142
  1116
inline void
peterv@103142
  1117
ClearWrapper(T* p, nsWrapperCache* cache)
peterv@103142
  1118
{
peterv@103142
  1119
  cache->ClearWrapper();
peterv@103142
  1120
}
peterv@103142
  1121
peterv@103142
  1122
template<class T>
peterv@103142
  1123
inline void
peterv@103142
  1124
ClearWrapper(T* p, void*)
peterv@103142
  1125
{
peterv@103142
  1126
  nsWrapperCache* cache;
peterv@103142
  1127
  CallQueryInterface(p, &cache);
peterv@103142
  1128
  ClearWrapper(p, cache);
peterv@103142
  1129
}
peterv@103142
  1130
amccreight@118460
  1131
// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
amccreight@118460
  1132
// Return true if we successfully preserved the wrapper, or there is no wrapper
amccreight@118460
  1133
// to preserve. In the latter case we don't need to preserve the wrapper, because
amccreight@118460
  1134
// the object can only be obtained by JS once, or they cannot be meaningfully
amccreight@118460
  1135
// owned from the native side.
amccreight@118460
  1136
//
amccreight@118460
  1137
// This operation will return false only for non-nsISupports cycle-collected
amccreight@118460
  1138
// objects, because we cannot determine if they are wrappercached or not.
amccreight@118460
  1139
bool
amccreight@118460
  1140
TryPreserveWrapper(JSObject* obj);
amccreight@118460
  1141
bzbarsky@101792
  1142
// Can only be called with the immediate prototype of the instance object. Can
bzbarsky@101792
  1143
// only be called on the prototype of an object known to be a DOM instance.
nnethercote@141957
  1144
bool
bhackett1024@152907
  1145
InstanceClassHasProtoAtDepth(JSObject* protoObject, uint32_t protoID,
bzbarsky@101792
  1146
                             uint32_t depth);
bzbarsky@101792
  1147
peterv@90770
  1148
// Only set allowNativeWrapper to false if you really know you need it, if in
peterv@90770
  1149
// doubt use true. Setting it to false disables security wrappers.
peterv@90770
  1150
bool
bzbarsky@130279
  1151
XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
bzbarsky@130279
  1152
                   xpcObjectHelper& helper, const nsIID* iid,
sfink@151157
  1153
                   bool allowNativeWrapper, JS::MutableHandle<JS::Value> rval);
peterv@90770
  1154
bzbarsky@127622
  1155
// Special-cased wrapping for variants
bzbarsky@127622
  1156
bool
bzbarsky@177857
  1157
VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
bzbarsky@177857
  1158
               JS::MutableHandle<JS::Value> aRetval);
bzbarsky@127622
  1159
bzbarsky@112852
  1160
// Wrap an object "p" which is not using WebIDL bindings yet.  This _will_
bzbarsky@112852
  1161
// actually work on WebIDL binding objects that are wrappercached, but will be
bzbarsky@112852
  1162
// much slower than WrapNewBindingObject.  "cache" must either be null or be the
bzbarsky@112852
  1163
// nsWrapperCache for "p".
peterv@90770
  1164
template<class T>
peterv@90770
  1165
inline bool
bzbarsky@177857
  1166
WrapObject(JSContext* cx, T* p, nsWrapperCache* cache, const nsIID* iid,
bzbarsky@134439
  1167
           JS::MutableHandle<JS::Value> rval)
peterv@90770
  1168
{
bzbarsky@177855
  1169
  if (xpc_FastGetCachedWrapper(cx, cache, rval))
peterv@90770
  1170
    return true;
peterv@90770
  1171
  qsObjectHelper helper(p, cache);
bzbarsky@177857
  1172
  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
sfink@151157
  1173
  return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
peterv@90770
  1174
}
peterv@90770
  1175
bzbarsky@127622
  1176
// A specialization of the above for nsIVariant, because that needs to
bzbarsky@127622
  1177
// do something different.
bzbarsky@127622
  1178
template<>
bzbarsky@127622
  1179
inline bool
bzbarsky@177857
  1180
WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
bzbarsky@134439
  1181
                       nsWrapperCache* cache, const nsIID* iid,
bzbarsky@134439
  1182
                       JS::MutableHandle<JS::Value> rval)
bzbarsky@127622
  1183
{
bzbarsky@127622
  1184
  MOZ_ASSERT(iid);
bzbarsky@127622
  1185
  MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
bzbarsky@177857
  1186
  return VariantToJsval(cx, p, rval);
bzbarsky@127622
  1187
}
bzbarsky@127622
  1188
bzbarsky@112852
  1189
// Wrap an object "p" which is not using WebIDL bindings yet.  Just like the
bzbarsky@112852
  1190
// variant that takes an nsWrapperCache above, but will try to auto-derive the
bzbarsky@112852
  1191
// nsWrapperCache* from "p".
peterv@90770
  1192
template<class T>
peterv@90770
  1193
inline bool
bzbarsky@177857
  1194
WrapObject(JSContext* cx, T* p, const nsIID* iid,
bzbarsky@134439
  1195
           JS::MutableHandle<JS::Value> rval)
peterv@90770
  1196
{
bzbarsky@177857
  1197
  return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
peterv@90770
  1198
}
peterv@90770
  1199
bzbarsky@112852
  1200
// Just like the WrapObject above, but without requiring you to pick which
bzbarsky@112852
  1201
// interface you're wrapping as.  This should only be used for objects that have
bzbarsky@112852
  1202
// classinfo, for which it doesn't matter what IID is used to wrap.
peterv@90770
  1203
template<class T>
peterv@90770
  1204
inline bool
bzbarsky@177857
  1205
WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval)
peterv@90770
  1206
{
bzbarsky@177857
  1207
  return WrapObject(cx, p, nullptr, rval);
peterv@90770
  1208
}
peterv@90770
  1209
bzbarsky@112852
  1210
// Helper to make it possible to wrap directly out of an nsCOMPtr
peterv@90770
  1211
template<class T>
peterv@90770
  1212
inline bool
bzbarsky@177857
  1213
WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
bzbarsky@134439
  1214
           const nsIID* iid, JS::MutableHandle<JS::Value> rval)
peterv@90770
  1215
{
bzbarsky@177857
  1216
  return WrapObject(cx, p.get(), iid, rval);
peterv@90770
  1217
}
peterv@90770
  1218
bzbarsky@112852
  1219
// Helper to make it possible to wrap directly out of an nsCOMPtr
peterv@90770
  1220
template<class T>
peterv@90770
  1221
inline bool
bzbarsky@177857
  1222
WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
bzbarsky@134439
  1223
           JS::MutableHandle<JS::Value> rval)
peterv@90770
  1224
{
bzbarsky@177857
  1225
  return WrapObject(cx, p, nullptr, rval);
peterv@90770
  1226
}
peterv@90770
  1227
bzbarsky@112852
  1228
// Helper to make it possible to wrap directly out of an nsRefPtr
peterv@90770
  1229
template<class T>
peterv@90770
  1230
inline bool
bzbarsky@177857
  1231
WrapObject(JSContext* cx, const nsRefPtr<T>& p,
bzbarsky@134439
  1232
           const nsIID* iid, JS::MutableHandle<JS::Value> rval)
peterv@90770
  1233
{
bzbarsky@177857
  1234
  return WrapObject(cx, p.get(), iid, rval);
peterv@90770
  1235
}
peterv@90770
  1236
bzbarsky@112852
  1237
// Helper to make it possible to wrap directly out of an nsRefPtr
peterv@90770
  1238
template<class T>
peterv@90770
  1239
inline bool
bzbarsky@177857
  1240
WrapObject(JSContext* cx, const nsRefPtr<T>& p,
bzbarsky@134439
  1241
           JS::MutableHandle<JS::Value> rval)
peterv@90770
  1242
{
bzbarsky@177857
  1243
  return WrapObject(cx, p, nullptr, rval);
peterv@90770
  1244
}
peterv@90770
  1245
bzbarsky@112852
  1246
// Specialization to make it easy to use WrapObject in codegen.
peterv@90770
  1247
template<>
peterv@90770
  1248
inline bool
bzbarsky@177857
  1249
WrapObject<JSObject>(JSContext* cx, JSObject* p,
bzbarsky@134439
  1250
                     JS::MutableHandle<JS::Value> rval)
peterv@90770
  1251
{
bzbarsky@134439
  1252
  rval.set(JS::ObjectOrNullValue(p));
peterv@90770
  1253
  return true;
peterv@90770
  1254
}
peterv@90770
  1255
Olli@124296
  1256
inline bool
bzbarsky@177857
  1257
WrapObject(JSContext* cx, JSObject& p, JS::MutableHandle<JS::Value> rval)
Olli@124296
  1258
{
bzbarsky@134439
  1259
  rval.set(JS::ObjectValue(p));
Olli@124296
  1260
  return true;
Olli@124296
  1261
}
Olli@124296
  1262
bzbarsky@112852
  1263
// Given an object "p" that inherits from nsISupports, wrap it and return the
bzbarsky@112852
  1264
// result.  Null is returned on wrapping failure.  This is somewhat similar to
bzbarsky@112852
  1265
// WrapObject() above, but does NOT allow Xrays around the result, since we
bzbarsky@112852
  1266
// don't want those for our parent object.
peterv@110769
  1267
template<typename T>
peterv@110769
  1268
static inline JSObject*
bzbarsky@177622
  1269
WrapNativeISupportsParent(JSContext* cx, T* p, nsWrapperCache* cache)
peterv@110769
  1270
{
peterv@110769
  1271
  qsObjectHelper helper(ToSupports(p), cache);
bzbarsky@177622
  1272
  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
bzbarsky@130815
  1273
  JS::Rooted<JS::Value> v(cx);
sfink@151157
  1274
  return XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v) ?
sfink@151157
  1275
         v.toObjectOrNull() :
peterv@110769
  1276
         nullptr;
peterv@110769
  1277
}
peterv@110769
  1278
bzbarsky@112852
  1279
bzbarsky@112852
  1280
// Fallback for when our parent is not a WebIDL binding object.
peterv@140097
  1281
template<typename T, bool isISupports=IsBaseOf<nsISupports, T>::value>
peterv@110769
  1282
struct WrapNativeParentFallback
peterv@110769
  1283
{
bzbarsky@177622
  1284
  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
peterv@110769
  1285
  {
peterv@110769
  1286
    return nullptr;
peterv@110769
  1287
  }
peterv@110769
  1288
};
peterv@110769
  1289
bzbarsky@112852
  1290
// Fallback for when our parent is not a WebIDL binding object but _is_ an
bzbarsky@112852
  1291
// nsISupports object.
peterv@110769
  1292
template<typename T >
peterv@110769
  1293
struct WrapNativeParentFallback<T, true >
peterv@110769
  1294
{
bzbarsky@177622
  1295
  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
peterv@110769
  1296
  {
bzbarsky@177622
  1297
    return WrapNativeISupportsParent(cx, parent, cache);
peterv@110769
  1298
  }
peterv@110222
  1299
};
peterv@110222
  1300
bzbarsky@112852
  1301
// Wrapping of our native parent, for cases when it's a WebIDL object (though
bzbarsky@112852
  1302
// possibly preffed off).
peterv@110222
  1303
template<typename T, bool hasWrapObject=HasWrapObject<T>::Value >
peterv@110222
  1304
struct WrapNativeParentHelper
peterv@110222
  1305
{
bzbarsky@177622
  1306
  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
peterv@110222
  1307
  {
peterv@110222
  1308
    MOZ_ASSERT(cache);
peterv@110222
  1309
peterv@110222
  1310
    JSObject* obj;
peterv@110222
  1311
    if ((obj = cache->GetWrapper())) {
peterv@110222
  1312
      return obj;
peterv@110222
  1313
    }
peterv@110222
  1314
peterv@114346
  1315
    // Inline this here while we have non-dom objects in wrapper caches.
peterv@114346
  1316
    if (!CouldBeDOMBinding(parent)) {
bzbarsky@177622
  1317
      obj = WrapNativeParentFallback<T>::Wrap(cx, parent, cache);
peterv@114346
  1318
    } else {
bzbarsky@177628
  1319
      obj = parent->WrapObject(cx);
peterv@114346
  1320
    }
peterv@114346
  1321
peterv@110769
  1322
    return obj;
peterv@110222
  1323
  }
peterv@110222
  1324
};
peterv@110222
  1325
bzbarsky@112852
  1326
// Wrapping of our native parent, for cases when it's not a WebIDL object.  In
bzbarsky@112852
  1327
// this case it must be nsISupports.
peterv@110222
  1328
template<typename T>
peterv@110769
  1329
struct WrapNativeParentHelper<T, false >
peterv@110222
  1330
{
bzbarsky@177622
  1331
  static inline JSObject* Wrap(JSContext* cx, T* parent, nsWrapperCache* cache)
peterv@110222
  1332
  {
peterv@110222
  1333
    JSObject* obj;
peterv@110222
  1334
    if (cache && (obj = cache->GetWrapper())) {
peterv@110222
  1335
#ifdef DEBUG
bzbarsky@177622
  1336
      NS_ASSERTION(WrapNativeISupportsParent(cx, parent, cache) == obj,
peterv@110222
  1337
                   "Unexpected object in nsWrapperCache");
peterv@110222
  1338
#endif
peterv@110222
  1339
      return obj;
peterv@110222
  1340
    }
peterv@110222
  1341
bzbarsky@177622
  1342
    return WrapNativeISupportsParent(cx, parent, cache);
peterv@110222
  1343
  }
peterv@110222
  1344
};
peterv@110222
  1345
bzbarsky@112852
  1346
// Wrapping of our native parent.
peterv@110222
  1347
template<typename T>
peterv@110222
  1348
static inline JSObject*
bzbarsky@177623
  1349
WrapNativeParent(JSContext* cx, T* p, nsWrapperCache* cache,
bzbarsky@177623
  1350
                 bool useXBLScope = false)
peterv@110222
  1351
{
peterv@110222
  1352
  if (!p) {
bzbarsky@177623
  1353
    return JS::CurrentGlobalOrNull(cx);
peterv@110222
  1354
  }
peterv@110222
  1355
bzbarsky@177622
  1356
  JSObject* parent = WrapNativeParentHelper<T>::Wrap(cx, p, cache);
bobbyholley@174332
  1357
  if (!useXBLScope) {
bobbyholley@174332
  1358
    return parent;
bobbyholley@174332
  1359
  }
bobbyholley@174332
  1360
bobbyholley@174332
  1361
  // If useXBLScope is true, it means that the canonical reflector for this
bobbyholley@174332
  1362
  // native object should live in the XBL scope.
bobbyholley@174332
  1363
  if (xpc::IsInXBLScope(parent)) {
bobbyholley@174332
  1364
    return parent;
bobbyholley@174332
  1365
  }
bobbyholley@174332
  1366
  JS::Rooted<JSObject*> rootedParent(cx, parent);
bobbyholley@174332
  1367
  JS::Rooted<JSObject*> xblScope(cx, xpc::GetXBLScope(cx, rootedParent));
bobbyholley@174332
  1368
  JSAutoCompartment ac(cx, xblScope);
bobbyholley@174332
  1369
  if (NS_WARN_IF(!JS_WrapObject(cx, &rootedParent))) {
bobbyholley@174332
  1370
    return nullptr;
bobbyholley@174332
  1371
  }
bobbyholley@174332
  1372
bobbyholley@174332
  1373
  return rootedParent;
peterv@110222
  1374
}
peterv@110222
  1375
bzbarsky@112852
  1376
// Wrapping of our native parent, when we don't want to explicitly pass in
bzbarsky@112852
  1377
// things like the nsWrapperCache for it.
bzbarsky@93761
  1378
template<typename T>
peterv@90770
  1379
static inline JSObject*
bzbarsky@177623
  1380
WrapNativeParent(JSContext* cx, const T& p)
peterv@90770
  1381
{
bzbarsky@177623
  1382
  return WrapNativeParent(cx, GetParentPointer(p), GetWrapperCache(p), GetUseXBLScope(p));
peterv@90770
  1383
}
peterv@90770
  1384
bzbarsky@130278
  1385
// A way to differentiate between nodes, which use the parent object
bzbarsky@130278
  1386
// returned by native->GetParentObject(), and all other objects, which
bzbarsky@130278
  1387
// just use the parent's global.
bzbarsky@130278
  1388
static inline JSObject*
bzbarsky@130278
  1389
GetRealParentObject(void* aParent, JSObject* aParentObject)
bzbarsky@130278
  1390
{
bzbarsky@130278
  1391
  return aParentObject ?
bzbarsky@130278
  1392
    js::GetGlobalForObjectCrossCompartment(aParentObject) : nullptr;
bzbarsky@130278
  1393
}
bzbarsky@130278
  1394
bzbarsky@130278
  1395
static inline JSObject*
bzbarsky@130278
  1396
GetRealParentObject(Element* aParent, JSObject* aParentObject)
bzbarsky@130278
  1397
{
bzbarsky@130278
  1398
  return aParentObject;
bzbarsky@130278
  1399
}
bzbarsky@130278
  1400
peterv@116780
  1401
HAS_MEMBER(GetParentObject)
peterv@116780
  1402
peterv@116780
  1403
template<typename T, bool WrapperCached=HasGetParentObjectMember<T>::Value>
peterv@116780
  1404
struct GetParentObject
peterv@116780
  1405
{
bzbarsky@129890
  1406
  static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj)
peterv@116780
  1407
  {
bzbarsky@177623
  1408
    MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
peterv@116780
  1409
    T* native = UnwrapDOMObject<T>(obj);
bzbarsky@130278
  1410
    return
bzbarsky@130278
  1411
      GetRealParentObject(native,
bzbarsky@177623
  1412
                          WrapNativeParent(cx, native->GetParentObject()));
peterv@116780
  1413
  }
peterv@116780
  1414
};
peterv@116780
  1415
peterv@116780
  1416
template<typename T>
peterv@116780
  1417
struct GetParentObject<T, false>
peterv@116780
  1418
{
bzbarsky@129890
  1419
  static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj)
peterv@116780
  1420
  {
peterv@116780
  1421
    MOZ_CRASH();
peterv@116780
  1422
    return nullptr;
peterv@116780
  1423
  }
peterv@116780
  1424
};
peterv@116780
  1425
bzbarsky@120996
  1426
MOZ_ALWAYS_INLINE
bzbarsky@120996
  1427
JSObject* GetJSObjectFromCallback(CallbackObject* callback)
bzbarsky@120996
  1428
{
bzbarsky@120996
  1429
  return callback->Callback();
bzbarsky@120996
  1430
}
bzbarsky@120996
  1431
bzbarsky@120996
  1432
MOZ_ALWAYS_INLINE
bzbarsky@120996
  1433
JSObject* GetJSObjectFromCallback(void* noncallback)
bzbarsky@120996
  1434
{
bzbarsky@120996
  1435
  return nullptr;
bzbarsky@120996
  1436
}
bzbarsky@120996
  1437
bzbarsky@112827
  1438
template<typename T>
bzbarsky@112827
  1439
static inline JSObject*
bzbarsky@177624
  1440
WrapCallThisObject(JSContext* cx, const T& p)
bzbarsky@112827
  1441
{
bzbarsky@120996
  1442
  // Callbacks are nsISupports, so WrapNativeParent will just happily wrap them
bzbarsky@120996
  1443
  // up as an nsISupports XPCWrappedNative... which is not at all what we want.
bzbarsky@120996
  1444
  // So we need to special-case them.
dzbarsky@130652
  1445
  JS::Rooted<JSObject*> obj(cx, GetJSObjectFromCallback(p));
bzbarsky@112827
  1446
  if (!obj) {
bzbarsky@120996
  1447
    // WrapNativeParent is a bit of a Swiss army knife that will
bzbarsky@120996
  1448
    // wrap anything for us.
bzbarsky@177623
  1449
    obj = WrapNativeParent(cx, p);
bzbarsky@120996
  1450
    if (!obj) {
bzbarsky@120996
  1451
      return nullptr;
bzbarsky@120996
  1452
    }
bzbarsky@112827
  1453
  }
bzbarsky@112827
  1454
bzbarsky@120996
  1455
  // But all that won't necessarily put things in the compartment of cx.
evilpies@150816
  1456
  if (!JS_WrapObject(cx, &obj)) {
bzbarsky@112827
  1457
    return nullptr;
bzbarsky@112827
  1458
  }
bzbarsky@112827
  1459
bzbarsky@112827
  1460
  return obj;
bzbarsky@112827
  1461
}
bzbarsky@112827
  1462
nsm@164946
  1463
/*
nsm@164946
  1464
 * This specialized function simply wraps a JS::Rooted<> since
nsm@164946
  1465
 * WrapNativeParent() is not applicable for JS objects.
nsm@164946
  1466
 */
nsm@164946
  1467
template<>
nsm@164946
  1468
inline JSObject*
nsm@164946
  1469
WrapCallThisObject<JS::Rooted<JSObject*>>(JSContext* cx,
nsm@164946
  1470
                                          const JS::Rooted<JSObject*>& p)
nsm@164946
  1471
{
nsm@164946
  1472
  JS::Rooted<JSObject*> obj(cx, p);
nsm@164946
  1473
nsm@164946
  1474
  if (!JS_WrapObject(cx, &obj)) {
nsm@164946
  1475
    return nullptr;
nsm@164946
  1476
  }
nsm@164946
  1477
nsm@164946
  1478
  return obj;
nsm@164946
  1479
}
nsm@164946
  1480
bzbarsky@112831
  1481
// Helper for calling WrapNewBindingObject with smart pointers
bzbarsky@112831
  1482
// (nsAutoPtr/nsRefPtr/nsCOMPtr) or references.
bzbarsky@112831
  1483
template <class T, bool isSmartPtr=HasgetMember<T>::Value>
bzbarsky@112831
  1484
struct WrapNewBindingObjectHelper
bzbarsky@112831
  1485
{
bzbarsky@177629
  1486
  static inline bool Wrap(JSContext* cx, const T& value,
bzbarsky@177629
  1487
                          JS::MutableHandle<JS::Value> rval)
bzbarsky@112831
  1488
  {
bzbarsky@177629
  1489
    return WrapNewBindingObject(cx, value.get(), rval);
bzbarsky@112831
  1490
  }
bzbarsky@112831
  1491
};
bzbarsky@112831
  1492
bzbarsky@112831
  1493
template <class T>
bzbarsky@112831
  1494
struct WrapNewBindingObjectHelper<T, false>
bzbarsky@112831
  1495
{
bzbarsky@177629
  1496
  static inline bool Wrap(JSContext* cx, T& value,
bzbarsky@134439
  1497
                          JS::MutableHandle<JS::Value> rval)
bzbarsky@112831
  1498
  {
bzbarsky@177629
  1499
    return WrapNewBindingObject(cx, &value, rval);
bzbarsky@112831
  1500
  }
bzbarsky@112831
  1501
};
bzbarsky@112831
  1502
bzbarsky@112831
  1503
template<class T>
bzbarsky@112831
  1504
inline bool
bzbarsky@177629
  1505
WrapNewBindingObject(JSContext* cx, T& value, JS::MutableHandle<JS::Value> rval)
bzbarsky@177629
  1506
{
bzbarsky@177629
  1507
  return WrapNewBindingObjectHelper<T>::Wrap(cx, value, rval);
bzbarsky@177629
  1508
}
bzbarsky@177629
  1509
bzbarsky@177629
  1510
// We need this version of WrapNewBindingObject for codegen, so it'll have the
bzbarsky@177629
  1511
// same signature as WrapNewBindingNonWrapperCachedObject and
bzbarsky@177629
  1512
// WrapNewBindingNonWrapperCachedOwnedObject, which still need the scope.
bzbarsky@177629
  1513
template<class T>
bzbarsky@177629
  1514
inline bool
bzbarsky@129890
  1515
WrapNewBindingObject(JSContext* cx, JS::Handle<JSObject*> scope, T& value,
bzbarsky@134439
  1516
                     JS::MutableHandle<JS::Value> rval)
bzbarsky@112831
  1517
{
bzbarsky@177629
  1518
  return WrapNewBindingObject(cx, value, rval);
bzbarsky@112831
  1519
}
bzbarsky@112831
  1520
bzbarsky@120075
  1521
template <class T>
bzbarsky@120075
  1522
inline JSObject*
bzbarsky@120075
  1523
GetCallbackFromCallbackObject(T* aObj)
bzbarsky@120075
  1524
{
bzbarsky@120075
  1525
  return aObj->Callback();
bzbarsky@120075
  1526
}
bzbarsky@120075
  1527
bzbarsky@120075
  1528
// Helper for getting the callback JSObject* of a smart ptr around a
bzbarsky@120075
  1529
// CallbackObject or a reference to a CallbackObject or something like
bzbarsky@120075
  1530
// that.
bzbarsky@120075
  1531
template <class T, bool isSmartPtr=HasgetMember<T>::Value>
bzbarsky@120075
  1532
struct GetCallbackFromCallbackObjectHelper
bzbarsky@120075
  1533
{
bzbarsky@120075
  1534
  static inline JSObject* Get(const T& aObj)
bzbarsky@120075
  1535
  {
bzbarsky@120075
  1536
    return GetCallbackFromCallbackObject(aObj.get());
bzbarsky@120075
  1537
  }
bzbarsky@120075
  1538
};
bzbarsky@120075
  1539
bzbarsky@120075
  1540
template <class T>
bzbarsky@120075
  1541
struct GetCallbackFromCallbackObjectHelper<T, false>
bzbarsky@120075
  1542
{
bzbarsky@120075
  1543
  static inline JSObject* Get(T& aObj)
bzbarsky@120075
  1544
  {
bzbarsky@120075
  1545
    return GetCallbackFromCallbackObject(&aObj);
bzbarsky@120075
  1546
  }
bzbarsky@120075
  1547
};
bzbarsky@120075
  1548
bzbarsky@120075
  1549
template<class T>
bzbarsky@120075
  1550
inline JSObject*
bzbarsky@120075
  1551
GetCallbackFromCallbackObject(T& aObj)
bzbarsky@120075
  1552
{
bzbarsky@120075
  1553
  return GetCallbackFromCallbackObjectHelper<T>::Get(aObj);
bzbarsky@120075
  1554
}
bzbarsky@120075
  1555
bzbarsky@96481
  1556
static inline bool
bzbarsky@96481
  1557
InternJSString(JSContext* cx, jsid& id, const char* chars)
bzbarsky@96481
  1558
{
bzbarsky@96481
  1559
  if (JSString *str = ::JS_InternString(cx, chars)) {
bzbarsky@96481
  1560
    id = INTERNED_STRING_TO_JSID(cx, str);
bzbarsky@96481
  1561
    return true;
bzbarsky@96481
  1562
  }
bzbarsky@96481
  1563
  return false;
bzbarsky@96481
  1564
}
bzbarsky@96481
  1565
peterv@90770
  1566
// Spec needs a name property
peterv@90770
  1567
template <typename Spec>
peterv@90770
  1568
static bool
bzbarsky@127005
  1569
InitIds(JSContext* cx, const Prefable<Spec>* prefableSpecs, jsid* ids)
peterv@90770
  1570
{
bzbarsky@94726
  1571
  MOZ_ASSERT(prefableSpecs);
bzbarsky@94726
  1572
  MOZ_ASSERT(prefableSpecs->specs);
peterv@90770
  1573
  do {
bzbarsky@94726
  1574
    // We ignore whether the set of ids is enabled and just intern all the IDs,
bzbarsky@94726
  1575
    // because this is only done once per application runtime.
bzbarsky@94726
  1576
    Spec* spec = prefableSpecs->specs;
bzbarsky@94726
  1577
    do {
bzbarsky@96481
  1578
      if (!InternJSString(cx, *ids, spec->name)) {
bzbarsky@94726
  1579
        return false;
bzbarsky@94726
  1580
      }
bzbarsky@94726
  1581
    } while (++ids, (++spec)->name);
bzbarsky@94726
  1582
wkocher@160750
  1583
    // We ran out of ids for that pref.  Put a JSID_VOID in on the id
bzbarsky@94726
  1584
    // corresponding to the list terminator for the pref.
wkocher@160750
  1585
    *ids = JSID_VOID;
bzbarsky@94726
  1586
    ++ids;
bzbarsky@94726
  1587
  } while ((++prefableSpecs)->specs);
peterv@90770
  1588
peterv@90770
  1589
  return true;
peterv@90770
  1590
}
peterv@90770
  1591
nnethercote@141555
  1592
bool
peterv@90770
  1593
QueryInterface(JSContext* cx, unsigned argc, JS::Value* vp);
peterv@140097
  1594
peterv@148690
  1595
template <class T>
peterv@140097
  1596
struct
peterv@140097
  1597
WantsQueryInterface
peterv@140097
  1598
{
peterv@148690
  1599
  static_assert(IsBaseOf<nsISupports, T>::value,
peterv@148690
  1600
                "QueryInterface can't work without an nsISupports.");
peterv@140097
  1601
  static bool Enabled(JSContext* aCx, JSObject* aGlobal)
peterv@140097
  1602
  {
Olli@146460
  1603
    return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
peterv@140097
  1604
  }
peterv@140097
  1605
};
peterv@140097
  1606
peterv@177573
  1607
JS::Value
peterv@177573
  1608
GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
peterv@177573
  1609
                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError);
peterv@177573
  1610
peterv@177573
  1611
template<class T>
peterv@177573
  1612
JS::Value
peterv@177573
  1613
GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, ErrorResult& aError)
peterv@177573
  1614
{
peterv@177573
  1615
  return GetInterfaceImpl(aCx, aThis, aThis, aIID, aError);
peterv@177573
  1616
}
peterv@177573
  1617
nnethercote@141555
  1618
bool
bzbarsky@92915
  1619
ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
peterv@90770
  1620
bzbarsky@172864
  1621
bool
bzbarsky@172864
  1622
ThrowConstructorWithoutNew(JSContext* cx, const char* name);
bzbarsky@172864
  1623
peterv@142159
  1624
// vp is allowed to be null; in that case no get will be attempted,
peterv@142159
  1625
// and *found will simply indicate whether the property exists.
peterv@103142
  1626
bool
bzbarsky@130812
  1627
GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
bzbarsky@130812
  1628
                       JS::Handle<jsid> id, bool* found,
peterv@103142
  1629
                       JS::Value* vp);
peterv@103142
  1630
peterv@103142
  1631
bool
bzbarsky@130812
  1632
HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
bzbarsky@130812
  1633
                       JS::Handle<jsid> id);
peterv@103142
  1634
peterv@142159
  1635
peterv@142159
  1636
// Append the property names in "names" to "props". If
peterv@142159
  1637
// shadowPrototypeProperties is false then skip properties that are also
peterv@142159
  1638
// present on the proto chain of proxy.  If shadowPrototypeProperties is true,
peterv@142159
  1639
// then the "proxy" argument is ignored.
peterv@142159
  1640
bool
peterv@142159
  1641
AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
peterv@142159
  1642
                       nsTArray<nsString>& names,
peterv@142159
  1643
                       bool shadowPrototypeProperties, JS::AutoIdVector& props);
peterv@142159
  1644
bzbarsky@164848
  1645
namespace binding_detail {
bzbarsky@164848
  1646
bzbarsky@99577
  1647
// A struct that has the same layout as an nsDependentString but much
bzbarsky@99577
  1648
// faster constructor and destructor behavior
bzbarsky@99577
  1649
struct FakeDependentString {
bzbarsky@99577
  1650
  FakeDependentString() :
bzbarsky@99577
  1651
    mFlags(nsDependentString::F_TERMINATED)
bzbarsky@99577
  1652
  {
bzbarsky@99577
  1653
  }
bzbarsky@99577
  1654
bzbarsky@99577
  1655
  void SetData(const nsDependentString::char_type* aData,
bzbarsky@99577
  1656
               nsDependentString::size_type aLength) {
bzbarsky@99577
  1657
    MOZ_ASSERT(mFlags == nsDependentString::F_TERMINATED);
bzbarsky@99577
  1658
    mData = aData;
bzbarsky@99577
  1659
    mLength = aLength;
bzbarsky@99577
  1660
  }
bzbarsky@99577
  1661
bzbarsky@99577
  1662
  void Truncate() {
bzbarsky@99578
  1663
    mData = nsDependentString::char_traits::sEmptyBuffer;
bzbarsky@99577
  1664
    mLength = 0;
bzbarsky@99577
  1665
  }
bzbarsky@99577
  1666
bzbarsky@99577
  1667
  void SetNull() {
bzbarsky@99577
  1668
    Truncate();
bzbarsky@99577
  1669
    mFlags |= nsDependentString::F_VOIDED;
bzbarsky@99577
  1670
  }
bzbarsky@99577
  1671
bzbarsky@145568
  1672
  const nsDependentString::char_type* Data() const
bzbarsky@145568
  1673
  {
bzbarsky@145568
  1674
    return mData;
bzbarsky@145568
  1675
  }
bzbarsky@145568
  1676
bzbarsky@145568
  1677
  nsDependentString::size_type Length() const
bzbarsky@145568
  1678
  {
bzbarsky@145568
  1679
    return mLength;
bzbarsky@145568
  1680
  }
bzbarsky@145568
  1681
bzbarsky@120998
  1682
  // If this ever changes, change the corresponding code in the
bzbarsky@120998
  1683
  // Optional<nsAString> specialization as well.
bzbarsky@99577
  1684
  const nsAString* ToAStringPtr() const {
bzbarsky@99577
  1685
    return reinterpret_cast<const nsDependentString*>(this);
bzbarsky@99577
  1686
  }
bzbarsky@99577
  1687
bzbarsky@99577
  1688
  nsAString* ToAStringPtr() {
bzbarsky@99577
  1689
    return reinterpret_cast<nsDependentString*>(this);
bzbarsky@99577
  1690
  }
bzbarsky@99577
  1691
bzbarsky@99577
  1692
  operator const nsAString& () const {
bzbarsky@99577
  1693
    return *reinterpret_cast<const nsDependentString*>(this);
bzbarsky@99577
  1694
  }
bzbarsky@99577
  1695
bzbarsky@99577
  1696
private:
bzbarsky@99577
  1697
  const nsDependentString::char_type* mData;
bzbarsky@99577
  1698
  nsDependentString::size_type mLength;
ehsan@102997
  1699
  uint32_t mFlags;
bzbarsky@99577
  1700
bzbarsky@99577
  1701
  // A class to use for our static asserts to ensure our object layout
bzbarsky@99577
  1702
  // matches that of nsDependentString.
bzbarsky@99577
  1703
  class DependentStringAsserter;
bzbarsky@99577
  1704
  friend class DependentStringAsserter;
bzbarsky@99577
  1705
bzbarsky@99577
  1706
  class DepedentStringAsserter : public nsDependentString {
bzbarsky@99577
  1707
  public:
bzbarsky@99577
  1708
    static void StaticAsserts() {
ehsan@140595
  1709
      static_assert(sizeof(FakeDependentString) == sizeof(nsDependentString),
ehsan@140595
  1710
                    "Must have right object size");
ehsan@140595
  1711
      static_assert(offsetof(FakeDependentString, mData) ==
ehsan@140595
  1712
                      offsetof(DepedentStringAsserter, mData),
ehsan@140595
  1713
                    "Offset of mData should match");
ehsan@140595
  1714
      static_assert(offsetof(FakeDependentString, mLength) ==
ehsan@140595
  1715
                      offsetof(DepedentStringAsserter, mLength),
ehsan@140595
  1716
                    "Offset of mLength should match");
ehsan@140595
  1717
      static_assert(offsetof(FakeDependentString, mFlags) ==
ehsan@140595
  1718
                      offsetof(DepedentStringAsserter, mFlags),
ehsan@140595
  1719
                    "Offset of mFlags should match");
bzbarsky@99577
  1720
    }
bzbarsky@99577
  1721
  };
bzbarsky@99577
  1722
};
bzbarsky@99577
  1723
bzbarsky@164848
  1724
} // namespace binding_detail
bzbarsky@164848
  1725
peterv@94618
  1726
enum StringificationBehavior {
peterv@94618
  1727
  eStringify,
peterv@94618
  1728
  eEmpty,
peterv@94618
  1729
  eNull
peterv@94618
  1730
};
peterv@94618
  1731
bzbarsky@100950
  1732
// pval must not be null and must point to a rooted JS::Value
peterv@94618
  1733
static inline bool
bzbarsky@132899
  1734
ConvertJSValueToString(JSContext* cx, JS::Handle<JS::Value> v,
bzbarsky@132899
  1735
                       JS::MutableHandle<JS::Value> pval,
peterv@94618
  1736
                       StringificationBehavior nullBehavior,
peterv@94618
  1737
                       StringificationBehavior undefinedBehavior,
bzbarsky@164848
  1738
                       binding_detail::FakeDependentString& result)
peterv@94618
  1739
{
peterv@94618
  1740
  JSString *s;
peterv@94618
  1741
  if (v.isString()) {
peterv@94618
  1742
    s = v.toString();
peterv@94618
  1743
  } else {
peterv@94618
  1744
    StringificationBehavior behavior;
peterv@94618
  1745
    if (v.isNull()) {
peterv@94618
  1746
      behavior = nullBehavior;
peterv@94618
  1747
    } else if (v.isUndefined()) {
peterv@94618
  1748
      behavior = undefinedBehavior;
peterv@94618
  1749
    } else {
peterv@94618
  1750
      behavior = eStringify;
peterv@94618
  1751
    }
peterv@94618
  1752
bzbarsky@100950
  1753
    if (behavior != eStringify) {
bzbarsky@99577
  1754
      if (behavior == eEmpty) {
bzbarsky@99577
  1755
        result.Truncate();
bzbarsky@99577
  1756
      } else {
bzbarsky@99577
  1757
        result.SetNull();
bzbarsky@99577
  1758
      }
peterv@94618
  1759
      return true;
peterv@94618
  1760
    }
peterv@94618
  1761
evilpies@155063
  1762
    s = JS::ToString(cx, v);
peterv@94618
  1763
    if (!s) {
peterv@94618
  1764
      return false;
peterv@94618
  1765
    }
bzbarsky@132899
  1766
    pval.set(JS::StringValue(s));  // Root the new string.
peterv@94618
  1767
  }
peterv@94618
  1768
peterv@94618
  1769
  size_t len;
peterv@94618
  1770
  const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
peterv@94618
  1771
  if (!chars) {
peterv@94618
  1772
    return false;
peterv@94618
  1773
  }
peterv@94618
  1774
bzbarsky@99577
  1775
  result.SetData(chars, len);
peterv@94618
  1776
  return true;
peterv@94618
  1777
}
peterv@94618
  1778
jkitch@134919
  1779
bool
jkitch@134919
  1780
ConvertJSValueToByteString(JSContext* cx, JS::Handle<JS::Value> v,
jkitch@134919
  1781
                           JS::MutableHandle<JS::Value> pval, bool nullable,
jkitch@134919
  1782
                           nsACString& result);
jkitch@134919
  1783
bzbarsky@132129
  1784
template<typename T>
bzbarsky@132129
  1785
void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
bzbarsky@132131
  1786
template<typename T>
bzbarsky@132131
  1787
void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq);
bzbarsky@132129
  1788
bzbarsky@123551
  1789
// Class for simple sequence arguments, only used internally by codegen.
bzbarsky@164847
  1790
namespace binding_detail {
bzbarsky@164847
  1791
bzbarsky@123551
  1792
template<typename T>
bzbarsky@123551
  1793
class AutoSequence : public AutoFallibleTArray<T, 16>
bzbarsky@123551
  1794
{
bzbarsky@123551
  1795
public:
bzbarsky@123551
  1796
  AutoSequence() : AutoFallibleTArray<T, 16>()
bzbarsky@123551
  1797
  {}
bzbarsky@123551
  1798
bzbarsky@123551
  1799
  // Allow converting to const sequences as needed
bzbarsky@123551
  1800
  operator const Sequence<T>&() const {
bzbarsky@123551
  1801
    return *reinterpret_cast<const Sequence<T>*>(this);
bzbarsky@123551
  1802
  }
bzbarsky@123551
  1803
};
bzbarsky@123551
  1804
bzbarsky@164847
  1805
} // namespace binding_detail
bzbarsky@164847
  1806
bzbarsky@132129
  1807
// Class used to trace sequences, with specializations for various
bzbarsky@132129
  1808
// sequence types.
bzbarsky@141431
  1809
template<typename T,
bzbarsky@141431
  1810
         bool isDictionary=IsBaseOf<DictionaryBase, T>::value,
bzbarsky@173700
  1811
         bool isTypedArray=IsBaseOf<AllTypedArraysBase, T>::value,
bzbarsky@173700
  1812
         bool isOwningUnion=IsBaseOf<AllOwningUnionBase, T>::value>
bzbarsky@132129
  1813
class SequenceTracer
bzbarsky@132129
  1814
{
bzbarsky@132129
  1815
  explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated
bzbarsky@132129
  1816
};
bzbarsky@132129
  1817
bzbarsky@132129
  1818
// sequence<object> or sequence<object?>
bzbarsky@132129
  1819
template<>
bzbarsky@173700
  1820
class SequenceTracer<JSObject*, false, false, false>
bzbarsky@132129
  1821
{
bzbarsky@132129
  1822
  explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated
bzbarsky@132129
  1823
bzbarsky@132129
  1824
public:
bzbarsky@132129
  1825
  static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) {
bzbarsky@141431
  1826
    for (; objp != end; ++objp) {
bzbarsky@132129
  1827
      JS_CallObjectTracer(trc, objp, "sequence<object>");
bzbarsky@132129
  1828
    }
bzbarsky@132129
  1829
  }
bzbarsky@132129
  1830
};
bzbarsky@132129
  1831
bzbarsky@132129
  1832
// sequence<any>
bzbarsky@132129
  1833
template<>
bzbarsky@173700
  1834
class SequenceTracer<JS::Value, false, false, false>
bzbarsky@132129
  1835
{
bzbarsky@132129
  1836
  explicit SequenceTracer() MOZ_DELETE; // Should never be instantiated
bzbarsky@132129
  1837
bzbarsky@132129
  1838
public:
bzbarsky@132129
  1839
  static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) {
bzbarsky@141431
  1840
    for (; valp != end; ++valp) {
bzbarsky@132129
  1841
      JS_CallValueTracer(trc, valp, "sequence<any>");
bzbarsky@132129
  1842
    }
bzbarsky@132129
  1843
  }
bzbarsky@132129
  1844
};
bzbarsky@132129
  1845
bzbarsky@132129
  1846
// sequence<sequence<T>></