Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/dom/bindings/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 126 kB image not shown  

Quelle  BindingUtils.h   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef mozilla_dom_BindingUtils_h__
#define mozilla_dom_BindingUtils_h__

#include <type_traits>

#include "jsfriendapi.h"
#include "js/CharacterEncoding.h"
#include "js/Conversions.h"
#include "js/experimental/BindingAllocs.h"
#include "js/experimental/JitInfo.h"  // JSJitGetterOp, JSJitInfo
#include "js/friend/WindowProxy.h"  // js::IsWindow, js::IsWindowProxy, js::ToWindowProxyIfWindow
#include "js/MemoryFunctions.h"
#include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
#include "js/RealmOptions.h"
#include "js/String.h"  // JS::GetLatin1LinearStringChars, JS::GetTwoByteLinearStringChars, JS::GetLinearStringLength, JS::LinearStringHasLatin1Chars, JS::StringHasLatin1Chars
#include "js/Wrapper.h"
#include "js/Zone.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Array.h"
#include "mozilla/Assertions.h"
#include "mozilla/DeferredFinalize.h"
#include "mozilla/EnumTypeTraits.h"
#include "mozilla/EnumeratedRange.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/BindingCallContext.h"
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/DOMJSClass.h"
#include "mozilla/dom/DOMJSProxyHandler.h"
#include "mozilla/dom/JSSlots.h"
#include "mozilla/dom/NonRefcountedDOMObject.h"
#include "mozilla/dom/Nullable.h"
#include "mozilla/dom/PrototypeList.h"
#include "mozilla/dom/RemoteObjectProxy.h"
#include "mozilla/SegmentedVector.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/Likely.h"
#include "mozilla/MemoryReporting.h"
#include "nsIGlobalObject.h"
#include "nsJSUtils.h"
#include "nsISupportsImpl.h"
#include "xpcObjectHelper.h"
#include "xpcpublic.h"
#include "nsIVariant.h"
#include "mozilla/dom/FakeString.h"
#include "mozilla/ProfilerLabels.h"

#include "nsWrapperCacheInlines.h"

class nsGlobalWindowInner;
class nsGlobalWindowOuter;
class nsIInterfaceRequestor;

namespace mozilla {

enum UseCounter : int16_t;
enum class UseCounterWorker : int16_t;

namespace dom {
class CustomElementReactionsStack;
class Document;
class EventTarget;
class MessageManagerGlobal;
class ObservableArrayProxyHandler;
class DedicatedWorkerGlobalScope;
template <typename KeyType, typename ValueType>
class Record;
class WindowProxyHolder;

enum class DeprecatedOperations : uint16_t;

nsresult UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src,
                       const nsIID& iid, void** ppArg);

/** Convert a jsval to an XPCOM pointer. Caller must not assume that src will
    keep the XPCOM pointer rooted. */

template <class Interface>
inline nsresult UnwrapArg(JSContext* cx, JS::Handle<JSObject*> src,
                          Interface** ppArg) {
  return UnwrapArgImpl(cx, src, NS_GET_TEMPLATE_IID(Interface),
                       reinterpret_cast<void**>(ppArg));
}

nsresult UnwrapWindowProxyArg(JSContext* cx, JS::Handle<JSObject*> src,
                              WindowProxyHolder& ppArg);

// Returns true if the JSClass is used for DOM objects.
inline bool IsDOMClass(const JSClass* clasp) {
  return clasp->flags & JSCLASS_IS_DOMJSCLASS;
}

// Return true if the JSClass is used for non-proxy DOM objects.
inline bool IsNonProxyDOMClass(const JSClass* clasp) {
  return IsDOMClass(clasp) && clasp->isNativeObject();
}

// Returns true if the JSClass is used for DOM interface and interface
// prototype objects.
inline bool IsDOMIfaceAndProtoClass(const JSClass* clasp) {
  return clasp->flags & JSCLASS_IS_DOMIFACEANDPROTOJSCLASS;
}

static_assert(DOM_OBJECT_SLOT == 0,
              "DOM_OBJECT_SLOT doesn't match the proxy private slot. "
              "Expect bad things");
template <class T>
inline T* UnwrapDOMObject(JSObject* obj) {
  MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
             "Don't pass non-DOM objects to this function");

  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  return static_cast<T*>(val.toPrivate());
}

template <class T>
inline T* UnwrapPossiblyNotInitializedDOMObject(JSObject* obj) {
  // This is used by the OjectMoved JSClass hook which can be called before
  // JS_NewObject has returned and so before we have a chance to set
  // DOM_OBJECT_SLOT to anything useful.

  MOZ_ASSERT(IsDOMClass(JS::GetClass(obj)),
             "Don't pass non-DOM objects to this function");

  JS::Value val = JS::GetReservedSlot(obj, DOM_OBJECT_SLOT);
  if (val.isUndefined()) {
    return nullptr;
  }
  return static_cast<T*>(val.toPrivate());
}

inline const DOMJSClass* GetDOMClass(const JSClass* clasp) {
  return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
}

inline const DOMJSClass* GetDOMClass(JSObject* obj) {
  return GetDOMClass(JS::GetClass(obj));
}

inline nsISupports* UnwrapDOMObjectToISupports(JSObject* aObject) {
  const DOMJSClass* clasp = GetDOMClass(aObject);
  if (!clasp || !clasp->mDOMObjectIsISupports) {
    return nullptr;
  }

  return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
}

inline bool IsDOMObject(JSObject* obj) { return IsDOMClass(JS::GetClass(obj)); }

// There are two valid ways to use UNWRAP_OBJECT: Either obj needs to
// be a MutableHandle<JSObject*>, or value needs to be a strong-reference
// smart pointer type (OwningNonNull or RefPtr or nsCOMPtr), in which case obj
// can be anything that converts to JSObject*.
//
// This can't be used with Window, EventTarget, or Location as the "Interface"
// argument (and will fail a static_assert if you try to do that).  Use
// UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT to unwrap to those interfaces.
#define UNWRAP_OBJECT(Interface, obj, value)                        \
  mozilla::dom::binding_detail::UnwrapObjectWithCrossOriginAsserts< \
      mozilla::dom::prototypes::id::Interface,                      \
      mozilla::dom::Interface##_Binding::NativeType>(obj, value)

// UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT is just like UNWRAP_OBJECT but requires a
// JSContext in a Realm that represents "who is doing the unwrapping?" to
// properly unwrap the object.
#define UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT(Interface, obj, value, cx)          \
  mozilla::dom::UnwrapObject<mozilla::dom::prototypes::id::Interface,        \
                             mozilla::dom::Interface##_Binding::NativeType>( \
      obj, value, cx)

// Test whether the given object is an instance of the given interface.
#define IS_INSTANCE_OF(Interface, obj)                                       \
  mozilla::dom::IsInstanceOf<mozilla::dom::prototypes::id::Interface,        \
                             mozilla::dom::Interface##_Binding::NativeType>( \
      obj)

// Unwrap the given non-wrapper object.  This can be used with any obj that
// converts to JSObject*; as long as that JSObject* is live the return value
// will be valid.
#define UNWRAP_NON_WRAPPER_OBJECT(Interface, obj, value) \
  mozilla::dom::UnwrapNonWrapperObject<                  \
      mozilla::dom::prototypes::id::Interface,           \
      mozilla::dom::Interface##_Binding::NativeType>(obj, value)

