tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

BindingUtils.h (132872B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef mozilla_dom_BindingUtils_h__
      8 #define mozilla_dom_BindingUtils_h__
      9 
     10 #include <type_traits>
     11 
     12 #include "js/CharacterEncoding.h"
     13 #include "js/Conversions.h"
     14 #include "js/MemoryFunctions.h"
     15 #include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
     16 #include "js/RealmOptions.h"
     17 #include "js/String.h"  // JS::GetLatin1LinearStringChars, JS::GetTwoByteLinearStringChars, JS::GetLinearStringLength, JS::LinearStringHasLatin1Chars, JS::StringHasLatin1Chars
     18 #include "js/Wrapper.h"
     19 #include "js/Zone.h"
     20 #include "js/experimental/BindingAllocs.h"
     21 #include "js/experimental/JitInfo.h"  // JSJitGetterOp, JSJitInfo
     22 #include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy, js::ToWindowProxyIfWindow
     23 #include "jsfriendapi.h"
     24 #include "mozilla/Array.h"
     25 #include "mozilla/Assertions.h"
     26 #include "mozilla/DeferredFinalize.h"
     27 #include "mozilla/EnumTypeTraits.h"
     28 #include "mozilla/EnumeratedRange.h"
     29 #include "mozilla/ErrorResult.h"
     30 #include "mozilla/Likely.h"
     31 #include "mozilla/MemoryReporting.h"
     32 #include "mozilla/ProfilerLabels.h"
     33 #include "mozilla/SegmentedVector.h"
     34 #include "mozilla/UniquePtr.h"
     35 #include "mozilla/dom/BindingCallContext.h"
     36 #include "mozilla/dom/BindingDeclarations.h"
     37 #include "mozilla/dom/DOMJSClass.h"
     38 #include "mozilla/dom/DOMJSProxyHandler.h"
     39 #include "mozilla/dom/FakeString.h"
     40 #include "mozilla/dom/JSSlots.h"
     41 #include "mozilla/dom/NonRefcountedDOMObject.h"
     42 #include "mozilla/dom/Nullable.h"
     43 #include "mozilla/dom/PrototypeList.h"
     44 #include "mozilla/dom/RemoteObjectProxy.h"
     45 #include "nsIGlobalObject.h"
     46 #include "nsISupportsImpl.h"
     47 #include "nsIVariant.h"
     48 #include "nsJSUtils.h"
     49 #include "nsWrapperCacheInlines.h"
     50 #include "xpcObjectHelper.h"
     51 #include "xpcpublic.h"
     52 
     53 class nsGlobalWindowInner;
     54 class nsGlobalWindowOuter;
     55 class nsIInterfaceRequestor;
     56 
     57 namespace mozilla {
     58 
     59 enum UseCounter : int16_t;
     60 enum class UseCounterWorker : int16_t;
     61 
     62 namespace dom {
     63 class CustomElementReactionsStack;
     64 class Document;
     65 class EventTarget;
     66 class MessageManagerGlobal;
     67 class ObservableArrayProxyHandler;
     68 class DedicatedWorkerGlobalScope;
     69 template <typename KeyType, typename ValueType>
     70 class Record;
     71 class WindowProxyHolder;
     72 
     73 enum class DeprecatedOperations : uint16_t;
     74 
     75 nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src,
     76                       const nsIID& iid, void** ppArg);
     77 
     78 /** Convert a jsval to an XPCOM pointer. Caller must not assume that src will
     79    keep the XPCOM pointer rooted. */
     80 template <class Interface>
     81 inline nsresult UnwrapArg(JSContext* cx, JS::Handle<JSObject*> src,
     82                          Interface** ppArg) {
     83  return UnwrapArgImpl(cx, src, NS_GET_IID(Interface),
     84                       reinterpret_cast<void**>(ppArg));
     85 }
     86 
     87 nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
     88                              WindowProxyHolder& ppArg);
     89 
     90 // Returns true if the JSClass is used for DOM objects.
     91 inline bool IsDOMClass(const JSClass* clasp) {
     92  return clasp->flags & JSCLASS_IS_DOMJSCLASS;
     93 }
     94 
     95 // Return true if the JSClass is used for non-proxy DOM objects.
     96 inline bool IsNonProxyDOMClass(const JSClass* clasp) {
     97  return IsDOMClass(clasp) && clasp->isNativeObject();
     98 }
     99 
    100 // Returns true if the JSClass is used for DOM interface and interface
    101 // prototype objects.
    102 inline bool IsDOMIfaceAndProtoClass(const JSClass* clasp) {
    103  return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
    104 }
    105 
    106 static_assert(DOM_OBJECT_SLOT == 0,
    107              "DOM_OBJECT_SLOT doesn't match the proxy private slot.  "
    108              "Expect bad things");
    109 template <class T>
    110 inline T* UnwrapDOMObject(JSObject* obj) {
    111  MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
    112             "Don't pass non-DOM objects to this function");
    113 
    114  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
    115  return static_cast<T*>(val.toPrivate());
    116 }
    117 
    118 template <class T>
    119 inline T* UnwrapPossiblyNotInitializedDOMObject(JSObject* obj) {
    120  // This is used by the OjectMoved JSClass hook which can be called before
    121  // JS_NewObject has returned and so before we have a chance to set
    122  // DOM_OBJECT_SLOT to anything useful.
    123 
    124  MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
    125             "Don't pass non-DOM objects to this function");
    126 
    127  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
    128  if (val.isUndefined()) {
    129    return nullptr;
    130  }
    131  return static_cast<T*>(val.toPrivate());
    132 }
    133 
    134 inline const DOMJSClass* GetDOMClass(const JSClass* clasp) {
    135  return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
    136 }
    137 
    138 inline const DOMJSClass* GetDOMClass(JSObject* obj) {
    139  return GetDOMClass(JS::GetClass(obj));
    140 }
    141 
    142 inline nsISupports* UnwrapDOMObjectToISupports(JSObject* aObject) {
    143  const DOMJSClass* clasp = GetDOMClass(aObject);
    144  if (!clasp || !clasp->mDOMObjectIsISupports) {
    145    return nullptr;
    146  }
    147 
    148  return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
    149 }
    150 
    151 inline bool IsDOMObject(JSObject* obj) { return IsDOMClass(JS::GetClass(obj)); }
    152 
    153 // There are two valid ways to use UNWRAP_OBJECT: Either obj needs to
    154 // be a MutableHandle<JSObject*>, or value needs to be a strong-reference
    155 // smart pointer type (OwningNonNull or RefPtr or nsCOMPtr), in which case obj
    156 // can be anything that converts to JSObject*.
    157 //
    158 // This can't be used with Window, EventTarget, or Location as the "Interface"
    159 // argument (and will fail a static_assert if you try to do that).  Use
    160 // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT to unwrap to those interfaces.
    161 #define UNWRAP_OBJECT(Interface, obj, value)                        \
    162  mozilla::dom::binding_detail::UnwrapObjectWithCrossOriginAsserts< \
    163      mozilla::dom::prototypes::id::Interface,                      \
    164      mozilla::dom::Interface##_Binding::NativeType>(obj, value)
    165 
    166 // UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT is just like UNWRAP_OBJECT but requires a
    167 // JSContext in a Realm that represents "who is doing the unwrapping?" to
    168 // properly unwrap the object.
    169 #define UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Interface, obj, value, cx)          \
    170  mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
    171                             mozilla::dom::Interface##_Binding::NativeType>( \
    172      obj, value, cx)
    173 
    174 // Test whether the given object is an instance of the given interface.
    175 #define IS_INSTANCE_OF(Interface, obj)                                       \
    176  mozilla::dom::IsInstanceOf<mozilla::dom::prototypes::id::Interface,        \
    177                             mozilla::dom::Interface##_Binding::NativeType>( \
    178      obj)
    179 
    180 // Unwrap the given non-wrapper object.  This can be used with any obj that
    181 // converts to JSObject*; as long as that JSObject* is live the return value
    182 // will be valid.
    183 #define UNWRAP_NON_WRAPPER_OBJECT(Interface, obj, value) \
    184  mozilla::dom::UnwrapNonWrapperObject<                  \
    185      mozilla::dom::prototypes::id::Interface,           \
    186      mozilla::dom::Interface##_Binding::NativeType>(obj, value)
    187 
    188 // Some callers don't want to set an exception when unwrapping fails
    189 // (for example, overload resolution uses unwrapping to tell what sort
    190 // of thing it's looking at).
    191 // U must be something that a T* can be assigned to (e.g. T* or an RefPtr<T>).
    192 //
    193 // The obj argument will be mutated to point to CheckedUnwrap of itself if the
    194 // passed-in value is not a DOM object and CheckedUnwrap succeeds.
    195 //
    196 // If mayBeWrapper is true, there are three valid ways to invoke
    197 // UnwrapObjectInternal: Either obj needs to be a class wrapping a
    198 // MutableHandle<JSObject*>, with an assignment operator that sets the handle to
    199 // the given object, or U needs to be a strong-reference smart pointer type
    200 // (OwningNonNull or RefPtr or nsCOMPtr), or the value being stored in "value"
    201 // must not escape past being tested for falsiness immediately after the
    202 // UnwrapObjectInternal call.
    203 //
    204 // If mayBeWrapper is false, obj can just be a JSObject*, and U anything that a
    205 // T* can be assigned to.
    206 //
    207 // The cx arg is in practice allowed to be either nullptr or JSContext* or a
    208 // BindingCallContext reference.  If it's nullptr we will do a
    209 // CheckedUnwrapStatic and it's the caller's responsibility to make sure they're
    210 // not trying to work with Window or Location objects.  Otherwise we'll do a
    211 // CheckedUnwrapDynamic.  This all only matters if mayBeWrapper is true; if it's
    212 // false just pass nullptr for the cx arg.
    213 namespace binding_detail {
    214 template <class T, bool mayBeWrapper, typename U, typename V, typename CxType>
    215 MOZ_ALWAYS_INLINE nsresult UnwrapObjectInternal(V& obj, U& value,
    216                                                prototypes::ID protoID,
    217                                                uint32_t protoDepth,
    218                                                const CxType& cx) {
    219  static_assert(std::is_same_v<CxType, JSContext*> ||
    220                    std::is_same_v<CxType, BindingCallContext> ||
    221                    std::is_same_v<CxType, decltype(nullptr)>,
    222                "Unexpected CxType");
    223 
    224  /* First check to see whether we have a DOM object */
    225  const DOMJSClass* domClass = GetDOMClass(obj);
    226  if (domClass) {
    227    /* This object is a DOM object.  Double-check that it is safely
    228       castable to T by checking whether it claims to inherit from the
    229       class identified by protoID. */
    230    if (domClass->mInterfaceChain[protoDepth] == protoID) {
    231      value = UnwrapDOMObject<T>(obj);
    232      return NS_OK;
    233    }
    234  }
    235 
    236  /* Maybe we have a security wrapper or outer window? */
    237  if (!mayBeWrapper || !js::IsWrapper(obj)) {
    238    // For non-cross-origin-accessible methods and properties, remote object
    239    // proxies should behave the same as opaque wrappers.
    240    if (IsRemoteObjectProxy(obj)) {
    241      return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
    242    }
    243 
    244    /* Not a DOM object, not a wrapper, just bail */
    245    return NS_ERROR_XPC_BAD_CONVERT_JS;
    246  }
    247 
    248  JSObject* unwrappedObj;
    249  if (std::is_same_v<CxType, decltype(nullptr)>) {
    250    unwrappedObj = js::CheckedUnwrapStatic(obj);
    251  } else {
    252    unwrappedObj =
    253        js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
    254  }
    255  if (!unwrappedObj) {
    256    return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
    257  }
    258 
    259  if (std::is_same_v<CxType, decltype(nullptr)>) {
    260    // We might still have a windowproxy here.  But it shouldn't matter, because
    261    // that's not what the caller is looking for, so we're going to fail out
    262    // anyway below once we do the recursive call to ourselves with wrapper
    263    // unwrapping disabled.
    264    MOZ_ASSERT(!js::IsWrapper(unwrappedObj) || js::IsWindowProxy(unwrappedObj));
    265  } else {
    266    // We shouldn't have a wrapper by now.
    267    MOZ_ASSERT(!js::IsWrapper(unwrappedObj));
    268  }
    269 
    270  // Recursive call is OK, because now we're using false for mayBeWrapper and
    271  // we never reach this code if that boolean is false, so can't keep calling
    272  // ourselves.
    273  //
    274  // Unwrap into a temporary pointer, because in general unwrapping into
    275  // something of type U might trigger GC (e.g. release the value currently
    276  // stored in there, with arbitrary consequences) and invalidate the
    277  // "unwrappedObj" pointer.
    278  T* tempValue = nullptr;
    279  nsresult rv = UnwrapObjectInternal<T, false>(unwrappedObj, tempValue, protoID,
    280                                               protoDepth, nullptr);
    281  if (NS_SUCCEEDED(rv)) {
    282    // Suppress a hazard related to keeping tempValue alive across
    283    // UnwrapObjectInternal, because the analysis can't tell that this function
    284    // will not GC if maybeWrapped=False and we've already gone through a level
    285    // of unwrapping so unwrappedObj will be !IsWrapper.
    286    JS::AutoSuppressGCAnalysis suppress;
    287 
    288    // It's very important to not update "obj" with the "unwrappedObj" value
    289    // until we know the unwrap has succeeded.  Otherwise, in a situation in
    290    // which we have an overload of object and primitive we could end up
    291    // converting to the primitive from the unwrappedObj, whereas we want to do
    292    // it from the original object.
    293    obj = unwrappedObj;
    294    // And now assign to "value"; at this point we don't care if a GC happens
    295    // and invalidates unwrappedObj.
    296    value = tempValue;
    297    return NS_OK;
    298  }
    299 
    300  /* It's the wrong sort of DOM object */
    301  return NS_ERROR_XPC_BAD_CONVERT_JS;
    302 }
    303 
    304 struct MutableObjectHandleWrapper {
    305  explicit MutableObjectHandleWrapper(JS::MutableHandle<JSObject*> aHandle)
    306      : mHandle(aHandle) {}
    307 
    308  void operator=(JSObject* aObject) {
    309    MOZ_ASSERT(aObject);
    310    mHandle.set(aObject);
    311  }
    312 
    313  operator JSObject*() const { return mHandle; }
    314 
    315 private:
    316  JS::MutableHandle<JSObject*> mHandle;
    317 };
    318 
    319 struct MutableValueHandleWrapper {
    320  explicit MutableValueHandleWrapper(JS::MutableHandle<JS::Value> aHandle)
    321      : mHandle(aHandle) {}
    322 
    323  void operator=(JSObject* aObject) {
    324    MOZ_ASSERT(aObject);
    325    mHandle.setObject(*aObject);
    326  }
    327 
    328  operator JSObject*() const { return &mHandle.toObject(); }
    329 
    330 private:
    331  JS::MutableHandle<JS::Value> mHandle;
    332 };
    333 
    334 }  // namespace binding_detail
    335 
    336 // UnwrapObject overloads that ensure we have a MutableHandle to keep it alive.
    337 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    338 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JSObject*> obj,
    339                                        U& value, const CxType& cx) {
    340  binding_detail::MutableObjectHandleWrapper wrapper(obj);
    341  return binding_detail::UnwrapObjectInternal<T, true>(
    342      wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    343 }
    344 
    345 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    346 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JS::Value> obj,
    347                                        U& value, const CxType& cx) {
    348  MOZ_ASSERT(obj.isObject());
    349  binding_detail::MutableValueHandleWrapper wrapper(obj);
    350  return binding_detail::UnwrapObjectInternal<T, true>(
    351      wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    352 }
    353 
    354 // UnwrapObject overloads that ensure we have a strong ref to keep it alive.
    355 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    356 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, RefPtr<U>& value,
    357                                        const CxType& cx) {
    358  return binding_detail::UnwrapObjectInternal<T, true>(
    359      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    360 }
    361 
    362 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    363 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, nsCOMPtr<U>& value,
    364                                        const CxType& cx) {
    365  return binding_detail::UnwrapObjectInternal<T, true>(
    366      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    367 }
    368 
    369 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    370 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull<U>& value,
    371                                        const CxType& cx) {
    372  return binding_detail::UnwrapObjectInternal<T, true>(
    373      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    374 }
    375 
    376 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    377 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, NonNull<U>& value,
    378                                        const CxType& cx) {
    379  return binding_detail::UnwrapObjectInternal<T, true>(
    380      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
    381 }
    382 
    383 // An UnwrapObject overload that just calls one of the JSObject* ones.
    384 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    385 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
    386                                        const CxType& cx) {
    387  MOZ_ASSERT(obj.isObject());
    388  return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
    389 }
    390 
    391 template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
    392 MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj,
    393                                        NonNull<U>& value, const CxType& cx) {
    394  MOZ_ASSERT(obj.isObject());
    395  return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
    396 }
    397 
    398 template <prototypes::ID PrototypeID>
    399 MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() {
    400  static_assert(PrototypeID != prototypes::id::Window,
    401                "Can't do static unwrap of WindowProxy; use "
    402                "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
    403                "aware version of IS_INSTANCE_OF");
    404  static_assert(PrototypeID != prototypes::id::EventTarget,
    405                "Can't do static unwrap of WindowProxy (which an EventTarget "
    406                "might be); use UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a "
    407                "cross-origin-object aware version of IS_INSTANCE_OF");
    408  static_assert(PrototypeID != prototypes::id::Location,
    409                "Can't do static unwrap of Location; use "
    410                "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
    411                "aware version of IS_INSTANCE_OF");
    412 }
    413 
    414 namespace binding_detail {
    415 // This function is just here so we can do some static asserts in a centralized
    416 // place instead of putting them in every single UnwrapObject overload.
    417 template <prototypes::ID PrototypeID, class T, typename U, typename V>
    418 MOZ_ALWAYS_INLINE nsresult UnwrapObjectWithCrossOriginAsserts(V&& obj,
    419                                                              U& value) {
    420  AssertStaticUnwrapOK<PrototypeID>();
    421  return UnwrapObject<PrototypeID, T>(obj, value, nullptr);
    422 }
    423 }  // namespace binding_detail
    424 
    425 template <prototypes::ID PrototypeID, class T>
    426 MOZ_ALWAYS_INLINE bool IsInstanceOf(JSObject* obj) {
    427  AssertStaticUnwrapOK<PrototypeID>();
    428  void* ignored;
    429  nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
    430      obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
    431  return NS_SUCCEEDED(unwrapped);
    432 }
    433 
    434 template <prototypes::ID PrototypeID, class T, typename U>
    435 MOZ_ALWAYS_INLINE nsresult UnwrapNonWrapperObject(JSObject* obj, U& value) {
    436  MOZ_ASSERT(!js::IsWrapper(obj));
    437  return binding_detail::UnwrapObjectInternal<T, false>(
    438      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
    439 }
    440 
    441 MOZ_ALWAYS_INLINE bool IsConvertibleToDictionary(JS::Handle<JS::Value> val) {
    442  return val.isNullOrUndefined() || val.isObject();
    443 }
    444 
    445 // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
    446 // constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
    447 // The end of the prototype objects should be the start of the interface
    448 // objects, and the end of the interface objects should be the start of the
    449 // named properties objects.
    450 static_assert((size_t)constructors::id::_ID_Start ==
    451                      (size_t)prototypes::id::_ID_Count &&
    452                  (size_t)namedpropertiesobjects::id::_ID_Start ==
    453                      (size_t)constructors::id::_ID_Count,
    454              "Overlapping or discontiguous indexes.");
    455 const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;
    456 
    457 class ProtoAndIfaceCache {
    458  // The caching strategy we use depends on what sort of global we're dealing
    459  // with.  For a window-like global, we want everything to be as fast as
    460  // possible, so we use a flat array, indexed by prototype/constructor ID.
    461  // For everything else (e.g. globals for JSMs), space is more important than
    462  // speed, so we use a two-level lookup table.
    463 
    464  class ArrayCache
    465      : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount> {
    466   public:
    467    bool HasEntryInSlot(size_t i) {
    468      // Do an explicit call to the Heap<…> bool conversion operator. Because
    469      // that operator is marked explicit we'd otherwise end up doing an
    470      // implicit cast to JSObject* first, causing an unnecessary call to
    471      // exposeToActiveJS().
    472      return bool((*this)[i]);
    473    }
    474 
    475    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { return (*this)[i]; }
    476 
    477    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { return (*this)[i]; }
    478 
    479    void Trace(JSTracer* aTracer) {
    480      for (size_t i = 0; i < std::size(*this); ++i) {
    481        JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
    482      }
    483    }
    484 
    485    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    486      return aMallocSizeOf(this);
    487    }
    488  };
    489 
    490  class PageTableCache {
    491   public:
    492    PageTableCache() { memset(mPages.begin(), 0, sizeof(mPages)); }
    493 
    494    ~PageTableCache() {
    495      for (size_t i = 0; i < std::size(mPages); ++i) {
    496        delete mPages[i];
    497      }
    498    }
    499 
    500    bool HasEntryInSlot(size_t i) {
    501      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
    502      size_t pageIndex = i / kPageSize;
    503      size_t leafIndex = i % kPageSize;
    504      Page* p = mPages[pageIndex];
    505      if (!p) {
    506        return false;
    507      }
    508      // Do an explicit call to the Heap<…> bool conversion operator. Because
    509      // that operator is marked explicit we'd otherwise end up doing an
    510      // implicit cast to JSObject* first, causing an unnecessary call to
    511      // exposeToActiveJS().
    512      return bool((*p)[leafIndex]);
    513    }
    514 
    515    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
    516      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
    517      size_t pageIndex = i / kPageSize;
    518      size_t leafIndex = i % kPageSize;
    519      Page* p = mPages[pageIndex];
    520      if (!p) {
    521        p = new Page;
    522        mPages[pageIndex] = p;
    523      }
    524      return (*p)[leafIndex];
    525    }
    526 
    527    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
    528      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
    529      size_t pageIndex = i / kPageSize;
    530      size_t leafIndex = i % kPageSize;
    531      Page* p = mPages[pageIndex];
    532      MOZ_ASSERT(p);
    533      return (*p)[leafIndex];
    534    }
    535 
    536    void Trace(JSTracer* trc) {
    537      for (size_t i = 0; i < std::size(mPages); ++i) {
    538        Page* p = mPages[i];
    539        if (p) {
    540          for (size_t j = 0; j < std::size(*p); ++j) {
    541            JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
    542          }
    543        }
    544      }
    545    }
    546 
    547    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    548      size_t n = aMallocSizeOf(this);
    549      for (size_t i = 0; i < std::size(mPages); ++i) {
    550        n += aMallocSizeOf(mPages[i]);
    551      }
    552      return n;
    553    }
    554 
    555   private:
    556    static const size_t kPageSize = 16;
    557    typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
    558    static const size_t kNPages =
    559        kProtoAndIfaceCacheCount / kPageSize +
    560        size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
    561    Array<Page*, kNPages> mPages;
    562  };
    563 
    564 public:
    565  enum Kind { WindowLike, NonWindowLike };
    566 
    567  explicit ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
    568    MOZ_COUNT_CTOR(ProtoAndIfaceCache);
    569    if (aKind == WindowLike) {
    570      mArrayCache = new ArrayCache();
    571    } else {
    572      mPageTableCache = new PageTableCache();
    573    }
    574  }
    575 
    576  ~ProtoAndIfaceCache() {
    577    if (mKind == WindowLike) {
    578      delete mArrayCache;
    579    } else {
    580      delete mPageTableCache;
    581    }
    582    MOZ_COUNT_DTOR(ProtoAndIfaceCache);
    583  }
    584 
    585 #define FORWARD_OPERATION(opName, args)    \
    586  do {                                     \
    587    if (mKind == WindowLike) {             \
    588      return mArrayCache->opName args;     \
    589    } else {                               \
    590      return mPageTableCache->opName args; \
    591    }                                      \
    592  } while (0)
    593 
    594  // Return whether slot i contains an object.  This doesn't return the object
    595  // itself because in practice consumers just want to know whether it's there
    596  // or not, and that doesn't require barriering, which returning the object
    597  // pointer does.
    598  bool HasEntryInSlot(size_t i) { FORWARD_OPERATION(HasEntryInSlot, (i)); }
    599 
    600  // Return a reference to slot i, creating it if necessary.  There
    601  // may not be an object in the returned slot.
    602  JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
    603    FORWARD_OPERATION(EntrySlotOrCreate, (i));
    604  }
    605 
    606  // Return a reference to slot i, which is guaranteed to already
    607  // exist.  There may not be an object in the slot, if prototype and
    608  // constructor initialization for one of our bindings failed.
    609  JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
    610    FORWARD_OPERATION(EntrySlotMustExist, (i));
    611  }
    612 
    613  void Trace(JSTracer* aTracer) { FORWARD_OPERATION(Trace, (aTracer)); }
    614 
    615  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    616    size_t n = aMallocSizeOf(this);
    617    n += (mKind == WindowLike
    618              ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
    619              : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
    620    return n;
    621  }
    622 #undef FORWARD_OPERATION
    623 
    624 private:
    625  union {
    626    ArrayCache* mArrayCache;
    627    PageTableCache* mPageTableCache;
    628  };
    629  Kind mKind;
    630 };
    631 
    632 inline void AllocateProtoAndIfaceCache(JSObject* obj,
    633                                       ProtoAndIfaceCache::Kind aKind) {
    634  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
    635  MOZ_ASSERT(JS::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());
    636 
    637  ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);
    638 
    639  JS::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
    640                      JS::PrivateValue(protoAndIfaceCache));
    641 }
    642 
    643 #ifdef DEBUG
    644 struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer {
    645  bool ok;
    646 
    647  explicit VerifyTraceProtoAndIfaceCacheCalledTracer(JSContext* cx)
    648      : JS::CallbackTracer(cx, JS::TracerKind::VerifyTraceProtoAndIface),
    649        ok(false) {}
    650 
    651  void onChild(JS::GCCellPtr, const char* name) override {
    652    // We don't do anything here, we only want to verify that
    653    // TraceProtoAndIfaceCache was called.
    654  }
    655 };
    656 #endif
    657 
    658 inline void TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj) {
    659  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
    660 
    661 #ifdef DEBUG
    662  if (trc->kind() == JS::TracerKind::VerifyTraceProtoAndIface) {
    663    // We don't do anything here, we only want to verify that
    664    // TraceProtoAndIfaceCache was called.
    665    static_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok = true;
    666    return;
    667  }
    668 #endif
    669 
    670  if (!DOMGlobalHasProtoAndIFaceCache(obj)) return;
    671  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
    672  protoAndIfaceCache->Trace(trc);
    673 }
    674 
    675 inline void DestroyProtoAndIfaceCache(JSObject* obj) {
    676  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
    677 
    678  if (!DOMGlobalHasProtoAndIFaceCache(obj)) {
    679    return;
    680  }
    681 
    682  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
    683 
    684  delete protoAndIfaceCache;
    685 }
    686 
    687 /**
    688 * Add constants to an object.
    689 */
    690 bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
    691                     const ConstantSpec* cs);
    692 
    693 struct JSNativeHolder {
    694  JSNative mNative;
    695  const NativePropertyHooks* mPropertyHooks;
    696 };
    697 
    698 // Struct for holding information for WebIDL interface objects (which are
    699 // function objects). A pointer to this struct is held in the first reserved
    700 // slot of the function object.
    701 struct DOMInterfaceInfo {
    702  JSNativeHolder nativeHolder;
    703 
    704  ProtoHandleGetter mGetParentProto;
    705 
    706  const uint32_t mDepth;
    707 
    708  const prototypes::ID mPrototypeID;  // uint16_t
    709 
    710  // Boolean indicating whether this object wants a isInstance property
    711  // pointing to InterfaceIsInstance defined on it.  Only ever true for
    712  // interfaces.
    713  bool wantsInterfaceIsInstance;
    714 
    715  uint8_t mConstructorArgs;
    716 
    717  const char* mConstructorName;
    718 };
    719 
    720 struct LegacyFactoryFunction {
    721  const char* mName;
    722  const JSNativeHolder mHolder;
    723  uint8_t mNargs;
    724 };
    725 
    726 namespace binding_detail {
    727 
    728 void CreateInterfaceObjects(
    729    JSContext* cx, JS::Handle<JSObject*> global,
    730    JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
    731    JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
    732    const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
    733    bool isConstructorChromeOnly,
    734    const Span<const LegacyFactoryFunction>& legacyFactoryFunctions,
    735    JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
    736    const NativeProperties* chromeOnlyProperties, const char* name,
    737    bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
    738    const char* const* legacyWindowAliases);
    739 
    740 }  // namespace binding_detail
    741 
    742 // clang-format off
    743 /*
    744 * Create a DOM interface object (if constructorClass is non-null) and/or a
    745 * DOM interface prototype object (if protoClass is non-null).
    746 *
    747 * global is used as the parent of the interface object and the interface
    748 *        prototype object
    749 * protoProto is the prototype to use for the interface prototype object.
    750 * protoClass is the JSClass to use for the interface prototype object.
    751 *            This is null if we should not create an interface prototype
    752 *            object.
    753 * protoCache a pointer to a JSObject pointer where we should cache the
    754 *            interface prototype object. This must be null if protoClass is and
    755 *            vice versa.
    756 * interfaceProto is the prototype to use for the interface object.  This can be
    757 *                null if interfaceInfo is null (as in, if we're not creating an
    758 *                interface object at all).
    759 * interfaceInfo is the info to use for the interface object.  This can be null
    760 *               if we're not creating an interface object.
    761 * ctorNargs is the length of the constructor function; 0 if no constructor
    762 * isConstructorChromeOnly if true, the constructor is ChromeOnly.
    763 * legacyFactoryFunctions the legacy factory functions (can be empty)
    764 * constructorCache a pointer to a JSObject pointer where we should cache the
    765 *                  interface object. This must be null if both constructorClass
    766 *                  and constructor are null, and non-null otherwise.
    767 * properties contains the methods, attributes and constants to be defined on
    768 *            objects in any compartment.
    769 * chromeProperties contains the methods, attributes and constants to be defined
    770 *                  on objects in chrome compartments. This must be null if the
    771 *                  interface doesn't have any ChromeOnly properties or if the
    772 *                  object is being created in non-chrome compartment.
    773 * name the name to use for 1) the WebIDL class string, which is the value
    774 *      that's used for @@toStringTag, 2) the name property for interface
    775 *      objects and 3) the property on the global object that would be set to
    776 *      the interface object. In general this is the interface identifier.
    777 *      LegacyNamespace would expect something different for 1), but we don't
    778 *      support that. The class string for default iterator objects is not
    779 *      usable as 2) or 3), but default iterator objects don't have an interface
    780 *      object.
    781 * defineOnGlobal controls whether properties should be defined on the given
    782 *                global for the interface object (if any) and named
    783 *                constructors (if any) for this interface.  This can be
    784 *                false in situations where we want the properties to only
    785 *                appear on privileged Xrays but not on the unprivileged
    786 *                underlying global.
    787 * unscopableNames if not null it points to a null-terminated list of const
    788 *                 char* names of the unscopable properties for this interface.
    789 * isGlobal if true, we're creating interface objects for a [Global] interface,
    790 *          and hence shouldn't define properties on the prototype object.
    791 * legacyWindowAliases if not null it points to a null-terminated list of const
    792 *                     char* names of the legacy window aliases for this
    793 *                     interface.
    794 *
    795 * At least one of protoClass or interfaceInfo should be non-null. If
    796 * interfaceInfo is non-null, the resulting interface object will be defined on
    797 * the given global with property name |name|, which must also be non-null.
    798 */
    799 // clang-format on
    800 template <size_t N>
    801 inline void CreateInterfaceObjects(
    802    JSContext* cx, JS::Handle<JSObject*> global,
    803    JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
    804    JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
    805    const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
    806    bool isConstructorChromeOnly,
    807    const Span<const LegacyFactoryFunction, N>& legacyFactoryFunctions,
    808    JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
    809    const NativeProperties* chromeOnlyProperties, const char* name,
    810    bool defineOnGlobal, const char* const* unscopableNames, bool isGlobal,
    811    const char* const* legacyWindowAliases) {
    812  // We're using 1 slot for the interface info already, so we only have
    813  // INTERFACE_OBJECT_MAX_SLOTS - 1 slots for legacy factory functions.
    814  static_assert(N <= INTERFACE_OBJECT_MAX_SLOTS -
    815                         INTERFACE_OBJECT_FIRST_LEGACY_FACTORY_FUNCTION);
    816 
    817  return binding_detail::CreateInterfaceObjects(
    818      cx, global, protoProto, protoClass, protoCache, interfaceProto,
    819      interfaceInfo, ctorNargs, isConstructorChromeOnly, legacyFactoryFunctions,
    820      constructorCache, properties, chromeOnlyProperties, name, defineOnGlobal,
    821      unscopableNames, isGlobal, legacyWindowAliases);
    822 }
    823 
    824 /*
    825 * Create a namespace object.
    826 *
    827 * global the global on which to install a property named with name pointing to
    828 *        the namespace object if defineOnGlobal is true.
    829 * namespaceProto is the prototype to use for the namespace object.
    830 * namespaceClass is the JSClass to use for the namespace object.
    831 * namespaceCache a pointer to a JSObject pointer where we should cache the
    832 *                namespace object.
    833 * properties contains the methods, attributes and constants to be defined on
    834 *            objects in any compartment.
    835 * chromeProperties contains the methods, attributes and constants to be defined
    836 *                  on objects in chrome compartments. This must be null if the
    837 *                  namespace doesn't have any ChromeOnly properties or if the
    838 *                  object is being created in non-chrome compartment.
    839 * name the name to use for the WebIDL class string, which is the value
    840 *      that's used for @@toStringTag, and the name of the property on the
    841 *      global object that would be set to the namespace object.
    842 * defineOnGlobal controls whether properties should be defined on the given
    843 *                global for the namespace object. This can be false in
    844 *                situations where we want the properties to only appear on
    845 *                privileged Xrays but not on the unprivileged underlying
    846 *                global.
    847 */
    848 void CreateNamespaceObject(JSContext* cx, JS::Handle<JSObject*> global,
    849                           JS::Handle<JSObject*> namespaceProto,
    850                           const DOMIfaceAndProtoJSClass& namespaceClass,
    851                           JS::Heap<JSObject*>* namespaceCache,
    852                           const NativeProperties* properties,
    853                           const NativeProperties* chromeOnlyProperties,
    854                           const char* name, bool defineOnGlobal);
    855 
    856 /**
    857 * Define the properties (regular and chrome-only) on obj.
    858 *
    859 * obj the object to install the properties on. This should be the interface
    860 *     prototype object for regular interfaces and the instance object for
    861 *     interfaces marked with Global.
    862 * properties contains the methods, attributes and constants to be defined on
    863 *            objects in any compartment.
    864 * chromeProperties contains the methods, attributes and constants to be defined
    865 *                  on objects in chrome compartments. This must be null if the
    866 *                  interface doesn't have any ChromeOnly properties or if the
    867 *                  object is being created in non-chrome compartment.
    868 */
    869 bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
    870                      const NativeProperties* properties,
    871                      const NativeProperties* chromeOnlyProperties);
    872 
    873 /*
    874 * Define the legacy unforgeable methods on an object.
    875 */
    876 bool DefineLegacyUnforgeableMethods(
    877    JSContext* cx, JS::Handle<JSObject*> obj,
    878    const Prefable<const JSFunctionSpec>* props);
    879 
    880 /*
    881 * Define the legacy unforgeable attributes on an object.
    882 */
    883 bool DefineLegacyUnforgeableAttributes(
    884    JSContext* cx, JS::Handle<JSObject*> obj,
    885    const Prefable<const JSPropertySpec>* props);
    886 
    887 #define HAS_MEMBER_TYPEDEFS \
    888 private:                   \
    889  typedef char yes[1];      \
    890  typedef char no[2]
    891 
    892 #ifdef _MSC_VER
    893 #  define HAS_MEMBER_CHECK(_name) \
    894    template <typename V>         \
    895    static yes& Check##_name(char(*)[(&V::_name == 0) + 1])
    896 #else
    897 #  define HAS_MEMBER_CHECK(_name) \
    898    template <typename V>         \
    899    static yes& Check##_name(char(*)[sizeof(&V::_name) + 1])
    900 #endif
    901 
    902 #define HAS_MEMBER(_memberName, _valueName) \
    903 private:                                   \
    904  HAS_MEMBER_CHECK(_memberName);            \
    905  template <typename V>                     \
    906  static no& Check##_memberName(...);       \
    907                                            \
    908 public:                                    \
    909  static bool const _valueName =            \
    910      sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)
    911 
    912 template <class T>
    913 struct NativeHasMember {
    914  HAS_MEMBER_TYPEDEFS;
    915 
    916  HAS_MEMBER(GetParentObject, GetParentObject);
    917  HAS_MEMBER(WrapObject, WrapObject);
    918 };
    919 
    920 template <class T>
    921 struct IsSmartPtr {
    922  HAS_MEMBER_TYPEDEFS;
    923 
    924  HAS_MEMBER(get, value);
    925 };
    926 
    927 template <class T>
    928 struct IsRefcounted {
    929  HAS_MEMBER_TYPEDEFS;
    930 
    931  HAS_MEMBER(AddRef, HasAddref);
    932  HAS_MEMBER(Release, HasRelease);
    933 
    934 public:
    935  static bool const value = HasAddref && HasRelease;
    936 
    937 private:
    938  // This struct only works if T is fully declared (not just forward declared).
    939  // The std::is_base_of check will ensure that, we don't really need it for any
    940  // other reason (the static assert will of course always be true).
    941  static_assert(!std::is_base_of<nsISupports, T>::value || IsRefcounted::value,
    942                "Classes derived from nsISupports are refcounted!");
    943 };
    944 
    945 #undef HAS_MEMBER
    946 #undef HAS_MEMBER_CHECK
    947 #undef HAS_MEMBER_TYPEDEFS
    948 
    949 namespace binding_detail {
    950 
    951 /**
    952 * We support a couple of cases for the classes used as NativeType for bindings:
    953 *
    954 * 1) Classes derived from nsISupports, but not inheriting from nsWrapperCache.
    955 *    For these classes we'll use QI on the nsISupports pointer to get to the
    956 *    nsWrapperCache. The classes for native objects for the binding probably
    957 *    inherit from the NativeType class, and from nsWrapperCache.
    958 * 2) Classes derived from nsISupports, and inheriting from nsWrapperCache. For
    959 *    these we rely on the class or one of its ancestors on the primary
    960 *    inheritance chain that is identical to:
    961 *       class Foo : public nsISupports, public nsWrapperCache {}
    962 *    This allows us to calculate offsets that allow us to cast a void* pointer
    963 *    to the object to nsWrapperCache by adding a fixed offset.
    964 * 3) Classes that are not derived from nsISupports, but inherit from
    965 *    nsWrapperCache. For these we rely on being able to cast a void* pointer
    966 *    directly to nsWrapperCache.
    967 */
    968 
    969 // Small helper that has access to nsWrapperCache internals.
    970 class CastableToWrapperCacheHelper {
    971 public:
    972  template <class T>
    973  static constexpr uintptr_t OffsetOf() {
    974    return offsetof(T, mWrapper) - offsetof(nsWrapperCache, mWrapper);
    975  }
    976 };
    977 
    978 template <size_t Offset>
    979 class CastableToWrapperCache {
    980 protected:
    981  static nsWrapperCache* GetWrapperCache(void* aObj) {
    982    return aObj ? reinterpret_cast<nsWrapperCache*>(uintptr_t(aObj) + Offset)
    983                : nullptr;
    984  }
    985 
    986 public:
    987  static nsWrapperCache* GetWrapperCache(JSObject* aObj) {
    988    return GetWrapperCache(UnwrapPossiblyNotInitializedDOMObject<void>(aObj));
    989  }
    990 
    991  static size_t ObjectMoved(JSObject* aObj, JSObject* aOld) {
    992    JS::AutoAssertGCCallback inCallback;
    993    nsWrapperCache* cache = GetWrapperCache(aObj);
    994    if (cache) {
    995      cache->UpdateWrapper(aObj, aOld);
    996    }
    997 
    998    return 0;
    999  }
   1000  static constexpr js::ClassExtension sClassExtension = {ObjectMoved};
   1001 };
   1002 
   1003 class NeedsQIToWrapperCache {
   1004 protected:
   1005  static nsWrapperCache* GetWrapperCache(nsISupports* aObj) {
   1006    if (!aObj) {
   1007      return nullptr;
   1008    }
   1009    nsWrapperCache* cache;
   1010    CallQueryInterface(aObj, &cache);
   1011    return cache;
   1012  }
   1013 
   1014 public:
   1015  static nsWrapperCache* GetWrapperCache(JSObject* aObj) {
   1016    return GetWrapperCache(
   1017        UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
   1018  }
   1019 
   1020  static size_t ObjectMoved(JSObject* aObj, JSObject* aOld);
   1021  static constexpr js::ClassExtension sClassExtension = {ObjectMoved};
   1022 };
   1023 
   1024 template <class T>
   1025 using ToWrapperCacheHelper = std::conditional_t<
   1026    std::is_base_of_v<nsWrapperCache, T>,
   1027    CastableToWrapperCache<CastableToWrapperCacheHelper::OffsetOf<T>()>,
   1028    NeedsQIToWrapperCache>;
   1029 
   1030 template <class T,
   1031          bool CastableToWrapperCache = std::is_base_of_v<nsWrapperCache, T>>
   1032 class NativeTypeHelpers_nsISupports;
   1033 
   1034 template <class T>
   1035 class NativeTypeHelpers_nsISupports<T, true>
   1036    : public CastableToWrapperCache<
   1037          CastableToWrapperCacheHelper::OffsetOf<T>()> {};
   1038 
   1039 template <class T>
   1040 class NativeTypeHelpers_nsISupports<T, false> : public NeedsQIToWrapperCache {};
   1041 
   1042 template <class T>
   1043 class NativeTypeHelpers_Other
   1044    : public CastableToWrapperCache<
   1045          CastableToWrapperCacheHelper::OffsetOf<T>()> {};
   1046 
   1047 template <class T>
   1048 using NativeTypeHelpers = std::conditional_t<std::is_base_of_v<nsISupports, T>,
   1049                                             NativeTypeHelpers_nsISupports<T>,
   1050                                             NativeTypeHelpers_Other<T>>;
   1051 
   1052 }  // namespace binding_detail
   1053 
   1054 #ifdef DEBUG
   1055 template <class T>
   1056 struct CheckCastableWrapperCache {
   1057  static bool Check() {
   1058    return reinterpret_cast<uintptr_t>(
   1059               static_cast<nsWrapperCache*>(reinterpret_cast<T*>(1))) ==
   1060           1 + binding_detail::CastableToWrapperCacheHelper::OffsetOf<T>();
   1061  }
   1062 };
   1063 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   1064 struct CheckWrapperCacheCast : public CheckCastableWrapperCache<T> {};
   1065 template <class T>
   1066 struct CheckWrapperCacheCast<T, true> {
   1067  static bool Check() {
   1068    if constexpr (std::is_base_of_v<nsWrapperCache, T>) {
   1069      return CheckCastableWrapperCache<T>::Check();
   1070    } else {
   1071      return true;
   1072    }
   1073  }
   1074 };
   1075 #endif
   1076 
   1077 inline bool TryToOuterize(JS::MutableHandle<JS::Value> rval) {
   1078  MOZ_ASSERT(rval.isObject());
   1079  if (js::IsWindow(&rval.toObject())) {
   1080    JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
   1081    MOZ_ASSERT(obj);
   1082    rval.set(JS::ObjectValue(*obj));
   1083  }
   1084 
   1085  return true;
   1086 }
   1087 
   1088 inline bool TryToOuterize(JS::MutableHandle<JSObject*> obj) {
   1089  if (js::IsWindow(obj)) {
   1090    JSObject* proxy = js::ToWindowProxyIfWindow(obj);
   1091    MOZ_ASSERT(proxy);
   1092    obj.set(proxy);
   1093  }
   1094 
   1095  return true;
   1096 }
   1097 
   1098 // Make sure to wrap the given string value into the right compartment, as
   1099 // needed.
   1100 MOZ_ALWAYS_INLINE
   1101 bool MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
   1102  MOZ_ASSERT(rval.isString());
   1103  JSString* str = rval.toString();
   1104  if (JS::GetStringZone(str) != js::GetContextZone(cx)) {
   1105    return JS_WrapValue(cx, rval);
   1106  }
   1107  return true;
   1108 }
   1109 
   1110 // Make sure to wrap the given object value into the right compartment as
   1111 // needed.  This will work correctly, but possibly slowly, on all objects.
   1112 MOZ_ALWAYS_INLINE
   1113 bool MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
   1114  MOZ_ASSERT(rval.isObject());
   1115 
   1116  // Cross-compartment always requires wrapping.
   1117  JSObject* obj = &rval.toObject();
   1118  if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
   1119    return JS_WrapValue(cx, rval);
   1120  }
   1121 
   1122  // We're same-compartment, but we might still need to outerize if we
   1123  // have a Window.
   1124  return TryToOuterize(rval);
   1125 }
   1126 
   1127 // Like MaybeWrapObjectValue, but working with a
   1128 // JS::MutableHandle<JSObject*> which must be non-null.
   1129 MOZ_ALWAYS_INLINE
   1130 bool MaybeWrapObject(JSContext* cx, JS::MutableHandle<JSObject*> obj) {
   1131  if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
   1132    return JS_WrapObject(cx, obj);
   1133  }
   1134 
   1135  // We're same-compartment, but we might still need to outerize if we
   1136  // have a Window.
   1137  return TryToOuterize(obj);
   1138 }
   1139 
   1140 // Like MaybeWrapObjectValue, but also allows null
   1141 MOZ_ALWAYS_INLINE
   1142 bool MaybeWrapObjectOrNullValue(JSContext* cx,
   1143                                JS::MutableHandle<JS::Value> rval) {
   1144  MOZ_ASSERT(rval.isObjectOrNull());
   1145  if (rval.isNull()) {
   1146    return true;
   1147  }
   1148  return MaybeWrapObjectValue(cx, rval);
   1149 }
   1150 
   1151 // Wrapping for objects that are known to not be DOM objects
   1152 MOZ_ALWAYS_INLINE
   1153 bool MaybeWrapNonDOMObjectValue(JSContext* cx,
   1154                                JS::MutableHandle<JS::Value> rval) {
   1155  MOZ_ASSERT(rval.isObject());
   1156  // Compared to MaybeWrapObjectValue we just skip the TryToOuterize call.  The
   1157  // only reason it would be needed is if we have a Window object, which would
   1158  // have a DOM class.  Assert that we don't have any DOM-class objects coming
   1159  // through here.
   1160  MOZ_ASSERT(!GetDOMClass(&rval.toObject()));
   1161 
   1162  JSObject* obj = &rval.toObject();
   1163  if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
   1164    return true;
   1165  }
   1166  return JS_WrapValue(cx, rval);
   1167 }
   1168 
   1169 // Like MaybeWrapNonDOMObjectValue but allows null
   1170 MOZ_ALWAYS_INLINE
   1171 bool MaybeWrapNonDOMObjectOrNullValue(JSContext* cx,
   1172                                      JS::MutableHandle<JS::Value> rval) {
   1173  MOZ_ASSERT(rval.isObjectOrNull());
   1174  if (rval.isNull()) {
   1175    return true;
   1176  }
   1177  return MaybeWrapNonDOMObjectValue(cx, rval);
   1178 }
   1179 
   1180 // If rval is a gcthing and is not in the compartment of cx, wrap rval
   1181 // into the compartment of cx (typically by replacing it with an Xray or
   1182 // cross-compartment wrapper around the original object).
   1183 MOZ_ALWAYS_INLINE bool MaybeWrapValue(JSContext* cx,
   1184                                      JS::MutableHandle<JS::Value> rval) {
   1185  if (rval.isGCThing()) {
   1186    if (rval.isString()) {
   1187      return MaybeWrapStringValue(cx, rval);
   1188    }
   1189    if (rval.isObject()) {
   1190      return MaybeWrapObjectValue(cx, rval);
   1191    }
   1192    // This could be optimized by checking the zone first, similar to
   1193    // the way strings are handled. At present, this is used primarily
   1194    // for structured cloning, so avoiding the overhead of JS_WrapValue
   1195    // calls is less important than for other types.
   1196    if (rval.isBigInt()) {
   1197      return JS_WrapValue(cx, rval);
   1198    }
   1199    MOZ_ASSERT(rval.isSymbol());
   1200    JS_MarkCrossZoneId(cx, JS::PropertyKey::Symbol(rval.toSymbol()));
   1201  }
   1202  return true;
   1203 }
   1204 
   1205 namespace binding_detail {
   1206 enum GetOrCreateReflectorWrapBehavior {
   1207  eWrapIntoContextCompartment,
   1208  eDontWrapIntoContextCompartment
   1209 };
   1210 
   1211 template <class T>
   1212 struct TypeNeedsOuterization {
   1213  // We only need to outerize Window objects, so anything inheriting from
   1214  // nsGlobalWindow (which inherits from EventTarget itself).
   1215  static const bool value = std::is_base_of<nsGlobalWindowInner, T>::value ||
   1216                            std::is_base_of<nsGlobalWindowOuter, T>::value ||
   1217                            std::is_same_v<EventTarget, T>;
   1218 };
   1219 
   1220 #ifdef DEBUG
   1221 template <typename T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   1222 struct CheckWrapperCacheTracing {
   1223  static inline void Check(T* aObject) {}
   1224 };
   1225 
   1226 template <typename T>
   1227 struct CheckWrapperCacheTracing<T, true> {
   1228  static void Check(T* aObject) {
   1229    // Rooting analysis thinks QueryInterface may GC, but we're dealing with
   1230    // a subset of QueryInterface, C++ only types here.
   1231    JS::AutoSuppressGCAnalysis nogc;
   1232 
   1233    nsWrapperCache* wrapperCacheFromQI = nullptr;
   1234    aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
   1235                            reinterpret_cast<void**>(&wrapperCacheFromQI));
   1236 
   1237    MOZ_ASSERT(wrapperCacheFromQI,
   1238               "Missing nsWrapperCache from QueryInterface implementation?");
   1239 
   1240    if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
   1241      // Can't assert that we trace the wrapper, since we don't have any
   1242      // wrapper to trace.
   1243      return;
   1244    }
   1245 
   1246    nsISupports* ccISupports = nullptr;
   1247    aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
   1248                            reinterpret_cast<void**>(&ccISupports));
   1249    MOZ_ASSERT(ccISupports,
   1250               "nsWrapperCache object which isn't cycle collectable?");
   1251 
   1252    nsXPCOMCycleCollectionParticipant* participant = nullptr;
   1253    CallQueryInterface(ccISupports, &participant);
   1254    MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");
   1255 
   1256    wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
   1257  }
   1258 };
   1259 
   1260 void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
   1261                                  JS::Handle<JSObject*> aGivenProto);
   1262 #endif  // DEBUG
   1263 
   1264 template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
   1265 MOZ_ALWAYS_INLINE bool DoGetOrCreateDOMReflector(
   1266    JSContext* cx, T* value, JS::Handle<JSObject*> givenProto,
   1267    JS::MutableHandle<JS::Value> rval) {
   1268  MOZ_ASSERT(value);
   1269  MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
   1270  JSObject* obj = value->GetWrapper();
   1271  if (obj) {
   1272 #ifdef DEBUG
   1273    AssertReflectorHasGivenProto(cx, obj, givenProto);
   1274    // Have to reget obj because AssertReflectorHasGivenProto can
   1275    // trigger gc so the pointer may now be invalid.
   1276    obj = value->GetWrapper();
   1277 #endif
   1278  } else {
   1279    obj = value->WrapObject(cx, givenProto);
   1280    if (!obj) {
   1281      // At this point, obj is null, so just return false.
   1282      // Callers seem to be testing JS_IsExceptionPending(cx) to
   1283      // figure out whether WrapObject() threw.
   1284      return false;
   1285    }
   1286 
   1287 #ifdef DEBUG
   1288    if (std::is_base_of<nsWrapperCache, T>::value) {
   1289      CheckWrapperCacheTracing<T>::Check(value);
   1290    }
   1291 #endif
   1292  }
   1293 
   1294 #ifdef DEBUG
   1295  const DOMJSClass* clasp = GetDOMClass(obj);
   1296  // clasp can be null if the cache contained a non-DOM object.
   1297  if (clasp) {
   1298    // Some sanity asserts about our object.  Specifically:
   1299    // 1)  If our class claims we're nsISupports, we better be nsISupports
   1300    //     XXXbz ideally, we could assert that reinterpret_cast to nsISupports
   1301    //     does the right thing, but I don't see a way to do it.  :(
   1302    // 2)  If our class doesn't claim we're nsISupports we better be
   1303    //     reinterpret_castable to nsWrapperCache.
   1304    MOZ_ASSERT(clasp, "What happened here?");
   1305    MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports,
   1306                  (std::is_base_of<nsISupports, T>::value));
   1307    MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
   1308  }
   1309 #endif
   1310 
   1311  rval.set(JS::ObjectValue(*obj));
   1312 
   1313  if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
   1314    return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
   1315  }
   1316 
   1317  if (wrapBehavior == eDontWrapIntoContextCompartment) {
   1318    if (TypeNeedsOuterization<T>::value) {
   1319      JSAutoRealm ar(cx, obj);
   1320      return TryToOuterize(rval);
   1321    }
   1322 
   1323    return true;
   1324  }
   1325 
   1326  return JS_WrapValue(cx, rval);
   1327 }
   1328 
   1329 }  // namespace binding_detail
   1330 
   1331 // Create a JSObject wrapping "value", if there isn't one already, and store it
   1332 // in rval.  "value" must be a concrete class that implements a
   1333 // GetWrapperPreserveColor() which can return its existing wrapper, if any, and
   1334 // a WrapObject() which will try to create a wrapper. Typically, this is done by
   1335 // having "value" inherit from nsWrapperCache.
   1336 //
   1337 // The value stored in rval will be ready to be exposed to whatever JS
   1338 // is running on cx right now.  In particular, it will be in the
   1339 // compartment of cx, and outerized as needed.
   1340 template <class T>
   1341 MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflector(
   1342    JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval,
   1343    JS::Handle<JSObject*> givenProto = nullptr) {
   1344  using namespace binding_detail;
   1345  return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(
   1346      cx, value, givenProto, rval);
   1347 }
   1348 
   1349 // Like GetOrCreateDOMReflector but doesn't wrap into the context compartment,
   1350 // and hence does not actually require cx to be in a compartment.
   1351 template <class T>
   1352 MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflectorNoWrap(
   1353    JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval) {
   1354  using namespace binding_detail;
   1355  return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(
   1356      cx, value, nullptr, rval);
   1357 }
   1358 
   1359 // Helper for different overloadings of WrapNewBindingNonWrapperCachedObject()
   1360 inline bool FinishWrapping(JSContext* cx, JS::Handle<JSObject*> obj,
   1361                           JS::MutableHandle<JS::Value> rval) {
   1362  // We can end up here in all sorts of compartments, per comments in
   1363  // WrapNewBindingNonWrapperCachedObject(). Make sure to JS_WrapValue!
   1364  rval.set(JS::ObjectValue(*obj));
   1365  return MaybeWrapObjectValue(cx, rval);
   1366 }
   1367 
   1368 // Create a JSObject wrapping "value", for cases when "value" is a
   1369 // non-wrapper-cached object using WebIDL bindings.  "value" must implement a
   1370 // WrapObject() method taking a JSContext and a prototype (possibly null) and
   1371 // returning the resulting object via a MutableHandle<JSObject*> outparam.
   1372 template <class T>
   1373 inline bool WrapNewBindingNonWrapperCachedObject(
   1374    JSContext* cx, JS::Handle<JSObject*> scopeArg, T* value,
   1375    JS::MutableHandle<JS::Value> rval,
   1376    JS::Handle<JSObject*> givenProto = nullptr) {
   1377  static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
   1378  MOZ_ASSERT(value);
   1379  // We try to wrap in the realm of the underlying object of "scope"
   1380  JS::Rooted<JSObject*> obj(cx);
   1381  {
   1382    // scope for the JSAutoRealm so that we restore the realm
   1383    // before we call JS_WrapValue.
   1384    Maybe<JSAutoRealm> ar;
   1385    // Maybe<Handle> doesn't so much work, and in any case, adding
   1386    // more Maybe (one for a Rooted and one for a Handle) adds more
   1387    // code (and branches!) than just adding a single rooted.
   1388    JS::Rooted<JSObject*> scope(cx, scopeArg);
   1389    JS::Rooted<JSObject*> proto(cx, givenProto);
   1390    if (js::IsWrapper(scope)) {
   1391      // We are working in the Realm of cx and will be producing our reflector
   1392      // there, so we need to succeed if that realm has access to the scope.
   1393      scope =
   1394          js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
   1395      if (!scope) return false;
   1396      ar.emplace(cx, scope);
   1397      if (!JS_WrapObject(cx, &proto)) {
   1398        return false;
   1399      }
   1400    } else {
   1401      // cx and scope are same-compartment, but they might still be
   1402      // different-Realm.  Enter the Realm of scope, since that's
   1403      // where we want to create our object.
   1404      ar.emplace(cx, scope);
   1405    }
   1406 
   1407    MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
   1408    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
   1409    if (!value->WrapObject(cx, proto, &obj)) {
   1410      return false;
   1411    }
   1412  }
   1413 
   1414  return FinishWrapping(cx, obj, rval);
   1415 }
   1416 
   1417 // Create a JSObject wrapping "value", for cases when "value" is a
   1418 // non-wrapper-cached owned object using WebIDL bindings.  "value" must
   1419 // implement a WrapObject() method taking a taking a JSContext and a prototype
   1420 // (possibly null) and returning two pieces of information: the resulting object
   1421 // via a MutableHandle<JSObject*> outparam and a boolean return value that is
   1422 // true if the JSObject took ownership
   1423 template <class T>
   1424 inline bool WrapNewBindingNonWrapperCachedObject(
   1425    JSContext* cx, JS::Handle<JSObject*> scopeArg, UniquePtr<T>& value,
   1426    JS::MutableHandle<JS::Value> rval,
   1427    JS::Handle<JSObject*> givenProto = nullptr) {
   1428  static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
   1429  // We do a runtime check on value, because otherwise we might in
   1430  // fact end up wrapping a null and invoking methods on it later.
   1431  if (!value) {
   1432    MOZ_CRASH("Don't try to wrap null objects");
   1433  }
   1434  // We try to wrap in the realm of the underlying object of "scope"
   1435  JS::Rooted<JSObject*> obj(cx);
   1436  {
   1437    // scope for the JSAutoRealm so that we restore the realm
   1438    // before we call JS_WrapValue.
   1439    Maybe<JSAutoRealm> ar;
   1440    // Maybe<Handle> doesn't so much work, and in any case, adding
   1441    // more Maybe (one for a Rooted and one for a Handle) adds more
   1442    // code (and branches!) than just adding a single rooted.
   1443    JS::Rooted<JSObject*> scope(cx, scopeArg);
   1444    JS::Rooted<JSObject*> proto(cx, givenProto);
   1445    if (js::IsWrapper(scope)) {
   1446      // We are working in the Realm of cx and will be producing our reflector
   1447      // there, so we need to succeed if that realm has access to the scope.
   1448      scope =
   1449          js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
   1450      if (!scope) return false;
   1451      ar.emplace(cx, scope);
   1452      if (!JS_WrapObject(cx, &proto)) {
   1453        return false;
   1454      }
   1455    } else {
   1456      // cx and scope are same-compartment, but they might still be
   1457      // different-Realm.  Enter the Realm of scope, since that's
   1458      // where we want to create our object.
   1459      ar.emplace(cx, scope);
   1460    }
   1461 
   1462    MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
   1463    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
   1464    if (!value->WrapObject(cx, proto, &obj)) {
   1465      return false;
   1466    }
   1467 
   1468    // JS object took ownership
   1469    (void)value.release();
   1470  }
   1471 
   1472  return FinishWrapping(cx, obj, rval);
   1473 }
   1474 
   1475 // Helper for smart pointers (nsRefPtr/nsCOMPtr).
   1476 template <template <typename> class SmartPtr, typename T,
   1477          typename U = std::enable_if_t<IsRefcounted<T>::value, T>,
   1478          typename V = std::enable_if_t<IsSmartPtr<SmartPtr<T>>::value, T>>
   1479 inline bool WrapNewBindingNonWrapperCachedObject(
   1480    JSContext* cx, JS::Handle<JSObject*> scope, const SmartPtr<T>& value,
   1481    JS::MutableHandle<JS::Value> rval,
   1482    JS::Handle<JSObject*> givenProto = nullptr) {
   1483  return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
   1484                                              givenProto);
   1485 }
   1486 
   1487 // Helper for object references (as opposed to pointers).
   1488 template <typename T, typename U = std::enable_if_t<!IsSmartPtr<T>::value, T>>
   1489 inline bool WrapNewBindingNonWrapperCachedObject(
   1490    JSContext* cx, JS::Handle<JSObject*> scope, T& value,
   1491    JS::MutableHandle<JS::Value> rval,
   1492    JS::Handle<JSObject*> givenProto = nullptr) {
   1493  return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
   1494                                              givenProto);
   1495 }
   1496 
   1497 template <bool Fatal>
   1498 inline bool EnumValueNotFound(BindingCallContext& cx, JS::Handle<JSString*> str,
   1499                              const char* type, const char* sourceDescription);
   1500 
   1501 template <>
   1502 inline bool EnumValueNotFound<false>(BindingCallContext& cx,
   1503                                     JS::Handle<JSString*> str,
   1504                                     const char* type,
   1505                                     const char* sourceDescription) {
   1506  // TODO: Log a warning to the console.
   1507  return true;
   1508 }
   1509 
   1510 template <>
   1511 inline bool EnumValueNotFound<true>(BindingCallContext& cx,
   1512                                    JS::Handle<JSString*> str, const char* type,
   1513                                    const char* sourceDescription) {
   1514  JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
   1515  if (!deflated) {
   1516    return false;
   1517  }
   1518  return cx.ThrowErrorMessage<MSG_INVALID_ENUM_VALUE>(sourceDescription,
   1519                                                      deflated.get(), type);
   1520 }
   1521 
   1522 namespace binding_detail {
   1523 
   1524 template <typename CharT>
   1525 inline int FindEnumStringIndexImpl(const CharT* chars, size_t length,
   1526                                   const Span<const nsLiteralCString>& values) {
   1527  for (size_t i = 0; i < values.Length(); ++i) {
   1528    const nsLiteralCString& value = values[i];
   1529    if (length != value.Length()) {
   1530      continue;
   1531    }
   1532 
   1533    bool equal = true;
   1534    for (size_t j = 0; j != length; ++j) {
   1535      if (unsigned(value.CharAt(j)) != unsigned(chars[j])) {
   1536        equal = false;
   1537        break;
   1538      }
   1539    }
   1540 
   1541    if (equal) {
   1542      return (int)i;
   1543    }
   1544  }
   1545 
   1546  return -1;
   1547 }
   1548 
   1549 template <bool InvalidValueFatal>
   1550 inline bool FindEnumStringIndex(BindingCallContext& cx, JS::Handle<JS::Value> v,
   1551                                const Span<const nsLiteralCString>& values,
   1552                                const char* type, const char* sourceDescription,
   1553                                int* index) {
   1554  // JS_StringEqualsAscii is slow as molasses, so don't use it here.
   1555  JS::Rooted<JSString*> str(cx, JS::ToString(cx, v));
   1556  if (!str) {
   1557    return false;
   1558  }
   1559 
   1560  {
   1561    size_t length;
   1562    JS::AutoCheckCannotGC nogc;
   1563    if (JS::StringHasLatin1Chars(str)) {
   1564      const JS::Latin1Char* chars =
   1565          JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length);
   1566      if (!chars) {
   1567        return false;
   1568      }
   1569      *index = FindEnumStringIndexImpl(chars, length, values);
   1570    } else {
   1571      const char16_t* chars =
   1572          JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length);
   1573      if (!chars) {
   1574        return false;
   1575      }
   1576      *index = FindEnumStringIndexImpl(chars, length, values);
   1577    }
   1578    if (*index >= 0) {
   1579      return true;
   1580    }
   1581  }
   1582 
   1583  return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
   1584 }
   1585 
   1586 }  // namespace binding_detail
   1587 
   1588 template <typename Enum, class StringT>
   1589 inline Maybe<Enum> StringToEnum(const StringT& aString) {
   1590  int index = binding_detail::FindEnumStringIndexImpl(
   1591      aString.BeginReading(), aString.Length(),
   1592      binding_detail::EnumStrings<Enum>::Values);
   1593  return index >= 0 ? Some(static_cast<Enum>(index)) : Nothing();
   1594 }
   1595 
   1596 template <typename Enum>
   1597 inline constexpr const nsLiteralCString& GetEnumString(Enum stringId) {
   1598  MOZ_RELEASE_ASSERT(static_cast<size_t>(stringId) <
   1599                     std::size(binding_detail::EnumStrings<Enum>::Values));
   1600  return binding_detail::EnumStrings<Enum>::Values[static_cast<size_t>(
   1601      stringId)];
   1602 }
   1603 
   1604 template <typename Enum>
   1605 constexpr mozilla::detail::EnumeratedRange<Enum> MakeWebIDLEnumeratedRange() {
   1606  return MakeInclusiveEnumeratedRange(ContiguousEnumValues<Enum>::min,
   1607                                      ContiguousEnumValues<Enum>::max);
   1608 }
   1609 
   1610 inline nsWrapperCache* GetWrapperCache(const ParentObject& aParentObject) {
   1611  return aParentObject.mWrapperCache;
   1612 }
   1613 
   1614 template <class T>
   1615 inline T* GetParentPointer(T* aObject) {
   1616  return aObject;
   1617 }
   1618 
   1619 inline nsISupports* GetParentPointer(const ParentObject& aObject) {
   1620  return aObject.mObject;
   1621 }
   1622 
   1623 template <typename T>
   1624 inline mozilla::dom::ReflectionScope GetReflectionScope(T* aParentObject) {
   1625  return mozilla::dom::ReflectionScope::Content;
   1626 }
   1627 
   1628 inline mozilla::dom::ReflectionScope GetReflectionScope(
   1629    const ParentObject& aParentObject) {
   1630  return aParentObject.mReflectionScope;
   1631 }
   1632 
   1633 template <class T>
   1634 inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
   1635  MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
   1636             (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
   1637  cache->ClearWrapper(obj);
   1638 }
   1639 
   1640 template <class T>
   1641 inline void ClearWrapper(T* p, void*, JSObject* obj) {
   1642  // QueryInterface to nsWrapperCache can't GC, we hope.
   1643  JS::AutoSuppressGCAnalysis nogc;
   1644 
   1645  nsWrapperCache* cache;
   1646  CallQueryInterface(p, &cache);
   1647  ClearWrapper(p, cache, obj);
   1648 }
   1649 
   1650 template <class T>
   1651 inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj,
   1652                          const JSObject* old) {
   1653  JS::AutoAssertGCCallback inCallback;
   1654  cache->UpdateWrapper(obj, old);
   1655 }
   1656 
   1657 template <class T>
   1658 inline void UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old) {
   1659  JS::AutoAssertGCCallback inCallback;
   1660  nsWrapperCache* cache;
   1661  CallQueryInterface(p, &cache);
   1662  UpdateWrapper(p, cache, obj, old);
   1663 }
   1664 
   1665 // Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
   1666 // Return true if we successfully preserved the wrapper, or there is no wrapper
   1667 // to preserve. In the latter case we don't need to preserve the wrapper,
   1668 // because the object can only be obtained by JS once, or they cannot be
   1669 // meaningfully owned from the native side.
   1670 //
   1671 // This operation will return false only for non-nsISupports cycle-collected
   1672 // objects, because we cannot determine if they are wrappercached or not.
   1673 bool TryPreserveWrapper(JS::Handle<JSObject*> obj);
   1674 
   1675 bool HasReleasedWrapper(JS::Handle<JSObject*> obj);
   1676 
   1677 // Can only be called with a DOM JSClass.
   1678 bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
   1679                                  uint32_t depth);
   1680 
   1681 // Only set allowNativeWrapper to false if you really know you need it; if in
   1682 // doubt use true. Setting it to false disables security wrappers.
   1683 bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
   1684                        xpcObjectHelper& helper, const nsIID* iid,
   1685                        bool allowNativeWrapper,
   1686                        JS::MutableHandle<JS::Value> rval);
   1687 
   1688 // Special-cased wrapping for variants
   1689 bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
   1690                    JS::MutableHandle<JS::Value> aRetval);
   1691 
   1692 // Wrap an object "p" which is not using WebIDL bindings yet.  This _will_
   1693 // actually work on WebIDL binding objects that are wrappercached, but will be
   1694 // much slower than GetOrCreateDOMReflector.  "cache" must either be null or be
   1695 // the nsWrapperCache for "p".
   1696 template <class T>
   1697 inline bool WrapObject(JSContext* cx, T* p, nsWrapperCache* cache,
   1698                       const nsIID* iid, JS::MutableHandle<JS::Value> rval) {
   1699  if (xpc_FastGetCachedWrapper(cx, cache, rval)) return true;
   1700  xpcObjectHelper helper(ToSupports(p), cache);
   1701  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   1702  return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
   1703 }
   1704 
   1705 // A specialization of the above for nsIVariant, because that needs to
   1706 // do something different.
   1707 template <>
   1708 inline bool WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
   1709                                   nsWrapperCache* cache, const nsIID* iid,
   1710                                   JS::MutableHandle<JS::Value> rval) {
   1711  MOZ_ASSERT(iid);
   1712  MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
   1713  return VariantToJsval(cx, p, rval);
   1714 }
   1715 
   1716 // Wrap an object "p" which is not using WebIDL bindings yet.  Just like the
   1717 // variant that takes an nsWrapperCache above, but will try to auto-derive the
   1718 // nsWrapperCache* from "p".
   1719 template <class T>
   1720 inline bool WrapObject(JSContext* cx, T* p, const nsIID* iid,
   1721                       JS::MutableHandle<JS::Value> rval) {
   1722  return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
   1723 }
   1724 
   1725 // Just like the WrapObject above, but without requiring you to pick which
   1726 // interface you're wrapping as.  This should only be used for objects that have
   1727 // classinfo, for which it doesn't matter what IID is used to wrap.
   1728 template <class T>
   1729 inline bool WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval) {
   1730  return WrapObject(cx, p, nullptr, rval);
   1731 }
   1732 
   1733 // Helper to make it possible to wrap directly out of an nsCOMPtr
   1734 template <class T>
   1735 inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p, const nsIID* iid,
   1736                       JS::MutableHandle<JS::Value> rval) {
   1737  return WrapObject(cx, p.get(), iid, rval);
   1738 }
   1739 
   1740 // Helper to make it possible to wrap directly out of an nsCOMPtr
   1741 template <class T>
   1742 inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
   1743                       JS::MutableHandle<JS::Value> rval) {
   1744  return WrapObject(cx, p, nullptr, rval);
   1745 }
   1746 
   1747 // Helper to make it possible to wrap directly out of an nsRefPtr
   1748 template <class T>
   1749 inline bool WrapObject(JSContext* cx, const RefPtr<T>& p, const nsIID* iid,
   1750                       JS::MutableHandle<JS::Value> rval) {
   1751  return WrapObject(cx, p.get(), iid, rval);
   1752 }
   1753 
   1754 // Helper to make it possible to wrap directly out of an nsRefPtr
   1755 template <class T>
   1756 inline bool WrapObject(JSContext* cx, const RefPtr<T>& p,
   1757                       JS::MutableHandle<JS::Value> rval) {
   1758  return WrapObject(cx, p, nullptr, rval);
   1759 }
   1760 
   1761 // Specialization to make it easy to use WrapObject in codegen.
   1762 template <>
   1763 inline bool WrapObject<JSObject>(JSContext* cx, JSObject* p,
   1764                                 JS::MutableHandle<JS::Value> rval) {
   1765  rval.set(JS::ObjectOrNullValue(p));
   1766  return true;
   1767 }
   1768 
   1769 inline bool WrapObject(JSContext* cx, JSObject& p,
   1770                       JS::MutableHandle<JS::Value> rval) {
   1771  rval.set(JS::ObjectValue(p));
   1772  return true;
   1773 }
   1774 
   1775 bool WrapObject(JSContext* cx, const WindowProxyHolder& p,
   1776                JS::MutableHandle<JS::Value> rval);
   1777 
   1778 // Given an object "p" that inherits from nsISupports, wrap it and return the
   1779 // result.  Null is returned on wrapping failure.  This is somewhat similar to
   1780 // WrapObject() above, but does NOT allow Xrays around the result, since we
   1781 // don't want those for our parent object.
   1782 template <typename T>
   1783 static inline JSObject* WrapNativeISupports(JSContext* cx, T* p,
   1784                                            nsWrapperCache* cache) {
   1785  JS::Rooted<JSObject*> retval(cx);
   1786  {
   1787    xpcObjectHelper helper(ToSupports(p), cache);
   1788    JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
   1789    JS::Rooted<JS::Value> v(cx);
   1790    retval = XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v)
   1791                 ? v.toObjectOrNull()
   1792                 : nullptr;
   1793  }
   1794  return retval;
   1795 }
   1796 
   1797 // Wrapping of our native parent, for cases when it's a WebIDL object.
   1798 template <typename T, bool hasWrapObject = NativeHasMember<T>::WrapObject>
   1799 struct WrapNativeHelper {
   1800  static inline JSObject* Wrap(JSContext* cx, T* parent,
   1801                               nsWrapperCache* cache) {
   1802    MOZ_ASSERT(cache);
   1803 
   1804    JSObject* obj;
   1805    if ((obj = cache->GetWrapper())) {
   1806      // GetWrapper always unmarks gray.
   1807      JS::AssertObjectIsNotGray(obj);
   1808      return obj;
   1809    }
   1810 
   1811    // WrapObject never returns a gray thing.
   1812    obj = parent->WrapObject(cx, nullptr);
   1813    JS::AssertObjectIsNotGray(obj);
   1814 
   1815    return obj;
   1816  }
   1817 };
   1818 
   1819 // Wrapping of our native parent, for cases when it's not a WebIDL object.  In
   1820 // this case it must be nsISupports.
   1821 template <typename T>
   1822 struct WrapNativeHelper<T, false> {
   1823  static inline JSObject* Wrap(JSContext* cx, T* parent,
   1824                               nsWrapperCache* cache) {
   1825    JSObject* obj;
   1826    if (cache && (obj = cache->GetWrapper())) {
   1827 #ifdef DEBUG
   1828      JS::Rooted<JSObject*> rootedObj(cx, obj);
   1829      NS_ASSERTION(WrapNativeISupports(cx, parent, cache) == rootedObj,
   1830                   "Unexpected object in nsWrapperCache");
   1831      obj = rootedObj;
   1832 #endif
   1833      JS::AssertObjectIsNotGray(obj);
   1834      return obj;
   1835    }
   1836 
   1837    obj = WrapNativeISupports(cx, parent, cache);
   1838    JS::AssertObjectIsNotGray(obj);
   1839    return obj;
   1840  }
   1841 };
   1842 
   1843 // Finding the associated global for an object.
   1844 template <typename T>
   1845 static inline JSObject* FindAssociatedGlobal(
   1846    JSContext* cx, T* p, nsWrapperCache* cache,
   1847    mozilla::dom::ReflectionScope scope =
   1848        mozilla::dom::ReflectionScope::Content) {
   1849  if (!p) {
   1850    return JS::CurrentGlobalOrNull(cx);
   1851  }
   1852 
   1853  JSObject* obj = WrapNativeHelper<T>::Wrap(cx, p, cache);
   1854  if (!obj) {
   1855    return nullptr;
   1856  }
   1857  JS::AssertObjectIsNotGray(obj);
   1858 
   1859  // The object is never a CCW but it may not be in the current compartment of
   1860  // the JSContext.
   1861  obj = JS::GetNonCCWObjectGlobal(obj);
   1862 
   1863  switch (scope) {
   1864    case mozilla::dom::ReflectionScope::NAC: {
   1865      return xpc::NACScope(obj);
   1866    }
   1867 
   1868    case mozilla::dom::ReflectionScope::UAWidget: {
   1869      // If scope is set to UAWidgetScope, it means that the canonical reflector
   1870      // for this native object should live in the UA widget scope.
   1871      if (xpc::IsInUAWidgetScope(obj)) {
   1872        return obj;
   1873      }
   1874      JS::Rooted<JSObject*> rootedObj(cx, obj);
   1875      JSObject* uaWidgetScope = xpc::GetUAWidgetScope(cx, rootedObj);
   1876      MOZ_ASSERT_IF(uaWidgetScope, JS_IsGlobalObject(uaWidgetScope));
   1877      JS::AssertObjectIsNotGray(uaWidgetScope);
   1878      return uaWidgetScope;
   1879    }
   1880 
   1881    case ReflectionScope::Content:
   1882      return obj;
   1883  }
   1884 
   1885  MOZ_CRASH("Unknown ReflectionScope variant");
   1886 
   1887  return nullptr;
   1888 }
   1889 
   1890 // Finding of the associated global for an object, when we don't want to
   1891 // explicitly pass in things like the nsWrapperCache for it.
   1892 template <typename T>
   1893 static inline JSObject* FindAssociatedGlobal(JSContext* cx, const T& p) {
   1894  return FindAssociatedGlobal(cx, GetParentPointer(p), GetWrapperCache(p),
   1895                              GetReflectionScope(p));
   1896 }
   1897 
   1898 // Specialization for the case of nsIGlobalObject, since in that case
   1899 // we can just get the JSObject* directly.
   1900 template <>
   1901 inline JSObject* FindAssociatedGlobal(JSContext* cx,
   1902                                      nsIGlobalObject* const& p) {
   1903  if (!p) {
   1904    return JS::CurrentGlobalOrNull(cx);
   1905  }
   1906 
   1907  JSObject* global = p->GetGlobalJSObject();
   1908  if (!global) {
   1909    // nsIGlobalObject doesn't have a JS object anymore,
   1910    // fallback to the current global.
   1911    return JS::CurrentGlobalOrNull(cx);
   1912  }
   1913 
   1914  MOZ_ASSERT(JS_IsGlobalObject(global));
   1915  JS::AssertObjectIsNotGray(global);
   1916  return global;
   1917 }
   1918 
   1919 template <typename T,
   1920          bool hasAssociatedGlobal = NativeHasMember<T>::GetParentObject>
   1921 struct FindAssociatedGlobalForNative {
   1922  static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
   1923    MOZ_ASSERT(js::IsObjectInContextCompartment(obj, cx));
   1924    T* native = UnwrapDOMObject<T>(obj);
   1925    return FindAssociatedGlobal(cx, native->GetParentObject());
   1926  }
   1927 };
   1928 
   1929 template <typename T>
   1930 struct FindAssociatedGlobalForNative<T, false> {
   1931  static JSObject* Get(JSContext* cx, JS::Handle<JSObject*> obj) {
   1932    MOZ_CRASH();
   1933    return nullptr;
   1934  }
   1935 };
   1936 
   1937 // Helper for calling GetOrCreateDOMReflector with smart pointers
   1938 // (UniquePtr/RefPtr/nsCOMPtr) or references.
   1939 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
   1940 struct GetOrCreateDOMReflectorHelper {
   1941  static inline bool GetOrCreate(JSContext* cx, const T& value,
   1942                                 JS::Handle<JSObject*> givenProto,
   1943                                 JS::MutableHandle<JS::Value> rval) {
   1944    return GetOrCreateDOMReflector(cx, value.get(), rval, givenProto);
   1945  }
   1946 };
   1947 
   1948 template <class T>
   1949 struct GetOrCreateDOMReflectorHelper<T, false> {
   1950  static inline bool GetOrCreate(JSContext* cx, T& value,
   1951                                 JS::Handle<JSObject*> givenProto,
   1952                                 JS::MutableHandle<JS::Value> rval) {
   1953    static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
   1954    return GetOrCreateDOMReflector(cx, &value, rval, givenProto);
   1955  }
   1956 };
   1957 
   1958 template <class T>
   1959 inline bool GetOrCreateDOMReflector(
   1960    JSContext* cx, T& value, JS::MutableHandle<JS::Value> rval,
   1961    JS::Handle<JSObject*> givenProto = nullptr) {
   1962  return GetOrCreateDOMReflectorHelper<T>::GetOrCreate(cx, value, givenProto,
   1963                                                       rval);
   1964 }
   1965 
   1966 // Helper for calling GetOrCreateDOMReflectorNoWrap with smart pointers
   1967 // (UniquePtr/RefPtr/nsCOMPtr) or references.
   1968 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
   1969 struct GetOrCreateDOMReflectorNoWrapHelper {
   1970  static inline bool GetOrCreate(JSContext* cx, const T& value,
   1971                                 JS::MutableHandle<JS::Value> rval) {
   1972    return GetOrCreateDOMReflectorNoWrap(cx, value.get(), rval);
   1973  }
   1974 };
   1975 
   1976 template <class T>
   1977 struct GetOrCreateDOMReflectorNoWrapHelper<T, false> {
   1978  static inline bool GetOrCreate(JSContext* cx, T& value,
   1979                                 JS::MutableHandle<JS::Value> rval) {
   1980    return GetOrCreateDOMReflectorNoWrap(cx, &value, rval);
   1981  }
   1982 };
   1983 
   1984 template <class T>
   1985 inline bool GetOrCreateDOMReflectorNoWrap(JSContext* cx, T& value,
   1986                                          JS::MutableHandle<JS::Value> rval) {
   1987  return GetOrCreateDOMReflectorNoWrapHelper<T>::GetOrCreate(cx, value, rval);
   1988 }
   1989 
   1990 template <class T>
   1991 inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T* aObj) {
   1992  return aObj->Callback(aCx);
   1993 }
   1994 
   1995 // Helper for getting the callback JSObject* of a smart ptr around a
   1996 // CallbackObject or a reference to a CallbackObject or something like
   1997 // that.
   1998 template <class T, bool isSmartPtr = IsSmartPtr<T>::value>
   1999 struct GetCallbackFromCallbackObjectHelper {
   2000  static inline JSObject* Get(JSContext* aCx, const T& aObj) {
   2001    return GetCallbackFromCallbackObject(aCx, aObj.get());
   2002  }
   2003 };
   2004 
   2005 template <class T>
   2006 struct GetCallbackFromCallbackObjectHelper<T, false> {
   2007  static inline JSObject* Get(JSContext* aCx, T& aObj) {
   2008    return GetCallbackFromCallbackObject(aCx, &aObj);
   2009  }
   2010 };
   2011 
   2012 template <class T>
   2013 inline JSObject* GetCallbackFromCallbackObject(JSContext* aCx, T& aObj) {
   2014  return GetCallbackFromCallbackObjectHelper<T>::Get(aCx, aObj);
   2015 }
   2016 
   2017 static inline bool AtomizeAndPinJSString(JSContext* cx, jsid& id,
   2018                                         const char* chars) {
   2019  if (JSString* str = ::JS_AtomizeAndPinString(cx, chars)) {
   2020    id = JS::PropertyKey::fromPinnedString(str);
   2021    return true;
   2022  }
   2023  return false;
   2024 }
   2025 
   2026 void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
   2027                      nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
   2028                      JS::MutableHandle<JS::Value> aRetval,
   2029                      ErrorResult& aError);
   2030 
   2031 template <class T>
   2032 void GetInterface(JSContext* aCx, T* aThis, JS::Handle<JS::Value> aIID,
   2033                  JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError) {
   2034  GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
   2035 }
   2036 
   2037 bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
   2038 
   2039 bool ThrowConstructorWithoutNew(JSContext* cx, const char* name);
   2040 
   2041 // Helper for throwing an "invalid this" exception.
   2042 bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
   2043                      bool aSecurityError, prototypes::ID aProtoId);
   2044 
   2045 bool GetPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
   2046                            JS::Handle<JS::Value> receiver, JS::Handle<jsid> id,
   2047                            bool* found, JS::MutableHandle<JS::Value> vp);
   2048 
   2049 //
   2050 bool HasPropertyOnPrototype(JSContext* cx, JS::Handle<JSObject*> proxy,
   2051                            JS::Handle<jsid> id, bool* has);
   2052 
   2053 // Append the property names in "names" to "props". If
   2054 // shadowPrototypeProperties is false then skip properties that are also
   2055 // present on the proto chain of proxy.  If shadowPrototypeProperties is true,
   2056 // then the "proxy" argument is ignored.
   2057 bool AppendNamedPropertyIds(JSContext* cx, JS::Handle<JSObject*> proxy,
   2058                            nsTArray<nsString>& names,
   2059                            bool shadowPrototypeProperties,
   2060                            JS::MutableHandleVector<jsid> props);
   2061 
   2062 enum StringificationBehavior { eStringify, eEmpty, eNull };
   2063 
   2064 static inline JSString* ConvertJSValueToJSString(JSContext* cx,
   2065                                                 JS::Handle<JS::Value> v) {
   2066  if (MOZ_LIKELY(v.isString())) {
   2067    return v.toString();
   2068  }
   2069  return JS::ToString(cx, v);
   2070 }
   2071 
   2072 template <typename T>
   2073 static inline bool ConvertJSValueToString(
   2074    JSContext* cx, JS::Handle<JS::Value> v,
   2075    StringificationBehavior nullBehavior,
   2076    StringificationBehavior undefinedBehavior, T& result) {
   2077  JSString* s;
   2078  if (v.isString()) {
   2079    s = v.toString();
   2080  } else {
   2081    StringificationBehavior behavior;
   2082    if (v.isNull()) {
   2083      behavior = nullBehavior;
   2084    } else if (v.isUndefined()) {
   2085      behavior = undefinedBehavior;
   2086    } else {
   2087      behavior = eStringify;
   2088    }
   2089 
   2090    if (behavior != eStringify) {
   2091      if (behavior == eEmpty) {
   2092        result.Truncate();
   2093      } else {
   2094        result.SetIsVoid(true);
   2095      }
   2096      return true;
   2097    }
   2098 
   2099    s = JS::ToString(cx, v);
   2100    if (!s) {
   2101      return false;
   2102    }
   2103  }
   2104 
   2105  return AssignJSString(cx, result, s);
   2106 }
   2107 
   2108 template <typename T>
   2109 static inline bool ConvertJSValueToString(
   2110    JSContext* cx, JS::Handle<JS::Value> v,
   2111    const char* /* unused sourceDescription */, T& result) {
   2112  return ConvertJSValueToString(cx, v, eStringify, eStringify, result);
   2113 }
   2114 
   2115 [[nodiscard]] bool NormalizeUSVString(nsAString& aString);
   2116 
   2117 [[nodiscard]] bool NormalizeUSVString(
   2118    binding_detail::FakeString<char16_t>& aString);
   2119 
   2120 template <typename T>
   2121 static inline bool ConvertJSValueToUSVString(
   2122    JSContext* cx, JS::Handle<JS::Value> v,
   2123    const char* /* unused sourceDescription */, T& result) {
   2124  if (!ConvertJSValueToString(cx, v, eStringify, eStringify, result)) {
   2125    return false;
   2126  }
   2127 
   2128  if (!NormalizeUSVString(result)) {
   2129    JS_ReportOutOfMemory(cx);
   2130    return false;
   2131  }
   2132 
   2133  return true;
   2134 }
   2135 
   2136 template <typename T>
   2137 inline bool ConvertIdToString(JSContext* cx, JS::Handle<JS::PropertyKey> id,
   2138                              T& result, bool& isSymbol) {
   2139  if (MOZ_LIKELY(id.isString())) {
   2140    if (!AssignJSString(cx, result, id.toString())) {
   2141      return false;
   2142    }
   2143  } else if (id.isSymbol()) {
   2144    isSymbol = true;
   2145    return true;
   2146  } else {
   2147    JS::Rooted<JS::Value> nameVal(cx, js::IdToValue(id));
   2148    if (!ConvertJSValueToString(cx, nameVal, eStringify, eStringify, result)) {
   2149      return false;
   2150    }
   2151  }
   2152  isSymbol = false;
   2153  return true;
   2154 }
   2155 
   2156 bool ConvertJSValueToByteString(BindingCallContext& cx, JS::Handle<JS::Value> v,
   2157                                bool nullable, const char* sourceDescription,
   2158                                nsACString& result);
   2159 
   2160 inline bool ConvertJSValueToByteString(BindingCallContext& cx,
   2161                                       JS::Handle<JS::Value> v,
   2162                                       const char* sourceDescription,
   2163                                       nsACString& result) {
   2164  return ConvertJSValueToByteString(cx, v, false, sourceDescription, result);
   2165 }
   2166 
   2167 template <typename T>
   2168 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq);
   2169 template <typename T>
   2170 void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq);
   2171 
   2172 // Class used to trace sequences, with specializations for various
   2173 // sequence types.
   2174 template <typename T, bool isDictionary = is_dom_dictionary<T>,
   2175          bool isTypedArray = is_dom_typed_array<T>,
   2176          bool isOwningUnion = is_dom_owning_union<T>>
   2177 class SequenceTracer {
   2178  explicit SequenceTracer() = delete;  // Should never be instantiated
   2179 };
   2180 
   2181 // sequence<object> or sequence<object?>
   2182 template <>
   2183 class SequenceTracer<JSObject*, false, false, false> {
   2184  explicit SequenceTracer() = delete;  // Should never be instantiated
   2185 
   2186 public:
   2187  static void TraceSequence(JSTracer* trc, JSObject** objp, JSObject** end) {
   2188    for (; objp != end; ++objp) {
   2189      JS::TraceRoot(trc, objp, "sequence<object>");
   2190    }
   2191  }
   2192 };
   2193 
   2194 // sequence<any>
   2195 template <>
   2196 class SequenceTracer<JS::Value, false, false, false> {
   2197  explicit SequenceTracer() = delete;  // Should never be instantiated
   2198 
   2199 public:
   2200  static void TraceSequence(JSTracer* trc, JS::Value* valp, JS::Value* end) {
   2201    for (; valp != end; ++valp) {
   2202      JS::TraceRoot(trc, valp, "sequence<any>");
   2203    }
   2204  }
   2205 };
   2206 
   2207 // sequence<sequence<T>>
   2208 template <typename T>
   2209 class SequenceTracer<Sequence<T>, false, false, false> {
   2210  explicit SequenceTracer() = delete;  // Should never be instantiated
   2211 
   2212 public:
   2213  static void TraceSequence(JSTracer* trc, Sequence<T>* seqp,
   2214                            Sequence<T>* end) {
   2215    for (; seqp != end; ++seqp) {
   2216      DoTraceSequence(trc, *seqp);
   2217    }
   2218  }
   2219 };
   2220 
   2221 // sequence<sequence<T>> as return value
   2222 template <typename T>
   2223 class SequenceTracer<nsTArray<T>, false, false, false> {
   2224  explicit SequenceTracer() = delete;  // Should never be instantiated
   2225 
   2226 public:
   2227  static void TraceSequence(JSTracer* trc, nsTArray<T>* seqp,
   2228                            nsTArray<T>* end) {
   2229    for (; seqp != end; ++seqp) {
   2230      DoTraceSequence(trc, *seqp);
   2231    }
   2232  }
   2233 };
   2234 
   2235 // sequence<someDictionary>
   2236 template <typename T>
   2237 class SequenceTracer<T, true, false, false> {
   2238  explicit SequenceTracer() = delete;  // Should never be instantiated
   2239 
   2240 public:
   2241  static void TraceSequence(JSTracer* trc, T* dictp, T* end) {
   2242    for (; dictp != end; ++dictp) {
   2243      dictp->TraceDictionary(trc);
   2244    }
   2245  }
   2246 };
   2247 
   2248 // sequence<SomeTypedArray>
   2249 template <typename T>
   2250 class SequenceTracer<T, false, true, false> {
   2251  explicit SequenceTracer() = delete;  // Should never be instantiated
   2252 
   2253 public:
   2254  static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
   2255    for (; arrayp != end; ++arrayp) {
   2256      arrayp->TraceSelf(trc);
   2257    }
   2258  }
   2259 };
   2260 
   2261 // sequence<SomeOwningUnion>
   2262 template <typename T>
   2263 class SequenceTracer<T, false, false, true> {
   2264  explicit SequenceTracer() = delete;  // Should never be instantiated
   2265 
   2266 public:
   2267  static void TraceSequence(JSTracer* trc, T* arrayp, T* end) {
   2268    for (; arrayp != end; ++arrayp) {
   2269      arrayp->TraceUnion(trc);
   2270    }
   2271  }
   2272 };
   2273 
   2274 // sequence<T?> with T? being a Nullable<T>
   2275 template <typename T>
   2276 class SequenceTracer<Nullable<T>, false, false, false> {
   2277  explicit SequenceTracer() = delete;  // Should never be instantiated
   2278 
   2279 public:
   2280  static void TraceSequence(JSTracer* trc, Nullable<T>* seqp,
   2281                            Nullable<T>* end) {
   2282    for (; seqp != end; ++seqp) {
   2283      if (!seqp->IsNull()) {
   2284        // Pretend like we actually have a length-one sequence here so
   2285        // we can do template instantiation correctly for T.
   2286        T& val = seqp->Value();
   2287        T* ptr = &val;
   2288        SequenceTracer<T>::TraceSequence(trc, ptr, ptr + 1);
   2289      }
   2290    }
   2291  }
   2292 };
   2293 
   2294 template <typename K, typename V>
   2295 void TraceRecord(JSTracer* trc, Record<K, V>& record) {
   2296  for (auto& entry : record.Entries()) {
   2297    // Act like it's a one-element sequence to leverage all that infrastructure.
   2298    SequenceTracer<V>::TraceSequence(trc, &entry.mValue, &entry.mValue + 1);
   2299  }
   2300 }
   2301 
   2302 // sequence<record>
   2303 template <typename K, typename V>
   2304 class SequenceTracer<Record<K, V>, false, false, false> {
   2305  explicit SequenceTracer() = delete;  // Should never be instantiated
   2306 
   2307 public:
   2308  static void TraceSequence(JSTracer* trc, Record<K, V>* seqp,
   2309                            Record<K, V>* end) {
   2310    for (; seqp != end; ++seqp) {
   2311      TraceRecord(trc, *seqp);
   2312    }
   2313  }
   2314 };
   2315 
   2316 template <typename T>
   2317 void DoTraceSequence(JSTracer* trc, FallibleTArray<T>& seq) {
   2318  SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
   2319                                   seq.Elements() + seq.Length());
   2320 }
   2321 
   2322 template <typename T>
   2323 void DoTraceSequence(JSTracer* trc, nsTArray<T>& seq) {
   2324  SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
   2325                                   seq.Elements() + seq.Length());
   2326 }
   2327 
   2328 // Rooter class for sequences; this is what we mostly use in the codegen
   2329 template <typename T>
   2330 class MOZ_RAII SequenceRooter final : private JS::CustomAutoRooter {
   2331 public:
   2332  template <typename CX>
   2333  SequenceRooter(const CX& cx, FallibleTArray<T>* aSequence)
   2334      : JS::CustomAutoRooter(cx),
   2335        mFallibleArray(aSequence),
   2336        mSequenceType(eFallibleArray) {}
   2337 
   2338  template <typename CX>
   2339  SequenceRooter(const CX& cx, nsTArray<T>* aSequence)
   2340      : JS::CustomAutoRooter(cx),
   2341        mInfallibleArray(aSequence),
   2342        mSequenceType(eInfallibleArray) {}
   2343 
   2344  template <typename CX>
   2345  SequenceRooter(const CX& cx, Nullable<nsTArray<T>>* aSequence)
   2346      : JS::CustomAutoRooter(cx),
   2347        mNullableArray(aSequence),
   2348        mSequenceType(eNullableArray) {}
   2349 
   2350 private:
   2351  enum SequenceType { eInfallibleArray, eFallibleArray, eNullableArray };
   2352 
   2353  virtual void trace(JSTracer* trc) override {
   2354    if (mSequenceType == eFallibleArray) {
   2355      DoTraceSequence(trc, *mFallibleArray);
   2356    } else if (mSequenceType == eInfallibleArray) {
   2357      DoTraceSequence(trc, *mInfallibleArray);
   2358    } else {
   2359      MOZ_ASSERT(mSequenceType == eNullableArray);
   2360      if (!mNullableArray->IsNull()) {
   2361        DoTraceSequence(trc, mNullableArray->Value());
   2362      }
   2363    }
   2364  }
   2365 
   2366  union {
   2367    nsTArray<T>* mInfallibleArray;
   2368    FallibleTArray<T>* mFallibleArray;
   2369    Nullable<nsTArray<T>>* mNullableArray;
   2370  };
   2371 
   2372  SequenceType mSequenceType;
   2373 };
   2374 
   2375 // Rooter class for Record; this is what we mostly use in the codegen.
   2376 template <typename K, typename V>
   2377 class MOZ_RAII RecordRooter final : private JS::CustomAutoRooter {
   2378 public:
   2379  template <typename CX>
   2380  RecordRooter(const CX& cx, Record<K, V>* aRecord)
   2381      : JS::CustomAutoRooter(cx), mRecord(aRecord), mRecordType(eRecord) {}
   2382 
   2383  template <typename CX>
   2384  RecordRooter(const CX& cx, Nullable<Record<K, V>>* aRecord)
   2385      : JS::CustomAutoRooter(cx),
   2386        mNullableRecord(aRecord),
   2387        mRecordType(eNullableRecord) {}
   2388 
   2389 private:
   2390  enum RecordType { eRecord, eNullableRecord };
   2391 
   2392  virtual void trace(JSTracer* trc) override {
   2393    if (mRecordType == eRecord) {
   2394      TraceRecord(trc, *mRecord);
   2395    } else {
   2396      MOZ_ASSERT(mRecordType == eNullableRecord);
   2397      if (!mNullableRecord->IsNull()) {
   2398        TraceRecord(trc, mNullableRecord->Value());
   2399      }
   2400    }
   2401  }
   2402 
   2403  union {
   2404    Record<K, V>* mRecord;
   2405    Nullable<Record<K, V>>* mNullableRecord;
   2406  };
   2407 
   2408  RecordType mRecordType;
   2409 };
   2410 
   2411 template <typename T>
   2412 class MOZ_RAII RootedUnion : public T, private JS::CustomAutoRooter {
   2413 public:
   2414  template <typename CX>
   2415  explicit RootedUnion(const CX& cx) : T(), JS::CustomAutoRooter(cx) {}
   2416 
   2417  virtual void trace(JSTracer* trc) override { this->TraceUnion(trc); }
   2418 };
   2419 
   2420 template <typename T>
   2421 class MOZ_STACK_CLASS NullableRootedUnion : public Nullable<T>,
   2422                                            private JS::CustomAutoRooter {
   2423 public:
   2424  template <typename CX>
   2425  explicit NullableRootedUnion(const CX& cx)
   2426      : Nullable<T>(), JS::CustomAutoRooter(cx) {}
   2427 
   2428  virtual void trace(JSTracer* trc) override {
   2429    if (!this->IsNull()) {
   2430      this->Value().TraceUnion(trc);
   2431    }
   2432  }
   2433 };
   2434 
   2435 inline bool AddStringToIDVector(JSContext* cx,
   2436                                JS::MutableHandleVector<jsid> vector,
   2437                                const char* name) {
   2438  return vector.growBy(1) &&
   2439         AtomizeAndPinJSString(cx, *(vector[vector.length() - 1]).address(),
   2440                               name);
   2441 }
   2442 
   2443 // We use one JSNative to represent all DOM interface objects (so we can easily
   2444 // detect when we need to wrap them in an Xray wrapper). A pointer to the
   2445 // relevant DOMInterfaceInfo is stored in the
   2446 // INTERFACE_OBJECT_INFO_RESERVED_SLOT slot of the JSFunction object for a
   2447 // specific interface object. We store the real JSNative and the
   2448 // NativeProperties in a JSNativeHolder, held by the DOMInterfaceInfo.
   2449 bool InterfaceObjectJSNative(JSContext* cx, unsigned argc, JS::Value* vp);
   2450 
   2451 inline bool IsInterfaceObject(JSObject* obj) {
   2452  return JS_IsNativeFunction(obj, InterfaceObjectJSNative);
   2453 }
   2454 
   2455 inline const DOMInterfaceInfo* InterfaceInfoFromObject(JSObject* obj) {
   2456  MOZ_ASSERT(IsInterfaceObject(obj));
   2457  const JS::Value& v =
   2458      js::GetFunctionNativeReserved(obj, INTERFACE_OBJECT_INFO_RESERVED_SLOT);
   2459  return static_cast<const DOMInterfaceInfo*>(v.toPrivate());
   2460 }
   2461 
   2462 inline const JSNativeHolder* NativeHolderFromInterfaceObject(JSObject* obj) {
   2463  MOZ_ASSERT(IsInterfaceObject(obj));
   2464  return &InterfaceInfoFromObject(obj)->nativeHolder;
   2465 }
   2466 
   2467 // We use one JSNative to represent all legacy factory functions (so we can
   2468 // easily detect when we need to wrap them in an Xray wrapper). We store the
   2469 // real JSNative and the NativeProperties in a JSNativeHolder in a
   2470 // LegacyFactoryFunction in the LEGACY_FACTORY_FUNCTION_RESERVED_SLOT slot of
   2471 // the JSFunction object.
   2472 bool LegacyFactoryFunctionJSNative(JSContext* cx, unsigned argc, JS::Value* vp);
   2473 
   2474 inline bool IsLegacyFactoryFunction(JSObject* obj) {
   2475  return JS_IsNativeFunction(obj, LegacyFactoryFunctionJSNative);
   2476 }
   2477 
   2478 inline const LegacyFactoryFunction* LegacyFactoryFunctionFromObject(
   2479    JSObject* obj) {
   2480  MOZ_ASSERT(IsLegacyFactoryFunction(obj));
   2481  const JS::Value& v =
   2482      js::GetFunctionNativeReserved(obj, LEGACY_FACTORY_FUNCTION_RESERVED_SLOT);
   2483  return static_cast<const LegacyFactoryFunction*>(v.toPrivate());
   2484 }
   2485 
   2486 inline const JSNativeHolder* NativeHolderFromLegacyFactoryFunction(
   2487    JSObject* obj) {
   2488  return &LegacyFactoryFunctionFromObject(obj)->mHolder;
   2489 }
   2490 
   2491 inline const JSNativeHolder* NativeHolderFromObject(JSObject* obj) {
   2492  return IsInterfaceObject(obj) ? NativeHolderFromInterfaceObject(obj)
   2493                                : NativeHolderFromLegacyFactoryFunction(obj);
   2494 }
   2495 
   2496 // Implementation of the bits that XrayWrapper needs
   2497 
   2498 /**
   2499 * This resolves operations, attributes and constants of the interfaces for obj.
   2500 *
   2501 * wrapper is the Xray JS object.
   2502 * obj is the target object of the Xray, a binding's instance object or a
   2503 *     interface or interface prototype object.
   2504 */
   2505 bool XrayResolveOwnProperty(
   2506    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
   2507    JS::Handle<jsid> id,
   2508    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc,
   2509    bool& cacheOnHolder);
   2510 
   2511 /**
   2512 * Define a property on obj through an Xray wrapper.
   2513 *
   2514 * wrapper is the Xray JS object.
   2515 * obj is the target object of the Xray, a binding's instance object or a
   2516 *     interface or interface prototype object.
   2517 * id and desc are the parameters for the property to be defined.
   2518 * result is the out-parameter indicating success (read it only if
   2519 *     this returns true and also sets *done to true).
   2520 * done will be set to true if a property was set as a result of this call
   2521 *      or if we want to always avoid setting this property
   2522 *      (i.e. indexed properties on DOM objects)
   2523 */
   2524 bool XrayDefineProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
   2525                        JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   2526                        JS::Handle<JS::PropertyDescriptor> desc,
   2527                        JS::ObjectOpResult& result, bool* done);
   2528 
   2529 /**
   2530 * Add to props the property keys of all indexed or named properties of obj and
   2531 * operations, attributes and constants of the interfaces for obj.
   2532 *
   2533 * wrapper is the Xray JS object.
   2534 * obj is the target object of the Xray, a binding's instance object or a
   2535 *     interface or interface prototype object.
   2536 * flags are JSITER_* flags.
   2537 */
   2538 bool XrayOwnPropertyKeys(JSContext* cx, JS::Handle<JSObject*> wrapper,
   2539                         JS::Handle<JSObject*> obj, unsigned flags,
   2540                         JS::MutableHandleVector<jsid> props);
   2541 
   2542 /**
   2543 * Returns the prototype to use for an Xray for a DOM object, wrapped in cx's
   2544 * compartment. This always returns the prototype that would be used for a DOM
   2545 * object if we ignore any changes that might have been done to the prototype
   2546 * chain by JS, the XBL code or plugins.
   2547 *
   2548 * cx should be in the Xray's compartment.
   2549 * obj is the target object of the Xray, a binding's instance object or an
   2550 *     interface or interface prototype object.
   2551 */
   2552 inline bool XrayGetNativeProto(JSContext* cx, JS::Handle<JSObject*> obj,
   2553                               JS::MutableHandle<JSObject*> protop) {
   2554  JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj));
   2555  {
   2556    JSAutoRealm ar(cx, global);
   2557    const DOMJSClass* domClass = GetDOMClass(obj);
   2558    if (domClass) {
   2559      ProtoHandleGetter protoGetter = domClass->mGetProto;
   2560      if (protoGetter) {
   2561        protop.set(protoGetter(cx));
   2562      } else {
   2563        protop.set(JS::GetRealmObjectPrototype(cx));
   2564      }
   2565    } else if (JS_ObjectIsFunction(obj)) {
   2566      if (IsLegacyFactoryFunction(obj)) {
   2567        protop.set(JS::GetRealmFunctionPrototype(cx));
   2568      } else {
   2569        protop.set(InterfaceInfoFromObject(obj)->mGetParentProto(cx));
   2570      }
   2571    } else {
   2572      const JSClass* clasp = JS::GetClass(obj);
   2573      MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
   2574      ProtoGetter protoGetter =
   2575          DOMIfaceAndProtoJSClass::FromJSClass(clasp)->mGetParentProto;
   2576      protop.set(protoGetter(cx));
   2577    }
   2578  }
   2579 
   2580  return JS_WrapObject(cx, protop);
   2581 }
   2582 
   2583 /**
   2584 * Get the Xray expando class to use for the given DOM object.
   2585 */
   2586 const JSClass* XrayGetExpandoClass(JSContext* cx, JS::Handle<JSObject*> obj);
   2587 
   2588 /**
   2589 * Delete a named property, if any.  Return value is false if exception thrown,
   2590 * true otherwise.  The caller should not do any more work after calling this
   2591 * function, because it has no way whether a deletion was performed and hence
   2592 * opresult already has state set on it.  If callers ever need to change that,
   2593 * add a "bool* found" argument and change the generated DeleteNamedProperty to
   2594 * use it instead of a local variable.
   2595 */
   2596 bool XrayDeleteNamedProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
   2597                             JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
   2598                             JS::ObjectOpResult& opresult);
   2599 
   2600 namespace binding_detail {
   2601 
   2602 // Default implementations of the NativePropertyHooks' mResolveOwnProperty and
   2603 // mEnumerateOwnProperties for WebIDL bindings implemented as proxies.
   2604 bool ResolveOwnProperty(
   2605    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
   2606    JS::Handle<jsid> id,
   2607    JS::MutableHandle<mozilla::Maybe<JS::PropertyDescriptor>> desc);
   2608 bool EnumerateOwnProperties(JSContext* cx, JS::Handle<JSObject*> wrapper,
   2609                            JS::Handle<JSObject*> obj,
   2610                            JS::MutableHandleVector<jsid> props);
   2611 
   2612 }  // namespace binding_detail
   2613 
   2614 /**
   2615 * Get the object which should be used to cache the return value of a property
   2616 * getter in the case of a [Cached] or [StoreInSlot] property.  `obj` is the
   2617 * `this` value for our property getter that we're working with.
   2618 *
   2619 * This function can return null on failure to allocate the object, throwing on
   2620 * the JSContext in the process.
   2621 *
   2622 * The isXray outparam will be set to true if obj is an Xray and false
   2623 * otherwise.
   2624 *
   2625 * Note that the Slow version should only be called from
   2626 * GetCachedSlotStorageObject.
   2627 */
   2628 JSObject* GetCachedSlotStorageObjectSlow(JSContext* cx,
   2629                                         JS::Handle<JSObject*> obj,
   2630                                         bool* isXray);
   2631 
   2632 inline JSObject* GetCachedSlotStorageObject(JSContext* cx,
   2633                                            JS::Handle<JSObject*> obj,
   2634                                            bool* isXray) {
   2635  if (IsDOMObject(obj)) {
   2636    *isXray = false;
   2637    return obj;
   2638  }
   2639 
   2640  return GetCachedSlotStorageObjectSlow(cx, obj, isXray);
   2641 }
   2642 
   2643 extern NativePropertyHooks sEmptyNativePropertyHooks;
   2644 
   2645 inline bool IsDOMConstructor(JSObject* obj) {
   2646  return IsInterfaceObject(obj) || IsLegacyFactoryFunction(obj);
   2647 }
   2648 
   2649 inline bool UseDOMXray(JSObject* obj) {
   2650  const JSClass* clasp = JS::GetClass(obj);
   2651  return IsDOMClass(clasp) || IsDOMConstructor(obj) ||
   2652         IsDOMIfaceAndProtoClass(clasp);
   2653 }
   2654 
   2655 // Helpers for creating a const version of a type.
   2656 template <typename T>
   2657 const T& Constify(T& arg) {
   2658  return arg;
   2659 }
   2660 
   2661 // Helper for turning (Owning)NonNull<T> into T&
   2662 template <typename T>
   2663 T& NonNullHelper(T& aArg) {
   2664  return aArg;
   2665 }
   2666 
   2667 template <typename T>
   2668 T& NonNullHelper(NonNull<T>& aArg) {
   2669  return aArg;
   2670 }
   2671 
   2672 template <typename T>
   2673 const T& NonNullHelper(const NonNull<T>& aArg) {
   2674  return aArg;
   2675 }
   2676 
   2677 template <typename T>
   2678 T& NonNullHelper(OwningNonNull<T>& aArg) {
   2679  return aArg;
   2680 }
   2681 
   2682 template <typename T>
   2683 const T& NonNullHelper(const OwningNonNull<T>& aArg) {
   2684  return aArg;
   2685 }
   2686 
   2687 template <typename CharT>
   2688 inline void NonNullHelper(NonNull<binding_detail::FakeString<CharT>>& aArg) {
   2689  // This overload is here to make sure that we never end up applying
   2690  // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
   2691  // try to, it should fail to compile, since presumably the caller will try to
   2692  // use our nonexistent return value.
   2693 }
   2694 
   2695 template <typename CharT>
   2696 inline void NonNullHelper(
   2697    const NonNull<binding_detail::FakeString<CharT>>& aArg) {
   2698  // This overload is here to make sure that we never end up applying
   2699  // NonNullHelper to a NonNull<binding_detail::FakeString>. If we
   2700  // try to, it should fail to compile, since presumably the caller will try to
   2701  // use our nonexistent return value.
   2702 }
   2703 
   2704 template <typename CharT>
   2705 inline void NonNullHelper(binding_detail::FakeString<CharT>& aArg) {
   2706  // This overload is here to make sure that we never end up applying
   2707  // NonNullHelper to a FakeString before we've constified it.  If we
   2708  // try to, it should fail to compile, since presumably the caller will try to
   2709  // use our nonexistent return value.
   2710 }
   2711 
   2712 template <typename CharT>
   2713 MOZ_ALWAYS_INLINE const nsTSubstring<CharT>& NonNullHelper(
   2714    const binding_detail::FakeString<CharT>& aArg) {
   2715  return aArg;
   2716 }
   2717 
   2718 // Given a DOM reflector aObj, give its underlying DOM object a reflector in
   2719 // whatever global that underlying DOM object now thinks it should be in.  If
   2720 // this is in a different compartment from aObj, aObj will become a
   2721 // cross-compatment wrapper for the new object.  Otherwise, aObj will become the
   2722 // new object (via a brain transplant).  If the new global is the same as the
   2723 // old global, we just keep using the same object.
   2724 //
   2725 // On entry to this method, aCx and aObj must be same-compartment.
   2726 void UpdateReflectorGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
   2727                           ErrorResult& aError);
   2728 
   2729 // Helper for lenient getters/setters to report to console.  If this
   2730 // returns false, we couldn't even get a global.
   2731 bool ReportLenientThisUnwrappingFailure(JSContext* cx, JSObject* obj);
   2732 
   2733 // Given a JSObject* that represents the chrome side of a JS-implemented WebIDL
   2734 // interface, get the nsIGlobalObject corresponding to the content side, if any.
   2735 // A false return means an exception was thrown.
   2736 bool GetContentGlobalForJSImplementedObject(BindingCallContext& cx,
   2737                                            JS::Handle<JSObject*> obj,
   2738                                            nsIGlobalObject** global);
   2739 
   2740 void ConstructJSImplementation(const char* aContractId,
   2741                               nsIGlobalObject* aGlobal,
   2742                               JS::MutableHandle<JSObject*> aObject,
   2743                               ErrorResult& aRv);
   2744 
   2745 // XXX Avoid pulling in the whole ScriptSettings.h, however there should be a
   2746 // unique declaration of this function somewhere else.
   2747 JS::RootingContext* RootingCx();
   2748 
   2749 template <typename T>
   2750 already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
   2751                                              nsIGlobalObject* aGlobal,
   2752                                              ErrorResult& aRv) {
   2753  JS::RootingContext* cx = RootingCx();
   2754  JS::Rooted<JSObject*> jsImplObj(cx);
   2755  ConstructJSImplementation(aContractId, aGlobal, &jsImplObj, aRv);
   2756  if (aRv.Failed()) {
   2757    return nullptr;
   2758  }
   2759 
   2760  MOZ_RELEASE_ASSERT(!js::IsWrapper(jsImplObj));
   2761  JS::Rooted<JSObject*> jsImplGlobal(cx, JS::GetNonCCWObjectGlobal(jsImplObj));
   2762  RefPtr<T> newObj = new T(jsImplObj, jsImplGlobal, aGlobal);
   2763  return newObj.forget();
   2764 }
   2765 
   2766 template <typename T>
   2767 already_AddRefed<T> ConstructJSImplementation(const char* aContractId,
   2768                                              const GlobalObject& aGlobal,
   2769                                              ErrorResult& aRv) {
   2770  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   2771  if (!global) {
   2772    aRv.Throw(NS_ERROR_FAILURE);
   2773    return nullptr;
   2774  }
   2775 
   2776  return ConstructJSImplementation<T>(aContractId, global, aRv);
   2777 }
   2778 
   2779 /**
   2780 * Convert an nsCString to jsval, returning true on success.
   2781 * These functions are intended for ByteString implementations.
   2782 * As such, the string is not UTF-8 encoded.  Any UTF8 strings passed to these
   2783 * methods will be mangled.
   2784 */
   2785 inline bool NonVoidByteStringToJsval(JSContext* cx, const nsACString& str,
   2786                                     JS::MutableHandle<JS::Value> rval) {
   2787  return xpc::NonVoidLatin1StringToJsval(cx, str, rval);
   2788 }
   2789 inline bool ByteStringToJsval(JSContext* cx, const nsACString& str,
   2790                              JS::MutableHandle<JS::Value> rval) {
   2791  if (str.IsVoid()) {
   2792    rval.setNull();
   2793    return true;
   2794  }
   2795  return NonVoidByteStringToJsval(cx, str, rval);
   2796 }
   2797 
   2798 // Convert an utf-8 encoded nsCString to jsval, returning true on success.
   2799 //
   2800 // TODO(bug 1606957): This could probably be better.
   2801 inline bool NonVoidUTF8StringToJsval(JSContext* cx, const nsACString& str,
   2802                                     JS::MutableHandle<JS::Value> rval) {
   2803  return xpc::NonVoidUTF8StringToJsval(cx, str, rval);
   2804 }
   2805 
   2806 inline bool UTF8StringToJsval(JSContext* cx, const nsACString& str,
   2807                              JS::MutableHandle<JS::Value> rval) {
   2808  if (str.IsVoid()) {
   2809    rval.setNull();
   2810    return true;
   2811  }
   2812  return NonVoidUTF8StringToJsval(cx, str, rval);
   2813 }
   2814 
   2815 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   2816 struct PreserveWrapperHelper {
   2817  static void PreserveWrapper(T* aObject) {
   2818    aObject->PreserveWrapper(aObject, NS_CYCLE_COLLECTION_PARTICIPANT(T));
   2819  }
   2820 };
   2821 
   2822 template <class T>
   2823 struct PreserveWrapperHelper<T, true> {
   2824  static void PreserveWrapper(T* aObject) {
   2825    aObject->PreserveWrapper(reinterpret_cast<nsISupports*>(aObject));
   2826  }
   2827 };
   2828 
   2829 template <class T>
   2830 void PreserveWrapper(T* aObject) {
   2831  PreserveWrapperHelper<T>::PreserveWrapper(aObject);
   2832 }
   2833 
   2834 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   2835 struct CastingAssertions {
   2836  static bool ToSupportsIsCorrect(T*) { return true; }
   2837  static bool ToSupportsIsOnPrimaryInheritanceChain(T*, nsWrapperCache*) {
   2838    return true;
   2839  }
   2840 };
   2841 
   2842 template <class T>
   2843 struct CastingAssertions<T, true> {
   2844  static bool ToSupportsIsCorrect(T* aObject) {
   2845    return ToSupports(aObject) == reinterpret_cast<nsISupports*>(aObject);
   2846  }
   2847  static bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject,
   2848                                                    nsWrapperCache* aCache) {
   2849    return reinterpret_cast<void*>(aObject) != aCache;
   2850  }
   2851 };
   2852 
   2853 template <class T>
   2854 bool ToSupportsIsCorrect(T* aObject) {
   2855  return CastingAssertions<T>::ToSupportsIsCorrect(aObject);
   2856 }
   2857 
   2858 template <class T>
   2859 bool ToSupportsIsOnPrimaryInheritanceChain(T* aObject, nsWrapperCache* aCache) {
   2860  return CastingAssertions<T>::ToSupportsIsOnPrimaryInheritanceChain(aObject,
   2861                                                                     aCache);
   2862 }
   2863 
   2864 // Get the size of allocated memory to associate with a binding JSObject for a
   2865 // native object. This is supplied to the JS engine to allow it to schedule GC
   2866 // when necessary.
   2867 //
   2868 // This function supplies a default value and is overloaded for specific native
   2869 // object types.
   2870 inline size_t BindingJSObjectMallocBytes(void* aNativePtr) { return 0; }
   2871 
   2872 // The BindingJSObjectCreator class is supposed to be used by a caller that
   2873 // wants to create and initialise a binding JSObject. After initialisation has
   2874 // been successfully completed it should call InitializationSucceeded().
   2875 // The BindingJSObjectCreator object will root the JSObject until
   2876 // InitializationSucceeded() is called on it. If the native object for the
   2877 // binding is refcounted it will also hold a strong reference to it, that
   2878 // reference is transferred to the JSObject (which holds the native in a slot)
   2879 // when InitializationSucceeded() is called. If the BindingJSObjectCreator
   2880 // object is destroyed and InitializationSucceeded() was never called on it then
   2881 // the JSObject's slot holding the native will be set to undefined, and for a
   2882 // refcounted native the strong reference will be released.
   2883 template <class T>
   2884 class MOZ_STACK_CLASS BindingJSObjectCreator {
   2885 public:
   2886  explicit BindingJSObjectCreator(JSContext* aCx) : mReflector(aCx) {}
   2887 
   2888  ~BindingJSObjectCreator() {
   2889    if (mReflector) {
   2890      JS::SetReservedSlot(mReflector, DOM_OBJECT_SLOT, JS::UndefinedValue());
   2891    }
   2892  }
   2893 
   2894  void CreateProxyObject(JSContext* aCx, const JSClass* aClass,
   2895                         const DOMProxyHandler* aHandler,
   2896                         JS::Handle<JSObject*> aProto, bool aLazyProto,
   2897                         T* aNative, JS::Handle<JS::Value> aExpandoValue,
   2898                         JS::MutableHandle<JSObject*> aReflector) {
   2899    js::ProxyOptions options;
   2900    options.setClass(aClass);
   2901    options.setLazyProto(aLazyProto);
   2902 
   2903    aReflector.set(
   2904        js::NewProxyObject(aCx, aHandler, aExpandoValue, aProto, options));
   2905    if (aReflector) {
   2906      js::SetProxyReservedSlot(aReflector, DOM_OBJECT_SLOT,
   2907                               JS::PrivateValue(aNative));
   2908      mNative = aNative;
   2909      mReflector = aReflector;
   2910 
   2911      if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
   2912        JS::AddAssociatedMemory(aReflector, mallocBytes,
   2913                                JS::MemoryUse::DOMBinding);
   2914      }
   2915    }
   2916  }
   2917 
   2918  void CreateObject(JSContext* aCx, const JSClass* aClass,
   2919                    JS::Handle<JSObject*> aProto, T* aNative,
   2920                    JS::MutableHandle<JSObject*> aReflector) {
   2921    aReflector.set(
   2922        JS_NewObjectWithGivenProtoAndUseAllocSite(aCx, aClass, aProto));
   2923    if (aReflector) {
   2924      JS::SetReservedSlot(aReflector, DOM_OBJECT_SLOT,
   2925                          JS::PrivateValue(aNative));
   2926      mNative = aNative;
   2927      mReflector = aReflector;
   2928 
   2929      if (size_t mallocBytes = BindingJSObjectMallocBytes(aNative)) {
   2930        JS::AddAssociatedMemory(aReflector, mallocBytes,
   2931                                JS::MemoryUse::DOMBinding);
   2932      }
   2933    }
   2934  }
   2935 
   2936  void InitializationSucceeded() {
   2937    T* pointer;
   2938    mNative.forget(&pointer);
   2939    mReflector = nullptr;
   2940  }
   2941 
   2942 private:
   2943  struct OwnedNative {
   2944    // Make sure the native objects inherit from NonRefcountedDOMObject so
   2945    // that we log their ctor and dtor.
   2946    static_assert(std::is_base_of<NonRefcountedDOMObject, T>::value,
   2947                  "Non-refcounted objects with DOM bindings should inherit "
   2948                  "from NonRefcountedDOMObject.");
   2949 
   2950    OwnedNative& operator=(T* aNative) {
   2951      mNative = aNative;
   2952      return *this;
   2953    }
   2954 
   2955    // This signature sucks, but it's the only one that will make a nsRefPtr
   2956    // just forget about its pointer without warning.
   2957    void forget(T** aResult) {
   2958      *aResult = mNative;
   2959      mNative = nullptr;
   2960    }
   2961 
   2962    // Keep track of the pointer for use in InitializationSucceeded().
   2963    // The caller (or, after initialization succeeds, the JS object) retains
   2964    // ownership of the object.
   2965    T* mNative;
   2966  };
   2967 
   2968  JS::Rooted<JSObject*> mReflector;
   2969  std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, OwnedNative> mNative;
   2970 };
   2971 
   2972 template <class T>
   2973 struct DeferredFinalizerImpl {
   2974  using SmartPtr = std::conditional_t<
   2975      std::is_same_v<T, nsISupports>, nsCOMPtr<T>,
   2976      std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, UniquePtr<T>>>;
   2977  typedef SegmentedVector<SmartPtr> SmartPtrArray;
   2978 
   2979  static_assert(
   2980      std::is_same_v<T, nsISupports> || !std::is_base_of<nsISupports, T>::value,
   2981      "nsISupports classes should all use the nsISupports instantiation");
   2982 
   2983  static inline void AppendAndTake(
   2984      SegmentedVector<nsCOMPtr<nsISupports>>& smartPtrArray, nsISupports* ptr) {
   2985    smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
   2986  }
   2987  template <class U>
   2988  static inline void AppendAndTake(SegmentedVector<RefPtr<U>>& smartPtrArray,
   2989                                   U* ptr) {
   2990    smartPtrArray.InfallibleAppend(dont_AddRef(ptr));
   2991  }
   2992  template <class U>
   2993  static inline void AppendAndTake(SegmentedVector<UniquePtr<U>>& smartPtrArray,
   2994                                   U* ptr) {
   2995    smartPtrArray.InfallibleAppend(ptr);
   2996  }
   2997 
   2998  static void* AppendDeferredFinalizePointer(void* aData, void* aObject) {
   2999    SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
   3000    if (!pointers) {
   3001      pointers = new SmartPtrArray();
   3002    }
   3003    AppendAndTake(*pointers, static_cast<T*>(aObject));
   3004    return pointers;
   3005  }
   3006  static bool DeferredFinalize(uint32_t aSlice, void* aData) {
   3007    MOZ_ASSERT(aSlice > 0, "nonsensical/useless call with aSlice == 0");
   3008    SmartPtrArray* pointers = static_cast<SmartPtrArray*>(aData);
   3009    pointers->PopLastN(aSlice);
   3010    if (pointers->IsEmpty()) {
   3011      delete pointers;
   3012      return true;
   3013    }
   3014    return false;
   3015  }
   3016 };
   3017 
   3018 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   3019 struct DeferredFinalizer {
   3020  static void AddForDeferredFinalization(T* aObject) {
   3021    typedef DeferredFinalizerImpl<T> Impl;
   3022    DeferredFinalize(Impl::AppendDeferredFinalizePointer,
   3023                     Impl::DeferredFinalize, aObject);
   3024  }
   3025 };
   3026 
   3027 template <class T>
   3028 struct DeferredFinalizer<T, true> {
   3029  static void AddForDeferredFinalization(T* aObject) {
   3030    DeferredFinalize(reinterpret_cast<nsISupports*>(aObject));
   3031  }
   3032 };
   3033 
   3034 template <class T>
   3035 static void AddForDeferredFinalization(T* aObject) {
   3036  DeferredFinalizer<T>::AddForDeferredFinalization(aObject);
   3037 }
   3038 
   3039 // This returns T's CC participant if it participates in CC and does not inherit
   3040 // from nsISupports. Otherwise, it returns null. QI should be used to get the
   3041 // participant if T inherits from nsISupports.
   3042 template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
   3043 class GetCCParticipant {
   3044  // Helper for GetCCParticipant for classes that participate in CC.
   3045  template <class U>
   3046  static constexpr nsCycleCollectionParticipant* GetHelper(
   3047      int, typename U::NS_CYCLE_COLLECTION_INNERCLASS* dummy = nullptr) {
   3048    return T::NS_CYCLE_COLLECTION_INNERCLASS::GetParticipant();
   3049  }
   3050  // Helper for GetCCParticipant for classes that don't participate in CC.
   3051  template <class U>
   3052  static constexpr nsCycleCollectionParticipant* GetHelper(double) {
   3053    return nullptr;
   3054  }
   3055 
   3056 public:
   3057  static constexpr nsCycleCollectionParticipant* Get() {
   3058    // Passing int() here will try to call the GetHelper that takes an int as
   3059    // its first argument. If T doesn't participate in CC then substitution for
   3060    // the second argument (with a default value) will fail and because of
   3061    // SFINAE the next best match (the variant taking a double) will be called.
   3062    return GetHelper<T>(int());
   3063  }
   3064 };
   3065 
   3066 template <class T>
   3067 class GetCCParticipant<T, true> {
   3068 public:
   3069  static constexpr nsCycleCollectionParticipant* Get() { return nullptr; }
   3070 };
   3071 
   3072 void FinalizeGlobal(JS::GCContext* aGcx, JSObject* aObj);
   3073 
   3074 bool ResolveGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
   3075                   JS::Handle<jsid> aId, bool* aResolvedp);
   3076 
   3077 bool MayResolveGlobal(const JSAtomState& aNames, jsid aId, JSObject* aMaybeObj);
   3078 
   3079 bool EnumerateGlobal(JSContext* aCx, JS::Handle<JSObject*> aObj,
   3080                     JS::MutableHandleVector<jsid> aProperties,
   3081                     bool aEnumerableOnly);
   3082 
   3083 struct CreateGlobalOptionsGeneric {
   3084  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj) {
   3085    mozilla::dom::TraceProtoAndIfaceCache(aTrc, aObj);
   3086  }
   3087  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal) {
   3088    MOZ_ALWAYS_TRUE(TryPreserveWrapper(aGlobal));
   3089 
   3090    return true;
   3091  }
   3092 };
   3093 
   3094 struct CreateGlobalOptionsWithXPConnect {
   3095  static void TraceGlobal(JSTracer* aTrc, JSObject* aObj);
   3096  static bool PostCreateGlobal(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
   3097 };
   3098 
   3099 template <class T>
   3100 using IsGlobalWithXPConnect =
   3101    std::disjunction<std::is_base_of<nsGlobalWindowInner, T>,
   3102                     std::is_base_of<MessageManagerGlobal, T>>;
   3103 
   3104 template <class T>
   3105 struct CreateGlobalOptions
   3106    : std::conditional_t<IsGlobalWithXPConnect<T>::value,
   3107                         CreateGlobalOptionsWithXPConnect,
   3108                         CreateGlobalOptionsGeneric> {
   3109  static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
   3110      ProtoAndIfaceCache::NonWindowLike;
   3111 };
   3112 
   3113 template <>
   3114 struct CreateGlobalOptions<nsGlobalWindowInner>
   3115    : public CreateGlobalOptionsWithXPConnect {
   3116  static constexpr ProtoAndIfaceCache::Kind ProtoAndIfaceCacheKind =
   3117      ProtoAndIfaceCache::WindowLike;
   3118 };
   3119 
   3120 uint64_t GetWindowID(void* aGlobal);
   3121 uint64_t GetWindowID(nsGlobalWindowInner* aGlobal);
   3122 uint64_t GetWindowID(DedicatedWorkerGlobalScope* aGlobal);
   3123 
   3124 // The return value is true if we created and successfully performed our part of
   3125 // the setup for the global, false otherwise.
   3126 //
   3127 // Typically this method's caller will want to ensure that
   3128 // xpc::InitGlobalObjectOptions is called before, and xpc::InitGlobalObject is
   3129 // called after, this method, to ensure that this global object and its
   3130 // compartment are consistent with other global objects.
   3131 template <class T, ProtoHandleGetter GetProto>
   3132 bool CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
   3133                  const JSClass* aClass, JS::RealmOptions& aOptions,
   3134                  JSPrincipals* aPrincipal,
   3135                  JS::MutableHandle<JSObject*> aGlobal) {
   3136  AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Create global object", JS);
   3137  aOptions.creationOptions()
   3138      .setTrace(CreateGlobalOptions<T>::TraceGlobal)
   3139      .setProfilerRealmID(GetWindowID(aNative));
   3140  xpc::SetPrefableRealmOptions(aOptions);
   3141 
   3142  aGlobal.set(JS_NewGlobalObject(aCx, aClass, aPrincipal,
   3143                                 JS::DontFireOnNewGlobalHook, aOptions));
   3144  if (!aGlobal) {
   3145    NS_WARNING("Failed to create global");
   3146    return false;
   3147  }
   3148 
   3149  JSAutoRealm ar(aCx, aGlobal);
   3150 
   3151  {
   3152    JS::SetReservedSlot(aGlobal, DOM_OBJECT_SLOT, JS::PrivateValue(aNative));
   3153    NS_ADDREF(aNative);
   3154 
   3155    aCache->SetWrapper(aGlobal);
   3156 
   3157    dom::AllocateProtoAndIfaceCache(
   3158        aGlobal, CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
   3159 
   3160    if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
   3161      return false;
   3162    }
   3163 
   3164    // Initializing this at this point for nsGlobalWindowInner makes no sense,
   3165    // because GetRTPCallerType doesn't return the correct result before
   3166    // the global is completely initialized with a document.
   3167    if constexpr (!std::is_base_of_v<nsGlobalWindowInner, T>) {
   3168      JS::SetRealmReduceTimerPrecisionCallerType(
   3169          js::GetNonCCWObjectRealm(aGlobal),
   3170          RTPCallerTypeToToken(aNative->GetRTPCallerType()));
   3171    }
   3172  }
   3173 
   3174  JS::Handle<JSObject*> proto = GetProto(aCx);
   3175  if (!proto || !JS_SetPrototype(aCx, aGlobal, proto)) {
   3176    NS_WARNING("Failed to set proto");
   3177    return false;
   3178  }
   3179 
   3180  bool succeeded;
   3181  if (!JS_SetImmutablePrototype(aCx, aGlobal, &succeeded)) {
   3182    return false;
   3183  }
   3184  MOZ_ASSERT(succeeded,
   3185             "making a fresh global object's [[Prototype]] immutable can "
   3186             "internally fail, but it should never be unsuccessful");
   3187 
   3188  return true;
   3189 }
   3190 
   3191 namespace binding_detail {
   3192 /**
   3193 * WebIDL getters have a "generic" JSNative that is responsible for the
   3194 * following things:
   3195 *
   3196 * 1) Determining the "this" pointer for the C++ call.
   3197 * 2) Extracting the "specialized" getter from the jitinfo on the JSFunction.
   3198 * 3) Calling the specialized getter.
   3199 * 4) Handling exceptions as needed.
   3200 *
   3201 * There are several variants of (1) depending on the interface involved and
   3202 * there are two variants of (4) depending on whether the return type is a
   3203 * Promise.  We handle this by templating our generic getter on a
   3204 * this-determination policy and an exception handling policy, then explicitly
   3205 * instantiating the relevant template specializations.
   3206 */
   3207 template <typename ThisPolicy, typename ExceptionPolicy>
   3208 bool GenericGetter(JSContext* cx, unsigned argc, JS::Value* vp);
   3209 
   3210 /**
   3211 * WebIDL setters have a "generic" JSNative that is responsible for the
   3212 * following things:
   3213 *
   3214 * 1) Determining the "this" pointer for the C++ call.
   3215 * 2) Extracting the "specialized" setter from the jitinfo on the JSFunction.
   3216 * 3) Calling the specialized setter.
   3217 *
   3218 * There are several variants of (1) depending on the interface
   3219 * involved.  We handle this by templating our generic setter on a
   3220 * this-determination policy, then explicitly instantiating the
   3221 * relevant template specializations.
   3222 */
   3223 template <typename ThisPolicy>
   3224 bool GenericSetter(JSContext* cx, unsigned argc, JS::Value* vp);
   3225 
   3226 /**
   3227 * WebIDL methods have a "generic" JSNative that is responsible for the
   3228 * following things:
   3229 *
   3230 * 1) Determining the "this" pointer for the C++ call.
   3231 * 2) Extracting the "specialized" method from the jitinfo on the JSFunction.
   3232 * 3) Calling the specialized methodx.
   3233 * 4) Handling exceptions as needed.
   3234 *
   3235 * There are several variants of (1) depending on the interface involved and
   3236 * there are two variants of (4) depending on whether the return type is a
   3237 * Promise.  We handle this by templating our generic method on a
   3238 * this-determination policy and an exception handling policy, then explicitly
   3239 * instantiating the relevant template specializations.
   3240 */
   3241 template <typename ThisPolicy, typename ExceptionPolicy>
   3242 bool GenericMethod(JSContext* cx, unsigned argc, JS::Value* vp);
   3243 
   3244 // A this-extraction policy for normal getters/setters/methods.
   3245 struct NormalThisPolicy;
   3246 
   3247 // A this-extraction policy for getters/setters/methods on interfaces
   3248 // that are on some global's proto chain.
   3249 struct MaybeGlobalThisPolicy;
   3250 
   3251 // A this-extraction policy for lenient getters/setters.
   3252 struct LenientThisPolicy;
   3253 
   3254 // A this-extraction policy for cross-origin getters/setters/methods.
   3255 struct CrossOriginThisPolicy;
   3256 
   3257 // A this-extraction policy for getters/setters/methods that should
   3258 // not be allowed to be called cross-origin but expect objects that
   3259 // _can_ be cross-origin.
   3260 struct MaybeCrossOriginObjectThisPolicy;
   3261 
   3262 // A this-extraction policy which is just like
   3263 // MaybeCrossOriginObjectThisPolicy but has lenient-this behavior.
   3264 struct MaybeCrossOriginObjectLenientThisPolicy;
   3265 
   3266 // An exception-reporting policy for normal getters/setters/methods.
   3267 struct ThrowExceptions;
   3268 
   3269 // An exception-handling policy for Promise-returning getters/methods.
   3270 struct ConvertExceptionsToPromises;
   3271 }  // namespace binding_detail
   3272 
   3273 bool StaticMethodPromiseWrapper(JSContext* cx, unsigned argc, JS::Value* vp);
   3274 
   3275 // ConvertExceptionToPromise should only be called when we have an error
   3276 // condition (e.g. returned false from a JSAPI method).  Note that there may be
   3277 // no exception on cx, in which case this is an uncatchable failure that will
   3278 // simply be propagated.  Otherwise this method will attempt to convert the
   3279 // exception to a Promise rejected with the exception that it will store in
   3280 // rval.
   3281 bool ConvertExceptionToPromise(JSContext* cx,
   3282                               JS::MutableHandle<JS::Value> rval);
   3283 
   3284 #ifdef DEBUG
   3285 void AssertReturnTypeMatchesJitinfo(const JSJitInfo* aJitinfo,
   3286                                    JS::Handle<JS::Value> aValue);
   3287 #endif
   3288 
   3289 bool CallerSubsumes(JSObject* aObject);
   3290 
   3291 MOZ_ALWAYS_INLINE bool CallerSubsumes(JS::Handle<JS::Value> aValue) {
   3292  if (!aValue.isObject()) {
   3293    return true;
   3294  }
   3295  return CallerSubsumes(&aValue.toObject());
   3296 }
   3297 
   3298 template <class T, class S>
   3299 inline RefPtr<T> StrongOrRawPtr(already_AddRefed<S>&& aPtr) {
   3300  return std::move(aPtr);
   3301 }
   3302 
   3303 template <class T, class S>
   3304 inline RefPtr<T> StrongOrRawPtr(RefPtr<S>&& aPtr) {
   3305  return std::move(aPtr);
   3306 }
   3307 
   3308 template <class T, typename = std::enable_if_t<IsRefcounted<T>::value>>
   3309 inline T* StrongOrRawPtr(T* aPtr) {
   3310  return aPtr;
   3311 }
   3312 
   3313 template <class T, class S,
   3314          typename = std::enable_if_t<!IsRefcounted<S>::value>>
   3315 inline UniquePtr<T> StrongOrRawPtr(UniquePtr<S>&& aPtr) {
   3316  return std::move(aPtr);
   3317 }
   3318 
   3319 template <class T, template <typename> class SmartPtr, class S>
   3320 inline void StrongOrRawPtr(SmartPtr<S>&& aPtr) = delete;
   3321 
   3322 template <class T>
   3323 using StrongPtrForMember =
   3324    std::conditional_t<IsRefcounted<T>::value, RefPtr<T>, UniquePtr<T>>;
   3325 
   3326 namespace binding_detail {
   3327 inline JSObject* GetHackedNamespaceProtoObject(JSContext* aCx) {
   3328  return JS_NewPlainObject(aCx);
   3329 }
   3330 }  // namespace binding_detail
   3331 
   3332 // Resolve an id on the given global object that wants to be included in
   3333 // Exposed=System webidl annotations.  False return value means exception
   3334 // thrown.
   3335 bool SystemGlobalResolve(JSContext* cx, JS::Handle<JSObject*> obj,
   3336                         JS::Handle<jsid> id, bool* resolvedp);
   3337 
   3338 // Enumerate all ids on the given global object that wants to be included in
   3339 // Exposed=System webidl annotations.  False return value means exception
   3340 // thrown.
   3341 bool SystemGlobalEnumerate(JSContext* cx, JS::Handle<JSObject*> obj);
   3342 
   3343 // Slot indexes for maplike/setlike forEach functions
   3344 #define FOREACH_CALLBACK_SLOT 0
   3345 #define FOREACH_MAPLIKEORSETLIKEOBJ_SLOT 1
   3346 
   3347 // Backing function for running .forEach() on maplike/setlike interfaces.
   3348 // Unpacks callback and maplike/setlike object from reserved slots, then runs
   3349 // callback for each key (and value, for maplikes)
   3350 bool ForEachHandler(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
   3351 
   3352 // Unpacks backing object (ES6 map/set) from the reserved slot of a reflector
   3353 // for a maplike/setlike interface. If backing object does not exist, creates
   3354 // backing object in the compartment of the reflector involved, making this safe
   3355 // to use across compartments/via xrays. Return values of these methods will
   3356 // always be in the context compartment.
   3357 bool GetMaplikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
   3358                             size_t aSlotIndex,
   3359                             JS::MutableHandle<JSObject*> aBackingObj,
   3360                             bool* aBackingObjCreated);
   3361 bool GetSetlikeBackingObject(JSContext* aCx, JS::Handle<JSObject*> aObj,
   3362                             size_t aSlotIndex,
   3363                             JS::MutableHandle<JSObject*> aBackingObj,
   3364                             bool* aBackingObjCreated);
   3365 
   3366 // Unpacks backing object (ES Proxy exotic object) from the reserved slot of a
   3367 // reflector for a observableArray attribute. If backing object does not exist,
   3368 // creates backing object in the compartment of the reflector involved, making
   3369 // this safe to use across compartments/via xrays. Return values of these
   3370 // methods will always be in the context compartment.
   3371 bool GetObservableArrayBackingObject(
   3372    JSContext* aCx, JS::Handle<JSObject*> aObj, size_t aSlotIndex,
   3373    JS::MutableHandle<JSObject*> aBackingObj, bool* aBackingObjCreated,
   3374    const ObservableArrayProxyHandler* aHandler, void* aOwner);
   3375 
   3376 // Get the desired prototype object for an object construction from the given
   3377 // CallArgs.  The CallArgs must be for a constructor call.  The
   3378 // aProtoId/aCreator arguments are used to get a default if we don't find a
   3379 // prototype on the newTarget of the callargs.
   3380 bool GetDesiredProto(JSContext* aCx, const JS::CallArgs& aCallArgs,
   3381                     prototypes::id::ID aProtoId,
   3382                     CreateInterfaceObjectsMethod aCreator,
   3383                     JS::MutableHandle<JSObject*> aDesiredProto);
   3384 
   3385 // This function is expected to be called from the constructor function for an
   3386 // HTML or XUL element interface; the global/callargs need to be whatever was
   3387 // passed to that constructor function.
   3388 already_AddRefed<Element> CreateXULOrHTMLElement(
   3389    const GlobalObject& aGlobal, const JS::CallArgs& aCallArgs,
   3390    JS::Handle<JSObject*> aGivenProto, ErrorResult& aRv);
   3391 
   3392 void SetUseCounter(JSObject* aObject, UseCounter aUseCounter);
   3393 void SetUseCounter(UseCounterWorker aUseCounter);
   3394 
   3395 // Warnings
   3396 void DeprecationWarning(JSContext* aCx, JSObject* aObject,
   3397                        DeprecatedOperations aOperation);
   3398 
   3399 void DeprecationWarning(const GlobalObject& aGlobal,
   3400                        DeprecatedOperations aOperation);
   3401 
   3402 namespace binding_detail {
   3403 // Get a JS global object that can be used for some temporary allocations.  The
   3404 // idea is that this should be used for situations when you need to operate in
   3405 // _some_ compartment but don't care which one.  A typical example is when you
   3406 // have non-JS input, non-JS output, but have to go through some sort of JS
   3407 // representation in the middle, so need a compartment to allocate things in.
   3408 //
   3409 // It's VERY important that any consumers of this function only do things that
   3410 // are guaranteed to be side-effect-free, even in the face of a script
   3411 // environment controlled by a hostile adversary.  This is because in the worker
   3412 // case the global is in fact the worker global, so it and its standard objects
   3413 // are controlled by the worker script.  This is why this function is in the
   3414 // binding_detail namespace.  Any use of this function MUST be very carefully
   3415 // reviewed by someone who is sufficiently devious and has a very good
   3416 // understanding of all the code that will run while we're using the return
   3417 // value, including the SpiderMonkey parts.
   3418 JSObject* UnprivilegedJunkScopeOrWorkerGlobal(const fallible_t&);
   3419 
   3420 // Implementation of the [HTMLConstructor] extended attribute.
   3421 bool HTMLConstructor(JSContext* aCx, unsigned aArgc, JS::Value* aVp,
   3422                     constructors::id::ID aConstructorId,
   3423                     prototypes::id::ID aProtoId,
   3424                     CreateInterfaceObjectsMethod aCreator);
   3425 
   3426 // A method to test whether an attribute with the given JSJitGetterOp getter is
   3427 // enabled in the given set of prefable proeprty specs.  For use for toJSON
   3428 // conversions.  aObj is the object that would be used as the "this" value.
   3429 bool IsGetterEnabled(JSContext* aCx, JS::Handle<JSObject*> aObj,
   3430                     JSJitGetterOp aGetter,
   3431                     const Prefable<const JSPropertySpec>* aAttributes);
   3432 
   3433 // A class that can be used to examine the chars of a linear string.
   3434 class StringIdChars {
   3435 public:
   3436  // Require a non-const ref to an AutoRequireNoGC to prevent callers
   3437  // from passing temporaries.
   3438  StringIdChars(JS::AutoRequireNoGC& nogc, JSLinearString* str) {
   3439    mIsLatin1 = JS::LinearStringHasLatin1Chars(str);
   3440    if (mIsLatin1) {
   3441      mLatin1Chars = JS::GetLatin1LinearStringChars(nogc, str);
   3442    } else {
   3443      mTwoByteChars = JS::GetTwoByteLinearStringChars(nogc, str);
   3444    }
   3445 #ifdef DEBUG
   3446    mLength = JS::GetLinearStringLength(str);
   3447 #endif  // DEBUG
   3448  }
   3449 
   3450  MOZ_ALWAYS_INLINE char16_t operator[](size_t index) {
   3451    MOZ_ASSERT(index < mLength);
   3452    if (mIsLatin1) {
   3453      return mLatin1Chars[index];
   3454    }
   3455    return mTwoByteChars[index];
   3456  }
   3457 
   3458 private:
   3459  bool mIsLatin1;
   3460  union {
   3461    const JS::Latin1Char* mLatin1Chars;
   3462    const char16_t* mTwoByteChars;
   3463  };
   3464 #ifdef DEBUG
   3465  size_t mLength;
   3466 #endif  // DEBUG
   3467 };
   3468 
   3469 already_AddRefed<Promise> CreateRejectedPromiseFromThrownException(
   3470    JSContext* aCx, ErrorResult& aError);
   3471 
   3472 template <auto ConstructorEnabled>
   3473 inline bool ShouldExpose(JSContext* aCx, JS::Handle<JSObject*> aGlobal,
   3474                         DefineInterfaceProperty aDefine) {
   3475  return aDefine == DefineInterfaceProperty::Always ||
   3476         (aDefine == DefineInterfaceProperty::CheckExposure &&
   3477          ConstructorEnabled(aCx, aGlobal));
   3478 }
   3479 
   3480 class ReflectedHTMLAttributeSlotsBase {
   3481 protected:
   3482  static void ForEachXrayReflectedHTMLAttributeSlots(
   3483      JS::RootingContext* aCx, JSObject* aObject, size_t aSlotIndex,
   3484      size_t aArrayIndex, void (*aFunc)(void* aSlots, size_t aArrayIndex));
   3485  static void XrayExpandoObjectFinalize(JS::GCContext* aCx, JSObject* aObject);
   3486 };
   3487 
   3488 template <size_t SlotIndex, size_t XrayExpandoSlotIndex, size_t Count>
   3489 class ReflectedHTMLAttributeSlots : public Array<JS::Heap<JS::Value>, Count>,
   3490                                    private ReflectedHTMLAttributeSlotsBase {
   3491 public:
   3492  using Array<JS::Heap<JS::Value>, Count>::Array;
   3493 
   3494  static ReflectedHTMLAttributeSlots& GetOrCreate(JSObject* aSlotStorage,
   3495                                                  bool aIsXray) {
   3496    size_t slotIndex = aIsXray ? XrayExpandoSlotIndex : SlotIndex;
   3497    JS::Value v = JS::GetReservedSlot(aSlotStorage, slotIndex);
   3498    ReflectedHTMLAttributeSlots* array;
   3499    if (v.isUndefined()) {
   3500      array = new ReflectedHTMLAttributeSlots();
   3501      JS::SetReservedSlot(aSlotStorage, slotIndex, JS::PrivateValue(array));
   3502    } else {
   3503      array = static_cast<ReflectedHTMLAttributeSlots*>(v.toPrivate());
   3504    }
   3505    return *array;
   3506  }
   3507 
   3508  static void Clear(JSObject* aObject, size_t aArrayIndex) {
   3509    JS::Value array = JS::GetReservedSlot(aObject, SlotIndex);
   3510    if (!array.isUndefined()) {
   3511      ReflectedHTMLAttributeSlots& slots =
   3512          *static_cast<ReflectedHTMLAttributeSlots*>(array.toPrivate());
   3513      slots[aArrayIndex] = JS::UndefinedValue();
   3514    }
   3515  }
   3516  static void ClearInXrays(JS::RootingContext* aCx, JSObject* aObject,
   3517                           size_t aArrayIndex) {
   3518    ReflectedHTMLAttributeSlotsBase::ForEachXrayReflectedHTMLAttributeSlots(
   3519        aCx, aObject, XrayExpandoSlotIndex, aArrayIndex,
   3520        [](void* aSlots, size_t aArrayIndex) {
   3521          ReflectedHTMLAttributeSlots& slots =
   3522              *static_cast<ReflectedHTMLAttributeSlots*>(aSlots);
   3523          slots[aArrayIndex] = JS::UndefinedValue();
   3524        });
   3525  }
   3526 
   3527  static void Trace(JSTracer* aTracer, JSObject* aObject) {
   3528    Trace(aTracer, aObject, SlotIndex);
   3529  }
   3530 
   3531  static void Finalize(JSObject* aObject) { Finalize(aObject, SlotIndex); }
   3532 
   3533  static void XrayExpandoObjectTrace(JSTracer* aTracer, JSObject* aObject) {
   3534    Trace(aTracer, aObject, XrayExpandoSlotIndex);
   3535  }
   3536 
   3537  static void XrayExpandoObjectFinalize(JS::GCContext* aCx, JSObject* aObject) {
   3538    Finalize(aObject, XrayExpandoSlotIndex);
   3539    ReflectedHTMLAttributeSlotsBase::XrayExpandoObjectFinalize(aCx, aObject);
   3540  }
   3541 
   3542  static constexpr JSClassOps sXrayExpandoObjectClassOps = {
   3543      nullptr, /* addProperty */
   3544      nullptr, /* delProperty */
   3545      nullptr, /* enumerate */
   3546      nullptr, /* newEnumerate */
   3547      nullptr, /* resolve */
   3548      nullptr, /* mayResolve */
   3549      XrayExpandoObjectFinalize,
   3550      nullptr, /* call */
   3551      nullptr, /* construct */
   3552      XrayExpandoObjectTrace,
   3553  };
   3554 
   3555 private:
   3556  static void Trace(JSTracer* aTracer, JSObject* aObject, size_t aSlotIndex) {
   3557    JS::Value slotValue = JS::GetReservedSlot(aObject, aSlotIndex);
   3558    if (!slotValue.isUndefined()) {
   3559      auto* array =
   3560          static_cast<ReflectedHTMLAttributeSlots*>(slotValue.toPrivate());
   3561      for (JS::Heap<JS::Value>& v : *array) {
   3562        JS::TraceEdge(aTracer, &v, "ReflectedHTMLAttributeSlots[i]");
   3563      }
   3564    }
   3565  }
   3566  static void Finalize(JSObject* aObject, size_t aSlotIndex) {
   3567    JS::Value slotValue = JS::GetReservedSlot(aObject, aSlotIndex);
   3568    if (!slotValue.isUndefined()) {
   3569      delete static_cast<ReflectedHTMLAttributeSlots*>(slotValue.toPrivate());
   3570      JS::SetReservedSlot(aObject, aSlotIndex, JS::UndefinedValue());
   3571    }
   3572  }
   3573 };
   3574 
   3575 void ClearXrayExpandoSlots(JS::RootingContext* aCx, JSObject* aObject,
   3576                           size_t aSlotIndex);
   3577 
   3578 }  // namespace binding_detail
   3579 
   3580 }  // namespace dom
   3581 
   3582 }  // namespace mozilla
   3583 
   3584 #endif /* mozilla_dom_BindingUtils_h__ */