// Some callers don't want to set an exception when unwrapping fails
// (for example, overload resolution uses unwrapping to tell what sort
// of thing it's looking at).
// U must be something that a T* can be assigned to (e.g. T* or an RefPtr<T>).
//
// The obj argument will be mutated to point to CheckedUnwrap of itself if the
// passed-in value is not a DOM object and CheckedUnwrap succeeds.
//
// If mayBeWrapper is true, there are three valid ways to invoke
// UnwrapObjectInternal: Either obj needs to be a class wrapping a
// MutableHandle<JSObject*>, with an assignment operator that sets the handle to
// the given object, or U needs to be a strong-reference smart pointer type
// (OwningNonNull or RefPtr or nsCOMPtr), or the value being stored in "value"
// must not escape past being tested for falsiness immediately after the
// UnwrapObjectInternal call.
//
// If mayBeWrapper is false, obj can just be a JSObject*, and U anything that a
// T* can be assigned to.
//
// The cx arg is in practice allowed to be either nullptr or JSContext* or a
// BindingCallContext reference.  If it's nullptr we will do a
// CheckedUnwrapStatic and it's the caller's responsibility to make sure they're
// not trying to work with Window or Location objects.  Otherwise we'll do a
// CheckedUnwrapDynamic.  This all only matters if mayBeWrapper is true; if it's
// false just pass nullptr for the cx arg.
namespace binding_detail {
template <class T, bool mayBeWrapper, typename U, typename V, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObjectInternal(V& obj, U& value,
                                                prototypes::ID protoID,
                                                uint32_t protoDepth,
                                                const CxType& cx) {
  static_assert(std::is_same_v<CxType, JSContext*> ||
                    std::is_same_v<CxType, BindingCallContext> ||
                    std::is_same_v<CxType, decltype(nullptr)>,
                "Unexpected CxType");

  /* First check to see whether we have a DOM object */
  const DOMJSClass* domClass = GetDOMClass(obj);
  if (domClass) {
    /* This object is a DOM object.  Double-check that it is safely
       castable to T by checking whether it claims to inherit from the
       class identified by protoID. */

    if (domClass->mInterfaceChain[protoDepth] == protoID) {
      value = UnwrapDOMObject<T>(obj);
      return NS_OK;
    }
  }

  /* Maybe we have a security wrapper or outer window? */
  if (!mayBeWrapper || !js::IsWrapper(obj)) {
    // For non-cross-origin-accessible methods and properties, remote object
    // proxies should behave the same as opaque wrappers.
    if (IsRemoteObjectProxy(obj)) {
      return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
    }

    /* Not a DOM object, not a wrapper, just bail */
    return NS_ERROR_XPC_BAD_CONVERT_JS;
  }

  JSObject* unwrappedObj;
  if (std::is_same_v<CxType, decltype(nullptr)>) {
    unwrappedObj = js::CheckedUnwrapStatic(obj);
  } else {
    unwrappedObj =
        js::CheckedUnwrapDynamic(obj, cx, /* stopAtWindowProxy = */ false);
  }
  if (!unwrappedObj) {
    return NS_ERROR_XPC_SECURITY_MANAGER_VETO;
  }

  if (std::is_same_v<CxType, decltype(nullptr)>) {
    // We might still have a windowproxy here.  But it shouldn't matter, because
    // that's not what the caller is looking for, so we're going to fail out
    // anyway below once we do the recursive call to ourselves with wrapper
    // unwrapping disabled.
    MOZ_ASSERT(!js::IsWrapper(unwrappedObj) || js::IsWindowProxy(unwrappedObj));
  } else {
    // We shouldn't have a wrapper by now.
    MOZ_ASSERT(!js::IsWrapper(unwrappedObj));
  }

  // Recursive call is OK, because now we're using false for mayBeWrapper and
  // we never reach this code if that boolean is false, so can't keep calling
  // ourselves.
  //
  // Unwrap into a temporary pointer, because in general unwrapping into
  // something of type U might trigger GC (e.g. release the value currently
  // stored in there, with arbitrary consequences) and invalidate the
  // "unwrappedObj" pointer.
  T* tempValue = nullptr;
  nsresult rv = UnwrapObjectInternal<T, false>(unwrappedObj, tempValue, protoID,
                                               protoDepth, nullptr);
  if (NS_SUCCEEDED(rv)) {
    // Suppress a hazard related to keeping tempValue alive across
    // UnwrapObjectInternal, because the analysis can't tell that this function
    // will not GC if maybeWrapped=False and we've already gone through a level
    // of unwrapping so unwrappedObj will be !IsWrapper.
    JS::AutoSuppressGCAnalysis suppress;

    // It's very important to not update "obj" with the "unwrappedObj" value
    // until we know the unwrap has succeeded.  Otherwise, in a situation in
    // which we have an overload of object and primitive we could end up
    // converting to the primitive from the unwrappedObj, whereas we want to do
    // it from the original object.
    obj = unwrappedObj;
    // And now assign to "value"; at this point we don't care if a GC happens
    // and invalidates unwrappedObj.
    value = tempValue;
    return NS_OK;
  }

  /* It's the wrong sort of DOM object */
  return NS_ERROR_XPC_BAD_CONVERT_JS;
}

struct MutableObjectHandleWrapper {
  explicit MutableObjectHandleWrapper(JS::MutableHandle<JSObject*> aHandle)
      : mHandle(aHandle) {}

  void operator=(JSObject* aObject) {
    MOZ_ASSERT(aObject);
    mHandle.set(aObject);
  }

  operator JSObject*() const { return mHandle; }

 private:
  JS::MutableHandle<JSObject*> mHandle;
};

struct MutableValueHandleWrapper {
  explicit MutableValueHandleWrapper(JS::MutableHandle<JS::Value> aHandle)
      : mHandle(aHandle) {}

  void operator=(JSObject* aObject) {
    MOZ_ASSERT(aObject);
#ifdef ENABLE_RECORD_TUPLE
    MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*aObject));
#endif
    mHandle.setObject(*aObject);
  }

  operator JSObject*() const { return &mHandle.toObject(); }

 private:
  JS::MutableHandle<JS::Value> mHandle;
};

}  // namespace binding_detail

// UnwrapObject overloads that ensure we have a MutableHandle to keep it alive.
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JSObject*> obj,
                                        U& value, const CxType& cx) {
  binding_detail::MutableObjectHandleWrapper wrapper(obj);
  return binding_detail::UnwrapObjectInternal<T, true>(
      wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::MutableHandle<JS::Value> obj,
                                        U& value, const CxType& cx) {
  MOZ_ASSERT(obj.isObject());
  binding_detail::MutableValueHandleWrapper wrapper(obj);
  return binding_detail::UnwrapObjectInternal<T, true>(
      wrapper, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

// UnwrapObject overloads that ensure we have a strong ref to keep it alive.
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, RefPtr<U>& value,
                                        const CxType& cx) {
  return binding_detail::UnwrapObjectInternal<T, true>(
      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, nsCOMPtr<U>& value,
                                        const CxType& cx) {
  return binding_detail::UnwrapObjectInternal<T, true>(
      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, OwningNonNull<U>& value,
                                        const CxType& cx) {
  return binding_detail::UnwrapObjectInternal<T, true>(
      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JSObject* obj, NonNull<U>& value,
                                        const CxType& cx) {
  return binding_detail::UnwrapObjectInternal<T, true>(
      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, cx);
}

// An UnwrapObject overload that just calls one of the JSObject* ones.
template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj, U& value,
                                        const CxType& cx) {
  MOZ_ASSERT(obj.isObject());
  return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
}

template <prototypes::ID PrototypeID, class T, typename U, typename CxType>
MOZ_ALWAYS_INLINE nsresult UnwrapObject(JS::Handle<JS::Value> obj,
                                        NonNull<U>& value, const CxType& cx) {
  MOZ_ASSERT(obj.isObject());
  return UnwrapObject<PrototypeID, T>(&obj.toObject(), value, cx);
}

template <prototypes::ID PrototypeID>
MOZ_ALWAYS_INLINE void AssertStaticUnwrapOK() {
  static_assert(PrototypeID != prototypes::id::Window,
                "Can't do static unwrap of WindowProxy; use "
                "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
                "aware version of IS_INSTANCE_OF");
  static_assert(PrototypeID != prototypes::id::EventTarget,
                "Can't do static unwrap of WindowProxy (which an EventTarget "
                "might be); use UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a "
                "cross-origin-object aware version of IS_INSTANCE_OF");
  static_assert(PrototypeID != prototypes::id::Location,
                "Can't do static unwrap of Location; use "
                "UNWRAP_MAYBE_CROSS_ORIGIN_OBJECT or a cross-origin-object "
                "aware version of IS_INSTANCE_OF");
}

namespace binding_detail {
// This function is just here so we can do some static asserts in a centralized
// place instead of putting them in every single UnwrapObject overload.
template <prototypes::ID PrototypeID, class T, typename U, typename V>
MOZ_ALWAYS_INLINE nsresult UnwrapObjectWithCrossOriginAsserts(V&& obj,
                                                              U& value) {
  AssertStaticUnwrapOK<PrototypeID>();
  return UnwrapObject<PrototypeID, T>(obj, value, nullptr);
}
}  // namespace binding_detail

template <prototypes::ID PrototypeID, class T>
MOZ_ALWAYS_INLINE bool IsInstanceOf(JSObject* obj) {
  AssertStaticUnwrapOK<PrototypeID>();
  void* ignored;
  nsresult unwrapped = binding_detail::UnwrapObjectInternal<T, true>(
      obj, ignored, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
  return NS_SUCCEEDED(unwrapped);
}

template <prototypes::ID PrototypeID, class T, typename U>
MOZ_ALWAYS_INLINE nsresult UnwrapNonWrapperObject(JSObject* obj, U& value) {
  MOZ_ASSERT(!js::IsWrapper(obj));
  return binding_detail::UnwrapObjectInternal<T, false>(
      obj, value, PrototypeID, PrototypeTraits<PrototypeID>::Depth, nullptr);
}

MOZ_ALWAYS_INLINE bool IsConvertibleToDictionary(JS::Handle<JS::Value> val) {
  return val.isNullOrUndefined() || val.isObject();
}

// The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
// constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
// The end of the prototype objects should be the start of the interface
// objects, and the end of the interface objects should be the start of the
// named properties objects.
static_assert((size_t)constructors::id::_ID_Start ==
                      (size_t)prototypes::id::_ID_Count &&
                  (size_t)namedpropertiesobjects::id::_ID_Start ==
                      (size_t)constructors::id::_ID_Count,
              "Overlapping or discontiguous indexes.");
const size_t kProtoAndIfaceCacheCount = namedpropertiesobjects::id::_ID_Count;

class ProtoAndIfaceCache {
  // The caching strategy we use depends on what sort of global we're dealing
  // with.  For a window-like global, we want everything to be as fast as
  // possible, so we use a flat array, indexed by prototype/constructor ID.
  // For everything else (e.g. globals for JSMs), space is more important than
  // speed, so we use a two-level lookup table.

  class ArrayCache
      : public Array<JS::Heap<JSObject*>, kProtoAndIfaceCacheCount> {
   public:
    bool HasEntryInSlot(size_t i) {
      // Do an explicit call to the Heap<…> bool conversion operator. Because
      // that operator is marked explicit we'd otherwise end up doing an
      // implicit cast to JSObject* first, causing an unnecessary call to
      // exposeToActiveJS().
      return bool((*this)[i]);
    }

    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) { return (*this)[i]; }

    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) { return (*this)[i]; }

    void Trace(JSTracer* aTracer) {
      for (size_t i = 0; i < std::size(*this); ++i) {
        JS::TraceEdge(aTracer, &(*this)[i], "protoAndIfaceCache[i]");
      }
    }

    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
      return aMallocSizeOf(this);
    }
  };

  class PageTableCache {
   public:
    PageTableCache() { memset(mPages.begin(), 0, sizeof(mPages)); }

    ~PageTableCache() {
      for (size_t i = 0; i < std::size(mPages); ++i) {
        delete mPages[i];
      }
    }

    bool HasEntryInSlot(size_t i) {
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
      size_t pageIndex = i / kPageSize;
      size_t leafIndex = i % kPageSize;
      Page* p = mPages[pageIndex];
      if (!p) {
        return false;
      }
      // Do an explicit call to the Heap<…> bool conversion operator. Because
      // that operator is marked explicit we'd otherwise end up doing an
      // implicit cast to JSObject* first, causing an unnecessary call to
      // exposeToActiveJS().
      return bool((*p)[leafIndex]);
    }

    JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
      size_t pageIndex = i / kPageSize;
      size_t leafIndex = i % kPageSize;
      Page* p = mPages[pageIndex];
      if (!p) {
        p = new Page;
        mPages[pageIndex] = p;
      }
      return (*p)[leafIndex];
    }

    JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
      MOZ_ASSERT(i < kProtoAndIfaceCacheCount);
      size_t pageIndex = i / kPageSize;
      size_t leafIndex = i % kPageSize;
      Page* p = mPages[pageIndex];
      MOZ_ASSERT(p);
      return (*p)[leafIndex];
    }

    void Trace(JSTracer* trc) {
      for (size_t i = 0; i < std::size(mPages); ++i) {
        Page* p = mPages[i];
        if (p) {
          for (size_t j = 0; j < std::size(*p); ++j) {
            JS::TraceEdge(trc, &(*p)[j], "protoAndIfaceCache[i]");
          }
        }
      }
    }

    size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
      size_t n = aMallocSizeOf(this);
      for (size_t i = 0; i < std::size(mPages); ++i) {
        n += aMallocSizeOf(mPages[i]);
      }
      return n;
    }

   private:
    static const size_t kPageSize = 16;
    typedef Array<JS::Heap<JSObject*>, kPageSize> Page;
    static const size_t kNPages =
        kProtoAndIfaceCacheCount / kPageSize +
        size_t(bool(kProtoAndIfaceCacheCount % kPageSize));
    Array<Page*, kNPages> mPages;
  };

 public:
  enum Kind { WindowLike, NonWindowLike };

  explicit ProtoAndIfaceCache(Kind aKind) : mKind(aKind) {
    MOZ_COUNT_CTOR(ProtoAndIfaceCache);
    if (aKind == WindowLike) {
      mArrayCache = new ArrayCache();
    } else {
      mPageTableCache = new PageTableCache();
    }
  }

  ~ProtoAndIfaceCache() {
    if (mKind == WindowLike) {
      delete mArrayCache;
    } else {
      delete mPageTableCache;
    }
    MOZ_COUNT_DTOR(ProtoAndIfaceCache);
  }

#define FORWARD_OPERATION(opName, args)    \
  do {                                     \
    if (mKind == WindowLike) {             \
      return mArrayCache->opName args;     \
    } else {                               \
      return mPageTableCache->opName args; \
    }                                      \
  } while (0)

  // Return whether slot i contains an object.  This doesn't return the object
  // itself because in practice consumers just want to know whether it's there
  // or not, and that doesn't require barriering, which returning the object
  // pointer does.
  bool HasEntryInSlot(size_t i) { FORWARD_OPERATION(HasEntryInSlot, (i)); }

  // Return a reference to slot i, creating it if necessary.  There
  // may not be an object in the returned slot.
  JS::Heap<JSObject*>& EntrySlotOrCreate(size_t i) {
    FORWARD_OPERATION(EntrySlotOrCreate, (i));
  }

  // Return a reference to slot i, which is guaranteed to already
  // exist.  There may not be an object in the slot, if prototype and
  // constructor initialization for one of our bindings failed.
  JS::Heap<JSObject*>& EntrySlotMustExist(size_t i) {
    FORWARD_OPERATION(EntrySlotMustExist, (i));
  }

  void Trace(JSTracer* aTracer) { FORWARD_OPERATION(Trace, (aTracer)); }

  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) {
    size_t n = aMallocSizeOf(this);
    n += (mKind == WindowLike
              ? mArrayCache->SizeOfIncludingThis(aMallocSizeOf)
              : mPageTableCache->SizeOfIncludingThis(aMallocSizeOf));
    return n;
  }
#undef FORWARD_OPERATION

 private:
  union {
    ArrayCache* mArrayCache;
    PageTableCache* mPageTableCache;
  };
  Kind mKind;
};

inline void AllocateProtoAndIfaceCache(JSObject* obj,
                                       ProtoAndIfaceCache::Kind aKind) {
  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);
  MOZ_ASSERT(JS::GetReservedSlot(obj, DOM_PROTOTYPE_SLOT).isUndefined());

  ProtoAndIfaceCache* protoAndIfaceCache = new ProtoAndIfaceCache(aKind);

  JS::SetReservedSlot(obj, DOM_PROTOTYPE_SLOT,
                      JS::PrivateValue(protoAndIfaceCache));
}

#ifdef DEBUG
struct VerifyTraceProtoAndIfaceCacheCalledTracer : public JS::CallbackTracer {
  bool ok;

  explicit VerifyTraceProtoAndIfaceCacheCalledTracer(JSContext* cx)
      : JS::CallbackTracer(cx, JS::TracerKind::VerifyTraceProtoAndIface),
        ok(false) {}

  void onChild(JS::GCCellPtr, const char* name) override {
    // We don't do anything here, we only want to verify that
    // TraceProtoAndIfaceCache was called.
  }
};
#endif

inline void TraceProtoAndIfaceCache(JSTracer* trc, JSObject* obj) {
  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);

#ifdef DEBUG
  if (trc->kind() == JS::TracerKind::VerifyTraceProtoAndIface) {
    // We don't do anything here, we only want to verify that
    // TraceProtoAndIfaceCache was called.
    static_cast<VerifyTraceProtoAndIfaceCacheCalledTracer*>(trc)->ok = true;
    return;
  }
#endif

  if (!DOMGlobalHasProtoAndIFaceCache(obj)) return;
  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);
  protoAndIfaceCache->Trace(trc);
}

inline void DestroyProtoAndIfaceCache(JSObject* obj) {
  MOZ_ASSERT(JS::GetClass(obj)->flags & JSCLASS_DOM_GLOBAL);

  if (!DOMGlobalHasProtoAndIFaceCache(obj)) {
    return;
  }

  ProtoAndIfaceCache* protoAndIfaceCache = GetProtoAndIfaceCache(obj);

  delete protoAndIfaceCache;
}

/**
 * Add constants to an object.
 */

bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
                     const ConstantSpec* cs);

struct JSNativeHolder {
  JSNative mNative;
  const NativePropertyHooks* mPropertyHooks;
};

// Struct for holding information for WebIDL interface objects (which are
// function objects). A pointer to this struct is held in the first reserved
// slot of the function object.
struct DOMInterfaceInfo {
  JSNativeHolder nativeHolder;

  ProtoHandleGetter mGetParentProto;

  const uint32_t mDepth;

  const prototypes::ID mPrototypeID;  // uint16_t

  // Boolean indicating whether this object wants a isInstance property
  // pointing to InterfaceIsInstance defined on it.  Only ever true for
  // interfaces.
  bool wantsInterfaceIsInstance;

  uint8_t mConstructorArgs;

  const char* mConstructorName;
};

struct LegacyFactoryFunction {
  const char* mName;
  const JSNativeHolder mHolder;
  uint8_t mNargs;
};

namespace binding_detail {

void CreateInterfaceObjects(
    JSContext* cx, JS::Handle<JSObject*> global,
    JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
    JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
    const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
    bool isConstructorChromeOnly,
    const Span<const LegacyFactoryFunction>& legacyFactoryFunctions,
    JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
    const NativeProperties* chromeOnlyProperties, const char* name,
    bool defineOnGlobal, const charconst* unscopableNames, bool isGlobal,
    const charconst* legacyWindowAliases);

}  // namespace binding_detail

// clang-format off
/*
 * Create a DOM interface object (if constructorClass is non-null) and/or a
 * DOM interface prototype object (if protoClass is non-null).
 *
 * global is used as the parent of the interface object and the interface
 *        prototype object
 * protoProto is the prototype to use for the interface prototype object.
 * protoClass is the JSClass to use for the interface prototype object.
 *            This is null if we should not create an interface prototype
 *            object.
 * protoCache a pointer to a JSObject pointer where we should cache the
 *            interface prototype object. This must be null if protoClass is and
 *            vice versa.
 * interfaceProto is the prototype to use for the interface object.  This can be
 *                null if interfaceInfo is null (as in, if we're not creating an
 *                interface object at all).
 * interfaceInfo is the info to use for the interface object.  This can be null
 *               if we're not creating an interface object.
 * ctorNargs is the length of the constructor function; 0 if no constructor
 * isConstructorChromeOnly if true, the constructor is ChromeOnly.
 * legacyFactoryFunctions the legacy factory functions (can be empty)
 * constructorCache a pointer to a JSObject pointer where we should cache the
 *                  interface object. This must be null if both constructorClass
 *                  and constructor are null, and non-null otherwise.
 * properties contains the methods, attributes and constants to be defined on
 *            objects in any compartment.
 * chromeProperties contains the methods, attributes and constants to be defined
 *                  on objects in chrome compartments. This must be null if the
 *                  interface doesn't have any ChromeOnly properties or if the
 *                  object is being created in non-chrome compartment.
 * name the name to use for 1) the WebIDL class string, which is the value
 *      that's used for @@toStringTag, 2) the name property for interface
 *      objects and 3) the property on the global object that would be set to
 *      the interface object. In general this is the interface identifier.
 *      LegacyNamespace would expect something different for 1), but we don't
 *      support that. The class string for default iterator objects is not
 *      usable as 2) or 3), but default iterator objects don't have an interface
 *      object.
 * defineOnGlobal controls whether properties should be defined on the given
 *                global for the interface object (if any) and named
 *                constructors (if any) for this interface.  This can be
 *                false in situations where we want the properties to only
 *                appear on privileged Xrays but not on the unprivileged
 *                underlying global.
 * unscopableNames if not null it points to a null-terminated list of const
 *                 char* names of the unscopable properties for this interface.
 * isGlobal if true, we're creating interface objects for a [Global] interface,
 *          and hence shouldn't define properties on the prototype object.
 * legacyWindowAliases if not null it points to a null-terminated list of const
 *                     char* names of the legacy window aliases for this
 *                     interface.
 *
 * At least one of protoClass or interfaceInfo should be non-null. If
 * interfaceInfo is non-null, the resulting interface object will be defined on
 * the given global with property name |name|, which must also be non-null.
 */

// clang-format on
template <size_t N>
inline void CreateInterfaceObjects(
    JSContext* cx, JS::Handle<JSObject*> global,
    JS::Handle<JSObject*> protoProto, const DOMIfaceAndProtoJSClass* protoClass,
    JS::Heap<JSObject*>* protoCache, JS::Handle<JSObject*> interfaceProto,
    const DOMInterfaceInfo* interfaceInfo, unsigned ctorNargs,
    bool isConstructorChromeOnly,
    const Span<const LegacyFactoryFunction, N>& legacyFactoryFunctions,
    JS::Heap<JSObject*>* constructorCache, const NativeProperties* properties,
    const NativeProperties* chromeOnlyProperties, const char* name,
    bool defineOnGlobal, const charconst* unscopableNames, bool isGlobal,
    const charconst* legacyWindowAliases) {
  // We're using 1 slot for the interface info already, so we only have
  // INTERFACE_OBJECT_MAX_SLOTS - 1 slots for legacy factory functions.
  static_assert(N <= INTERFACE_OBJECT_MAX_SLOTS -
                         INTERFACE_OBJECT_FIRST_LEGACY_FACTORY_FUNCTION);

  return binding_detail::CreateInterfaceObjects(
      cx, global, protoProto, protoClass, protoCache, interfaceProto,
      interfaceInfo, ctorNargs, isConstructorChromeOnly, legacyFactoryFunctions,
      constructorCache, properties, chromeOnlyProperties, name, defineOnGlobal,
      unscopableNames, isGlobal, legacyWindowAliases);
}

/*
 * Create a namespace object.
 *
 * global the global on which to install a property named with name pointing to
 *        the namespace object if defineOnGlobal is true.
 * namespaceProto is the prototype to use for the namespace object.
 * namespaceClass is the JSClass to use for the namespace object.
 * namespaceCache a pointer to a JSObject pointer where we should cache the
 *                namespace object.
 * properties contains the methods, attributes and constants to be defined on
 *            objects in any compartment.
 * chromeProperties contains the methods, attributes and constants to be defined
 *                  on objects in chrome compartments. This must be null if the
 *                  namespace doesn't have any ChromeOnly properties or if the
 *                  object is being created in non-chrome compartment.
 * name the name to use for the WebIDL class string, which is the value
 *      that's used for @@toStringTag, and the name of the property on the
 *      global object that would be set to the namespace object.
 * defineOnGlobal controls whether properties should be defined on the given
 *                global for the namespace object. This can be false in
 *                situations where we want the properties to only appear on
 *                privileged Xrays but not on the unprivileged underlying
 *                global.
 */

void CreateNamespaceObject(JSContext* cx, JS::Handle<JSObject*> global,
                           JS::Handle<JSObject*> namespaceProto,
                           const DOMIfaceAndProtoJSClass& namespaceClass,
                           JS::Heap<JSObject*>* namespaceCache,
                           const NativeProperties* properties,
                           const NativeProperties* chromeOnlyProperties,
                           const char* name, bool defineOnGlobal);

/**
 * Define the properties (regular and chrome-only) on obj.
 *
 * obj the object to install the properties on. This should be the interface
 *     prototype object for regular interfaces and the instance object for
 *     interfaces marked with Global.
 * properties contains the methods, attributes and constants to be defined on
 *            objects in any compartment.
 * chromeProperties contains the methods, attributes and constants to be defined
 *                  on objects in chrome compartments. This must be null if the
 *                  interface doesn't have any ChromeOnly properties or if the
 *                  object is being created in non-chrome compartment.
 */

bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
                      const NativeProperties* properties,
                      const NativeProperties* chromeOnlyProperties);

/*
 * Define the legacy unforgeable methods on an object.
 */

bool DefineLegacyUnforgeableMethods(
    JSContext* cx, JS::Handle<JSObject*> obj,
    const Prefable<const JSFunctionSpec>* props);

/*
 * Define the legacy unforgeable attributes on an object.
 */

bool DefineLegacyUnforgeableAttributes(
    JSContext* cx, JS::Handle<JSObject*> obj,
    const Prefable<const JSPropertySpec>* props);

#define HAS_MEMBER_TYPEDEFS \
 private:                   \
  typedef char yes[1];      \
  typedef char no[2]

#ifdef _MSC_VER
#  define HAS_MEMBER_CHECK(_name) \
    template <typename V>         \
    static yes& Check##_name(char(*)[(&V::_name == 0) + 1])
#else
#  define HAS_MEMBER_CHECK(_name) \
    template <typename V>         \
    static yes& Check##_name(char(*)[sizeof(&V::_name) + 1])
#endif

#define HAS_MEMBER(_memberName, _valueName) \
 private:                                   \
  HAS_MEMBER_CHECK(_memberName);            \
  template <typename V>                     \
  static no& Check##_memberName(...);       \
                                            \
 public:                                    \
  static bool const _valueName =            \
      sizeof(Check##_memberName<T>(nullptr)) == sizeof(yes)

template <class T>
struct NativeHasMember {
  HAS_MEMBER_TYPEDEFS;

  HAS_MEMBER(GetParentObject, GetParentObject);
  HAS_MEMBER(WrapObject, WrapObject);
};

template <class T>
struct IsSmartPtr {
  HAS_MEMBER_TYPEDEFS;

  HAS_MEMBER(get, value);
};

template <class T>
struct IsRefcounted {
  HAS_MEMBER_TYPEDEFS;

  HAS_MEMBER(AddRef, HasAddref);
  HAS_MEMBER(Release, HasRelease);

 public:
  static bool const value = HasAddref && HasRelease;

 private:
  // This struct only works if T is fully declared (not just forward declared).
  // The std::is_base_of check will ensure that, we don't really need it for any
  // other reason (the static assert will of course always be true).
  static_assert(!std::is_base_of<nsISupports, T>::value || IsRefcounted::value,
                "Classes derived from nsISupports are refcounted!");
};

#undef HAS_MEMBER
#undef HAS_MEMBER_CHECK
#undef HAS_MEMBER_TYPEDEFS

#ifdef DEBUG
template <class T, bool isISupports = std::is_base_of<nsISupports, T>::value>
struct CheckWrapperCacheCast {
  static bool Check() {
    return reinterpret_cast<uintptr_t>(
               static_cast<nsWrapperCache*>(reinterpret_cast<T*>(1))) == 1;
  }
};
template <class T>
struct CheckWrapperCacheCast<T, true> {
  static bool Check() { return true; }
};
#endif

inline bool TryToOuterize(JS::MutableHandle<JS::Value> rval) {
#ifdef ENABLE_RECORD_TUPLE
  if (rval.isExtendedPrimitive()) {
    return true;
  }
#endif
  MOZ_ASSERT(rval.isObject());
  if (js::IsWindow(&rval.toObject())) {
    JSObject* obj = js::ToWindowProxyIfWindow(&rval.toObject());
    MOZ_ASSERT(obj);
    rval.set(JS::ObjectValue(*obj));
  }

  return true;
}

inline bool TryToOuterize(JS::MutableHandle<JSObject*> obj) {
  if (js::IsWindow(obj)) {
    JSObject* proxy = js::ToWindowProxyIfWindow(obj);
    MOZ_ASSERT(proxy);
    obj.set(proxy);
  }

  return true;
}

// Make sure to wrap the given string value into the right compartment, as
// needed.
MOZ_ALWAYS_INLINE
bool MaybeWrapStringValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(rval.isString());
  JSString* str = rval.toString();
  if (JS::GetStringZone(str) != js::GetContextZone(cx)) {
    return JS_WrapValue(cx, rval);
  }
  return true;
}

// Make sure to wrap the given object value into the right compartment as
// needed.  This will work correctly, but possibly slowly, on all objects.
MOZ_ALWAYS_INLINE
bool MaybeWrapObjectValue(JSContext* cx, JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(rval.hasObjectPayload());

  // Cross-compartment always requires wrapping.
  JSObject* obj = &rval.getObjectPayload();
  if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
    return JS_WrapValue(cx, rval);
  }

  // We're same-compartment, but we might still need to outerize if we
  // have a Window.
  return TryToOuterize(rval);
}

// Like MaybeWrapObjectValue, but working with a
// JS::MutableHandle<JSObject*> which must be non-null.
MOZ_ALWAYS_INLINE
bool MaybeWrapObject(JSContext* cx, JS::MutableHandle<JSObject*> obj) {
  if (JS::GetCompartment(obj) != js::GetContextCompartment(cx)) {
    return JS_WrapObject(cx, obj);
  }

  // We're same-compartment, but we might still need to outerize if we
  // have a Window.
  return TryToOuterize(obj);
}

// Like MaybeWrapObjectValue, but also allows null
MOZ_ALWAYS_INLINE
bool MaybeWrapObjectOrNullValue(JSContext* cx,
                                JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(rval.isObjectOrNull());
  if (rval.isNull()) {
    return true;
  }
  return MaybeWrapObjectValue(cx, rval);
}

// Wrapping for objects that are known to not be DOM objects
MOZ_ALWAYS_INLINE
bool MaybeWrapNonDOMObjectValue(JSContext* cx,
                                JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(rval.isObject());
  // Compared to MaybeWrapObjectValue we just skip the TryToOuterize call.  The
  // only reason it would be needed is if we have a Window object, which would
  // have a DOM class.  Assert that we don't have any DOM-class objects coming
  // through here.
  MOZ_ASSERT(!GetDOMClass(&rval.toObject()));

  JSObject* obj = &rval.toObject();
  if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
    return true;
  }
  return JS_WrapValue(cx, rval);
}

// Like MaybeWrapNonDOMObjectValue but allows null
MOZ_ALWAYS_INLINE
bool MaybeWrapNonDOMObjectOrNullValue(JSContext* cx,
                                      JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(rval.isObjectOrNull());
  if (rval.isNull()) {
    return true;
  }
  return MaybeWrapNonDOMObjectValue(cx, rval);
}

// If rval is a gcthing and is not in the compartment of cx, wrap rval
// into the compartment of cx (typically by replacing it with an Xray or
// cross-compartment wrapper around the original object).
MOZ_ALWAYS_INLINE bool MaybeWrapValue(JSContext* cx,
                                      JS::MutableHandle<JS::Value> rval) {
  if (rval.isGCThing()) {
    if (rval.isString()) {
      return MaybeWrapStringValue(cx, rval);
    }
    if (rval.hasObjectPayload()) {
      return MaybeWrapObjectValue(cx, rval);
    }
    // This could be optimized by checking the zone first, similar to
    // the way strings are handled. At present, this is used primarily
    // for structured cloning, so avoiding the overhead of JS_WrapValue
    // calls is less important than for other types.
    if (rval.isBigInt()) {
      return JS_WrapValue(cx, rval);
    }
    MOZ_ASSERT(rval.isSymbol());
    JS_MarkCrossZoneId(cx, JS::PropertyKey::Symbol(rval.toSymbol()));
  }
  return true;
}

namespace binding_detail {
enum GetOrCreateReflectorWrapBehavior {
  eWrapIntoContextCompartment,
  eDontWrapIntoContextCompartment
};

template <class T>
struct TypeNeedsOuterization {
  // We only need to outerize Window objects, so anything inheriting from
  // nsGlobalWindow (which inherits from EventTarget itself).
  static const bool value = std::is_base_of<nsGlobalWindowInner, T>::value ||
                            std::is_base_of<nsGlobalWindowOuter, T>::value ||
                            std::is_same_v<EventTarget, T>;
};

#ifdef DEBUG
template <typename T, bool isISupports = std::is_base_of<nsISupports, T>::value>
struct CheckWrapperCacheTracing {
  static inline void Check(T* aObject) {}
};

template <typename T>
struct CheckWrapperCacheTracing<T, true> {
  static void Check(T* aObject) {
    // Rooting analysis thinks QueryInterface may GC, but we're dealing with
    // a subset of QueryInterface, C++ only types here.
    JS::AutoSuppressGCAnalysis nogc;

    nsWrapperCache* wrapperCacheFromQI = nullptr;
    aObject->QueryInterface(NS_GET_IID(nsWrapperCache),
                            reinterpret_cast<void**>(&wrapperCacheFromQI));

    MOZ_ASSERT(wrapperCacheFromQI,
               "Missing nsWrapperCache from QueryInterface implementation?");

    if (!wrapperCacheFromQI->GetWrapperPreserveColor()) {
      // Can't assert that we trace the wrapper, since we don't have any
      // wrapper to trace.
      return;
    }

    nsISupports* ccISupports = nullptr;
    aObject->QueryInterface(NS_GET_IID(nsCycleCollectionISupports),
                            reinterpret_cast<void**>(&ccISupports));
    MOZ_ASSERT(ccISupports,
               "nsWrapperCache object which isn't cycle collectable?");

    nsXPCOMCycleCollectionParticipant* participant = nullptr;
    CallQueryInterface(ccISupports, &participant);
    MOZ_ASSERT(participant, "Can't QI to CycleCollectionParticipant?");

    wrapperCacheFromQI->CheckCCWrapperTraversal(ccISupports, participant);
  }
};

void AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
                                  JS::Handle<JSObject*> aGivenProto);
#endif  // DEBUG

template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
MOZ_ALWAYS_INLINE bool DoGetOrCreateDOMReflector(
    JSContext* cx, T* value, JS::Handle<JSObject*> givenProto,
    JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(value);
  MOZ_ASSERT_IF(givenProto, js::IsObjectInContextCompartment(givenProto, cx));
  JSObject* obj = value->GetWrapper();
  if (obj) {
#ifdef DEBUG
    AssertReflectorHasGivenProto(cx, obj, givenProto);
    // Have to reget obj because AssertReflectorHasGivenProto can
    // trigger gc so the pointer may now be invalid.
    obj = value->GetWrapper();
#endif
  } else {
    obj = value->WrapObject(cx, givenProto);
    if (!obj) {
      // At this point, obj is null, so just return false.
      // Callers seem to be testing JS_IsExceptionPending(cx) to
      // figure out whether WrapObject() threw.
      return false;
    }

#ifdef DEBUG
    if (std::is_base_of<nsWrapperCache, T>::value) {
      CheckWrapperCacheTracing<T>::Check(value);
    }
#endif
  }

#ifdef DEBUG
  const DOMJSClass* clasp = GetDOMClass(obj);
  // clasp can be null if the cache contained a non-DOM object.
  if (clasp) {
    // Some sanity asserts about our object.  Specifically:
    // 1)  If our class claims we're nsISupports, we better be nsISupports
    //     XXXbz ideally, we could assert that reinterpret_cast to nsISupports
    //     does the right thing, but I don't see a way to do it.  :(
    // 2)  If our class doesn't claim we're nsISupports we better be
    //     reinterpret_castable to nsWrapperCache.
    MOZ_ASSERT(clasp, "What happened here?");
    MOZ_ASSERT_IF(clasp->mDOMObjectIsISupports,
                  (std::is_base_of<nsISupports, T>::value));
    MOZ_ASSERT(CheckWrapperCacheCast<T>::Check());
  }
#endif

#ifdef ENABLE_RECORD_TUPLE
  MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*obj));
#endif
  rval.set(JS::ObjectValue(*obj));

  if (JS::GetCompartment(obj) == js::GetContextCompartment(cx)) {
    return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
  }

  if (wrapBehavior == eDontWrapIntoContextCompartment) {
    if (TypeNeedsOuterization<T>::value) {
      JSAutoRealm ar(cx, obj);
      return TryToOuterize(rval);
    }

    return true;
  }

  return JS_WrapValue(cx, rval);
}

}  // namespace binding_detail

// Create a JSObject wrapping "value", if there isn't one already, and store it
// in rval.  "value" must be a concrete class that implements a
// GetWrapperPreserveColor() which can return its existing wrapper, if any, and
// a WrapObject() which will try to create a wrapper. Typically, this is done by
// having "value" inherit from nsWrapperCache.
//
// The value stored in rval will be ready to be exposed to whatever JS
// is running on cx right now.  In particular, it will be in the
// compartment of cx, and outerized as needed.
template <class T>
MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflector(
    JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval,
    JS::Handle<JSObject*> givenProto = nullptr) {
  using namespace binding_detail;
  return DoGetOrCreateDOMReflector<T, eWrapIntoContextCompartment>(
      cx, value, givenProto, rval);
}

// Like GetOrCreateDOMReflector but doesn't wrap into the context compartment,
// and hence does not actually require cx to be in a compartment.
template <class T>
MOZ_ALWAYS_INLINE bool GetOrCreateDOMReflectorNoWrap(
    JSContext* cx, T* value, JS::MutableHandle<JS::Value> rval) {
  using namespace binding_detail;
  return DoGetOrCreateDOMReflector<T, eDontWrapIntoContextCompartment>(
      cx, value, nullptr, rval);
}

// Helper for different overloadings of WrapNewBindingNonWrapperCachedObject()
inline bool FinishWrapping(JSContext* cx, JS::Handle<JSObject*> obj,
                           JS::MutableHandle<JS::Value> rval) {
#ifdef ENABLE_RECORD_TUPLE
  // If calling an (object) value's WrapObject() method returned a record/tuple,
  // then something is very wrong.
  MOZ_ASSERT(!js::gc::MaybeForwardedIsExtendedPrimitive(*obj));
#endif

  // We can end up here in all sorts of compartments, per comments in
  // WrapNewBindingNonWrapperCachedObject(). Make sure to JS_WrapValue!
  rval.set(JS::ObjectValue(*obj));
  return MaybeWrapObjectValue(cx, rval);
}

// Create a JSObject wrapping "value", for cases when "value" is a
// non-wrapper-cached object using WebIDL bindings.  "value" must implement a
// WrapObject() method taking a JSContext and a prototype (possibly null) and
// returning the resulting object via a MutableHandle<JSObject*> outparam.
template <class T>
inline bool WrapNewBindingNonWrapperCachedObject(
    JSContext* cx, JS::Handle<JSObject*> scopeArg, T* value,
    JS::MutableHandle<JS::Value> rval,
    JS::Handle<JSObject*> givenProto = nullptr) {
  static_assert(IsRefcounted<T>::value, "Don't pass owned classes in here.");
  MOZ_ASSERT(value);
  // We try to wrap in the realm of the underlying object of "scope"
  JS::Rooted<JSObject*> obj(cx);
  {
    // scope for the JSAutoRealm so that we restore the realm
    // before we call JS_WrapValue.
    Maybe<JSAutoRealm> ar;
    // Maybe<Handle> doesn't so much work, and in any case, adding
    // more Maybe (one for a Rooted and one for a Handle) adds more
    // code (and branches!) than just adding a single rooted.
    JS::Rooted<JSObject*> scope(cx, scopeArg);
    JS::Rooted<JSObject*> proto(cx, givenProto);
    if (js::IsWrapper(scope)) {
      // We are working in the Realm of cx and will be producing our reflector
      // there, so we need to succeed if that realm has access to the scope.
      scope =
          js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
      if (!scope) return false;
      ar.emplace(cx, scope);
      if (!JS_WrapObject(cx, &proto)) {
        return false;
      }
    } else {
      // cx and scope are same-compartment, but they might still be
      // different-Realm.  Enter the Realm of scope, since that's
      // where we want to create our object.
      ar.emplace(cx, scope);
    }

    MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
    if (!value->WrapObject(cx, proto, &obj)) {
      return false;
    }
  }

  return FinishWrapping(cx, obj, rval);
}

// Create a JSObject wrapping "value", for cases when "value" is a
// non-wrapper-cached owned object using WebIDL bindings.  "value" must
// implement a WrapObject() method taking a taking a JSContext and a prototype
// (possibly null) and returning two pieces of information: the resulting object
// via a MutableHandle<JSObject*> outparam and a boolean return value that is
// true if the JSObject took ownership
template <class T>
inline bool WrapNewBindingNonWrapperCachedObject(
    JSContext* cx, JS::Handle<JSObject*> scopeArg, UniquePtr<T>& value,
    JS::MutableHandle<JS::Value> rval,
    JS::Handle<JSObject*> givenProto = nullptr) {
  static_assert(!IsRefcounted<T>::value, "Only pass owned classes in here.");
  // We do a runtime check on value, because otherwise we might in
  // fact end up wrapping a null and invoking methods on it later.
  if (!value) {
    MOZ_CRASH("Don't try to wrap null objects");
  }
  // We try to wrap in the realm of the underlying object of "scope"
  JS::Rooted<JSObject*> obj(cx);
  {
    // scope for the JSAutoRealm so that we restore the realm
    // before we call JS_WrapValue.
    Maybe<JSAutoRealm> ar;
    // Maybe<Handle> doesn't so much work, and in any case, adding
    // more Maybe (one for a Rooted and one for a Handle) adds more
    // code (and branches!) than just adding a single rooted.
    JS::Rooted<JSObject*> scope(cx, scopeArg);
    JS::Rooted<JSObject*> proto(cx, givenProto);
    if (js::IsWrapper(scope)) {
      // We are working in the Realm of cx and will be producing our reflector
      // there, so we need to succeed if that realm has access to the scope.
      scope =
          js::CheckedUnwrapDynamic(scope, cx, /* stopAtWindowProxy = */ false);
      if (!scope) return false;
      ar.emplace(cx, scope);
      if (!JS_WrapObject(cx, &proto)) {
        return false;
      }
    } else {
      // cx and scope are same-compartment, but they might still be
      // different-Realm.  Enter the Realm of scope, since that's
      // where we want to create our object.
      ar.emplace(cx, scope);
    }

    MOZ_ASSERT_IF(proto, js::IsObjectInContextCompartment(proto, cx));
    MOZ_ASSERT(js::IsObjectInContextCompartment(scope, cx));
    if (!value->WrapObject(cx, proto, &obj)) {
      return false;
    }

    // JS object took ownership
    Unused << value.release();
  }

  return FinishWrapping(cx, obj, rval);
}

// Helper for smart pointers (nsRefPtr/nsCOMPtr).
template <template <typenameclass SmartPtr, typename T,
          typename U = std::enable_if_t<IsRefcounted<T>::value, T>,
          typename V = std::enable_if_t<IsSmartPtr<SmartPtr<T>>::value, T>>
inline bool WrapNewBindingNonWrapperCachedObject(
    JSContext* cx, JS::Handle<JSObject*> scope, const SmartPtr<T>& value,
    JS::MutableHandle<JS::Value> rval,
    JS::Handle<JSObject*> givenProto = nullptr) {
  return WrapNewBindingNonWrapperCachedObject(cx, scope, value.get(), rval,
                                              givenProto);
}

// Helper for object references (as opposed to pointers).
template <typename T, typename U = std::enable_if_t<!IsSmartPtr<T>::value, T>>
inline bool WrapNewBindingNonWrapperCachedObject(
    JSContext* cx, JS::Handle<JSObject*> scope, T& value,
    JS::MutableHandle<JS::Value> rval,
    JS::Handle<JSObject*> givenProto = nullptr) {
  return WrapNewBindingNonWrapperCachedObject(cx, scope, &value, rval,
                                              givenProto);
}

template <bool Fatal>
inline bool EnumValueNotFound(BindingCallContext& cx, JS::Handle<JSString*> str,
                              const char* type, const char* sourceDescription);

template <>
inline bool EnumValueNotFound<false>(BindingCallContext& cx,
                                     JS::Handle<JSString*> str,
                                     const char* type,
                                     const char* sourceDescription) {
  // TODO: Log a warning to the console.
  return true;
}

template <>
inline bool EnumValueNotFound<true>(BindingCallContext& cx,
                                    JS::Handle<JSString*> str, const char* type,
                                    const char* sourceDescription) {
  JS::UniqueChars deflated = JS_EncodeStringToUTF8(cx, str);
  if (!deflated) {
    return false;
  }
  return cx.ThrowErrorMessage<MSG_INVALID_ENUM_VALUE>(sourceDescription,
                                                      deflated.get(), type);
}

namespace binding_detail {

template <typename CharT>
inline int FindEnumStringIndexImpl(const CharT* chars, size_t length,
                                   const Span<const nsLiteralCString>& values) {
  for (size_t i = 0; i < values.Length(); ++i) {
    const nsLiteralCString& value = values[i];
    if (length != value.Length()) {
      continue;
    }

    bool equal = true;
    for (size_t j = 0; j != length; ++j) {
      if (unsigned(value.CharAt(j)) != unsigned(chars[j])) {
        equal = false;
        break;
      }
    }

    if (equal) {
      return (int)i;
    }
  }

  return -1;
}

template <bool InvalidValueFatal>
inline bool FindEnumStringIndex(BindingCallContext& cx, JS::Handle<JS::Value> v,
                                const Span<const nsLiteralCString>& values,
                                const char* type, const char* sourceDescription,
                                int* index) {
  // JS_StringEqualsAscii is slow as molasses, so don't use it here.
  JS::Rooted<JSString*> str(cx, JS::ToString(cx, v));
  if (!str) {
    return false;
  }

  {
    size_t length;
    JS::AutoCheckCannotGC nogc;
    if (JS::StringHasLatin1Chars(str)) {
      const JS::Latin1Char* chars =
          JS_GetLatin1StringCharsAndLength(cx, nogc, str, &length);
      if (!chars) {
        return false;
      }
      *index = FindEnumStringIndexImpl(chars, length, values);
    } else {
      const char16_t* chars =
          JS_GetTwoByteStringCharsAndLength(cx, nogc, str, &length);
      if (!chars) {
        return false;
      }
      *index = FindEnumStringIndexImpl(chars, length, values);
    }
    if (*index >= 0) {
      return true;
    }
  }

  return EnumValueNotFound<InvalidValueFatal>(cx, str, type, sourceDescription);
}

}  // namespace binding_detail

template <typename Enumclass StringT>
inline Maybe<Enum> StringToEnum(const StringT& aString) {
  int index = binding_detail::FindEnumStringIndexImpl(
      aString.BeginReading(), aString.Length(),
      binding_detail::EnumStrings<Enum>::Values);
  return index >= 0 ? Some(static_cast<Enum>(index)) : Nothing();
}

template <typename Enum>
inline constexpr const nsLiteralCString& GetEnumString(Enum stringId) {
  MOZ_RELEASE_ASSERT(static_cast<size_t>(stringId) <
                     std::size(binding_detail::EnumStrings<Enum>::Values));
  return binding_detail::EnumStrings<Enum>::Values[static_cast<size_t>(
      stringId)];
}

template <typename Enum>
constexpr mozilla::detail::EnumeratedRange<Enum> MakeWebIDLEnumeratedRange() {
  return MakeInclusiveEnumeratedRange(ContiguousEnumValues<Enum>::min,
                                      ContiguousEnumValues<Enum>::max);
}

inline nsWrapperCache* GetWrapperCache(const ParentObject& aParentObject) {
  return aParentObject.mWrapperCache;
}

template <class T>
inline T* GetParentPointer(T* aObject) {
  return aObject;
}

inline nsISupports* GetParentPointer(const ParentObject& aObject) {
  return aObject.mObject;
}

template <typename T>
inline mozilla::dom::ReflectionScope GetReflectionScope(T* aParentObject) {
  return mozilla::dom::ReflectionScope::Content;
}

inline mozilla::dom::ReflectionScope GetReflectionScope(
    const ParentObject& aParentObject) {
  return aParentObject.mReflectionScope;
}

template <class T>
inline void ClearWrapper(T* p, nsWrapperCache* cache, JSObject* obj) {
  MOZ_ASSERT(cache->GetWrapperMaybeDead() == obj ||
             (js::RuntimeIsBeingDestroyed() && !cache->GetWrapperMaybeDead()));
  cache->ClearWrapper(obj);
}

template <class T>
inline void ClearWrapper(T* p, void*, JSObject* obj) {
  // QueryInterface to nsWrapperCache can't GC, we hope.
  JS::AutoSuppressGCAnalysis nogc;

  nsWrapperCache* cache;
  CallQueryInterface(p, &cache);
  ClearWrapper(p, cache, obj);
}

template <class T>
inline void UpdateWrapper(T* p, nsWrapperCache* cache, JSObject* obj,
                          const JSObject* old) {
  JS::AutoAssertGCCallback inCallback;
  cache->UpdateWrapper(obj, old);
}

template <class T>
inline void UpdateWrapper(T* p, void*, JSObject* obj, const JSObject* old) {
  JS::AutoAssertGCCallback inCallback;
  nsWrapperCache* cache;
  CallQueryInterface(p, &cache);
  UpdateWrapper(p, cache, obj, old);
}

// Attempt to preserve the wrapper, if any, for a Paris DOM bindings object.
// Return true if we successfully preserved the wrapper, or there is no wrapper
// to preserve. In the latter case we don't need to preserve the wrapper,
// because the object can only be obtained by JS once, or they cannot be
// meaningfully owned from the native side.
//
// This operation will return false only for non-nsISupports cycle-collected
// objects, because we cannot determine if they are wrappercached or not.
bool TryPreserveWrapper(JS::Handle<JSObject*> obj);

bool HasReleasedWrapper(JS::Handle<JSObject*> obj);

// Can only be called with a DOM JSClass.
bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
                                  uint32_t depth);

// Only set allowNativeWrapper to false if you really know you need it; if in
// doubt use true. Setting it to false disables security wrappers.
bool XPCOMObjectToJsval(JSContext* cx, JS::Handle<JSObject*> scope,
                        xpcObjectHelper& helper, const nsIID* iid,
                        bool allowNativeWrapper,
                        JS::MutableHandle<JS::Value> rval);

// Special-cased wrapping for variants
bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
                    JS::MutableHandle<JS::Value> aRetval);

// Wrap an object "p" which is not using WebIDL bindings yet.  This _will_
// actually work on WebIDL binding objects that are wrappercached, but will be
// much slower than GetOrCreateDOMReflector.  "cache" must either be null or be
// the nsWrapperCache for "p".
template <class T>
inline bool WrapObject(JSContext* cx, T* p, nsWrapperCache* cache,
                       const nsIID* iid, JS::MutableHandle<JS::Value> rval) {
  if (xpc_FastGetCachedWrapper(cx, cache, rval)) return true;
  xpcObjectHelper helper(ToSupports(p), cache);
  JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
  return XPCOMObjectToJsval(cx, scope, helper, iid, true, rval);
}

// A specialization of the above for nsIVariant, because that needs to
// do something different.
template <>
inline bool WrapObject<nsIVariant>(JSContext* cx, nsIVariant* p,
                                   nsWrapperCache* cache, const nsIID* iid,
                                   JS::MutableHandle<JS::Value> rval) {
  MOZ_ASSERT(iid);
  MOZ_ASSERT(iid->Equals(NS_GET_IID(nsIVariant)));
  return VariantToJsval(cx, p, rval);
}

// Wrap an object "p" which is not using WebIDL bindings yet.  Just like the
// variant that takes an nsWrapperCache above, but will try to auto-derive the
// nsWrapperCache* from "p".
template <class T>
inline bool WrapObject(JSContext* cx, T* p, const nsIID* iid,
                       JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p, GetWrapperCache(p), iid, rval);
}

// Just like the WrapObject above, but without requiring you to pick which
// interface you're wrapping as.  This should only be used for objects that have
// classinfo, for which it doesn't matter what IID is used to wrap.
template <class T>
inline bool WrapObject(JSContext* cx, T* p, JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p, nullptr, rval);
}

// Helper to make it possible to wrap directly out of an nsCOMPtr
template <class T>
inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p, const nsIID* iid,
                       JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p.get(), iid, rval);
}

// Helper to make it possible to wrap directly out of an nsCOMPtr
template <class T>
inline bool WrapObject(JSContext* cx, const nsCOMPtr<T>& p,
                       JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p, nullptr, rval);
}

// Helper to make it possible to wrap directly out of an nsRefPtr
template <class T>
inline bool WrapObject(JSContext* cx, const RefPtr<T>& p, const nsIID* iid,
                       JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p.get(), iid, rval);
}

// Helper to make it possible to wrap directly out of an nsRefPtr
template <class T>
inline bool WrapObject(JSContext* cx, const RefPtr<T>& p,
                       JS::MutableHandle<JS::Value> rval) {
  return WrapObject(cx, p, nullptr, rval);
}

// Specialization to make it easy to use WrapObject in codegen.
template <>
inline bool WrapObject<JSObject>(JSContext* cx, JSObject* p,
                                 JS::MutableHandle<JS::Value> rval) {
  rval.set(JS::ObjectOrNullValue(p));
  return true;
}

inline bool WrapObject(JSContext* cx, JSObject& p,
                       JS::MutableHandle<JS::Value> rval) {
  rval.set(JS::ObjectValue(p));
  return true;
}

bool WrapObject(JSContext* cx, const WindowProxyHolder& p,
                JS::MutableHandle<JS::Value> rval);

// Given an object "p" that inherits from nsISupports, wrap it and return the
// result.  Null is returned on wrapping failure.  This is somewhat similar to
// WrapObject() above, but does NOT allow Xrays around the result, since we
// don't want those for our parent object.
template <typename T>
static inline JSObject* WrapNativeISupports(JSContext* cx, T* p,
                                            nsWrapperCache* cache) {
  JS::Rooted<JSObject*> retval(cx);
  {
    xpcObjectHelper helper(ToSupports(p), cache);
    JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
    JS::Rooted<JS::Value> v(cx);
    retval = XPCOMObjectToJsval(cx, scope, helper, nullptr, false, &v)
                 ? v.toObjectOrNull()
                 : nullptr;
  }
  return retval;
}

--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=98 G=94

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.