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 149 kB image not shown  

Quelle  BindingUtils.cpp   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/. */


#include "BindingUtils.h"

#include <algorithm>
#include <cstdint>
#include <stdarg.h>

#include "mozilla/Assertions.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Encoding.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/Preferences.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Unused.h"
#include "mozilla/UseCounter.h"

#include "AccessCheck.h"
#include "js/CallAndConstruct.h"  // JS::Call, JS::IsCallable
#include "js/experimental/JitInfo.h"  // JSJit{Getter,Setter,Method}CallArgs, JSJit{Getter,Setter}Op, JSJitInfo
#include "js/friend/StackLimits.h"  // js::AutoCheckRecursionLimit
#include "js/Id.h"
#include "js/JSON.h"
#include "js/MapAndSet.h"
#include "js/Object.h"  // JS::GetClass, JS::GetCompartment, JS::GetReservedSlot, JS::SetReservedSlot
#include "js/PropertyAndElement.h"  // JS_AlreadyHasOwnPropertyById, JS_DefineFunction, JS_DefineFunctionById, JS_DefineFunctions, JS_DefineProperties, JS_DefineProperty, JS_DefinePropertyById, JS_ForwardGetPropertyTo, JS_GetProperty, JS_HasProperty, JS_HasPropertyById
#include "js/StableStringChars.h"
#include "js/String.h"  // JS::GetStringLength, JS::MaxStringLength, JS::StringHasLatin1Chars
#include "js/Symbol.h"
#include "jsfriendapi.h"
#include "nsContentCreatorFunctions.h"
#include "nsContentUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsHTMLTags.h"
#include "nsIDOMGlobalPropertyInitializer.h"
#include "nsINode.h"
#include "nsIOService.h"
#include "nsIPrincipal.h"
#include "nsIXPConnect.h"
#include "nsUTF8Utils.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WrapperFactory.h"
#include "xpcprivate.h"
#include "XrayWrapper.h"
#include "nsPrintfCString.h"
#include "mozilla/Sprintf.h"
#include "nsReadableUtils.h"
#include "nsWrapperCacheInlines.h"

#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/CustomElementRegistry.h"
#include "mozilla/dom/DeprecationReportBody.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/Exceptions.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLObjectElementBinding.h"
#include "mozilla/dom/HTMLEmbedElement.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/HTMLEmbedElementBinding.h"
#include "mozilla/dom/MaybeCrossOriginObject.h"
#include "mozilla/dom/ObservableArrayProxyHandler.h"
#include "mozilla/dom/ReportingUtils.h"
#include "mozilla/dom/XULElementBinding.h"
#include "mozilla/dom/XULFrameElementBinding.h"
#include "mozilla/dom/XULMenuElementBinding.h"
#include "mozilla/dom/XULPopupElementBinding.h"
#include "mozilla/dom/XULResizerElementBinding.h"
#include "mozilla/dom/XULTextElementBinding.h"
#include "mozilla/dom/XULTreeElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/WebIDLGlobalNameHash.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/XrayExpandoClass.h"
#include "mozilla/dom/WindowProxyHolder.h"
#include "ipc/ErrorIPCUtils.h"
#include "ipc/IPCMessageUtilsSpecializations.h"
#include "mozilla/dom/DocGroup.h"
#include "nsXULElement.h"

namespace mozilla {
namespace dom {

// Forward declare GetConstructorObjectHandle methods.
#define HTML_TAG(_tag, _classname, _interfacename)                \
  namespace HTML##_interfacename##Element_Binding {               \
    JS::Handle<JSObject*> GetConstructorObjectHandle(JSContext*); \
  }
#define HTML_OTHER(_tag)
#include "nsHTMLTagList.h"
#undef HTML_TAG
#undef HTML_OTHER

using constructorGetterCallback = JS::Handle<JSObject*> (*)(JSContext*);

// Mapping of html tag and GetConstructorObjectHandle methods.
#define HTML_TAG(_tag, _classname, _interfacename) \
  HTML##_interfacename##Element_Binding::GetConstructorObjectHandle,
#define HTML_OTHER(_tag) nullptr,
// We use eHTMLTag_foo (where foo is the tag) which is defined in nsHTMLTags.h
// to index into this array.
static const constructorGetterCallback sConstructorGetterCallback[] = {
    HTMLUnknownElement_Binding::GetConstructorObjectHandle,
#include "nsHTMLTagList.h"
#undef HTML_TAG
#undef HTML_OTHER
};

static const JSErrorFormatString ErrorFormatString[] = {
#define MSG_DEF(_name, _argc, _has_context, _exn, _str) \
  {#_name, _str, _argc, _exn},
#include "mozilla/dom/Errors.msg"
#undef MSG_DEF
};

#define MSG_DEF(_name, _argc, _has_context, _exn, _str) \
  static_assert(                                        \
      (_argc) < JS::MaxNumErrorArguments, #_name        \
      " must only have as many error arguments as the JS engine can support");
#include "mozilla/dom/Errors.msg"
#undef MSG_DEF

static const JSErrorFormatString* GetErrorMessage(void* aUserRef,
                                                  const unsigned aErrorNumber) {
  MOZ_ASSERT(aErrorNumber < std::size(ErrorFormatString));
  return &ErrorFormatString[aErrorNumber];
}

uint16_t GetErrorArgCount(const ErrNum aErrorNumber) {
  return GetErrorMessage(nullptr, aErrorNumber)->argCount;
}

// aErrorNumber needs to be unsigned, not an ErrNum, because the latter makes
// va_start have undefined behavior, and we do not want undefined behavior.
void binding_detail::ThrowErrorMessage(JSContext* aCx,
                                       const unsigned aErrorNumber, ...) {
  va_list ap;
  va_start(ap, aErrorNumber);

  if (!ErrorFormatHasContext[aErrorNumber]) {
    JS_ReportErrorNumberUTF8VA(aCx, GetErrorMessage, nullptr, aErrorNumber, ap);
    va_end(ap);
    return;
  }

  // Our first arg is the context arg.  We want to replace nullptr with empty
  // string, leave empty string alone, and for anything else append ": " to the
  // end.  See also the behavior of
  // TErrorResult::SetPendingExceptionWithMessage, which this is mirroring for
  // exceptions that are thrown directly, not via an ErrorResult.
  const char* args[JS::MaxNumErrorArguments + 1];
  size_t argCount = GetErrorArgCount(static_cast<ErrNum>(aErrorNumber));
  MOZ_ASSERT(argCount > 0, "We have a context arg!");
  nsAutoCString firstArg;

  for (size_t i = 0; i < argCount; ++i) {
    args[i] = va_arg(ap, const char*);
    if (i == 0) {
      if (args[0] && *args[0]) {
        firstArg.Append(args[0]);
        firstArg.AppendLiteral(": ");
      }
      args[0] = firstArg.get();
    }
  }

  JS_ReportErrorNumberUTF8Array(aCx, GetErrorMessage, nullptr, aErrorNumber,
                                args);
  va_end(ap);
}

static bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
                             bool aSecurityError, const char* aInterfaceName) {
  NS_ConvertASCIItoUTF16 ifaceName(aInterfaceName);
  // This should only be called for DOM methods/getters/setters, which
  // are JSNative-backed functions, so we can assume that
  // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
  // non-null and that JS_GetStringCharsZ returns non-null.
  JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
  MOZ_ASSERT(func);
  JS::Rooted<JSString*> funcName(aCx);
  if (!JS_GetFunctionDisplayId(aCx, func, &funcName)) {
    return false;
  }
  MOZ_ASSERT(funcName);
  nsAutoJSString funcNameStr;
  if (!funcNameStr.init(aCx, funcName)) {
    return false;
  }
  if (aSecurityError) {
    return Throw(aCx, NS_ERROR_DOM_SECURITY_ERR,
                 nsPrintfCString("Permission to call '%s' denied.",
                                 NS_ConvertUTF16toUTF8(funcNameStr).get()));
  }

  const ErrNum errorNumber = MSG_METHOD_THIS_DOES_NOT_IMPLEMENT_INTERFACE;
  MOZ_RELEASE_ASSERT(GetErrorArgCount(errorNumber) == 2);
  JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
                         static_cast<unsigned>(errorNumber),
                         static_cast<const char16_t*>(funcNameStr.get()),
                         static_cast<const char16_t*>(ifaceName.get()));
  return false;
}

bool ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
                      bool aSecurityError, prototypes::ID aProtoId) {
  return ThrowInvalidThis(aCx, aArgs, aSecurityError,
                          NamesOfInterfacesWithProtos(aProtoId));
}

bool ThrowNoSetterArg(JSContext* aCx, const JS::CallArgs& aArgs,
                      prototypes::ID aProtoId) {
  nsPrintfCString errorMessage("%s attribute setter",
                               NamesOfInterfacesWithProtos(aProtoId));
  return aArgs.requireAtLeast(aCx, errorMessage.get(), 1);
}

}  // namespace dom

namespace binding_danger {

template <typename CleanupPolicy>
struct TErrorResult<CleanupPolicy>::Message {
  Message() : mErrorNumber(dom::Err_Limit) {
    MOZ_COUNT_CTOR(TErrorResult::Message);
  }
  ~Message() { MOZ_COUNT_DTOR(TErrorResult::Message); }

  // UTF-8 strings (probably ASCII in most cases) in mArgs.
  nsTArray<nsCString> mArgs;
  dom::ErrNum mErrorNumber;

  bool HasCorrectNumberOfArguments() {
    return GetErrorArgCount(mErrorNumber) == mArgs.Length();
  }

  bool operator==(const TErrorResult<CleanupPolicy>::Message& aRight) const {
    return mErrorNumber == aRight.mErrorNumber && mArgs == aRight.mArgs;
  }
};

template <typename CleanupPolicy>
nsTArray<nsCString>& TErrorResult<CleanupPolicy>::CreateErrorMessageHelper(
    const dom::ErrNum errorNumber, nsresult errorType) {
  AssertInOwningThread();
  mResult = errorType;

  Message* message = InitMessage(new Message());
  message->mErrorNumber = errorNumber;
  return message->mArgs;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SerializeMessage(
    IPC::MessageWriter* aWriter) const {
  using namespace IPC;
  AssertInOwningThread();
  MOZ_ASSERT(mUnionState == HasMessage);
  MOZ_ASSERT(mExtra.mMessage);
  WriteParam(aWriter, mExtra.mMessage->mArgs);
  WriteParam(aWriter, mExtra.mMessage->mErrorNumber);
}

template <typename CleanupPolicy>
bool TErrorResult<CleanupPolicy>::DeserializeMessage(
    IPC::MessageReader* aReader) {
  using namespace IPC;
  AssertInOwningThread();
  auto readMessage = MakeUnique<Message>();
  if (!ReadParam(aReader, &readMessage->mArgs) ||
      !ReadParam(aReader, &readMessage->mErrorNumber)) {
    return false;
  }
  if (!readMessage->HasCorrectNumberOfArguments()) {
    return false;
  }

  MOZ_ASSERT(mUnionState == HasNothing);
  InitMessage(readMessage.release());
#ifdef DEBUG
  mUnionState = HasMessage;
#endif  // DEBUG
  return true;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingExceptionWithMessage(
    JSContext* aCx, const char* context) {
  AssertInOwningThread();
  MOZ_ASSERT(mUnionState == HasMessage);
  MOZ_ASSERT(mExtra.mMessage,
             "SetPendingExceptionWithMessage() can be called only once");

  Message* message = mExtra.mMessage;
  MOZ_RELEASE_ASSERT(message->HasCorrectNumberOfArguments());
  if (dom::ErrorFormatHasContext[message->mErrorNumber]) {
    MOZ_ASSERT(!message->mArgs.IsEmpty(), "How could we have no args here?");
    MOZ_ASSERT(message->mArgs[0].IsEmpty(), "Context should not be set yet!");
    if (context) {
      // Prepend our context and ": "; see API documentation.
      message->mArgs[0].AssignASCII(context);
      message->mArgs[0].AppendLiteral(": ");
    }
  }
  const uint32_t argCount = message->mArgs.Length();
  const char* args[JS::MaxNumErrorArguments + 1];
  for (uint32_t i = 0; i < argCount; ++i) {
    args[i] = message->mArgs.ElementAt(i).get();
  }
  args[argCount] = nullptr;

  JS_ReportErrorNumberUTF8Array(aCx, dom::GetErrorMessage, nullptr,
                                static_cast<unsigned>(message->mErrorNumber),
                                argCount > 0 ? args : nullptr);

  ClearMessage();
  mResult = NS_OK;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::ClearMessage() {
  AssertInOwningThread();
  MOZ_ASSERT(IsErrorWithMessage());
  MOZ_ASSERT(mUnionState == HasMessage);
  delete mExtra.mMessage;
  mExtra.mMessage = nullptr;
#ifdef DEBUG
  mUnionState = HasNothing;
#endif  // DEBUG
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::ThrowJSException(JSContext* cx,
                                                   JS::Handle<JS::Value> exn) {
  AssertInOwningThread();
  MOZ_ASSERT(mMightHaveUnreportedJSException,
             "Why didn't you tell us you planned to throw a JS exception?");

  ClearUnionData();

  // Make sure mExtra.mJSException is initialized _before_ we try to root it.
  // But don't set it to exn yet, because we don't want to do that until after
  // we root.
  JS::Value& exc = InitJSException();
  if (!js::AddRawValueRoot(cx, &exc, "TErrorResult::mExtra::mJSException")) {
    // Don't use NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION, because that
    // indicates we have in fact rooted mExtra.mJSException.
    mResult = NS_ERROR_OUT_OF_MEMORY;
  } else {
    exc = exn;
    mResult = NS_ERROR_INTERNAL_ERRORRESULT_JS_EXCEPTION;
#ifdef DEBUG
    mUnionState = HasJSException;
#endif  // DEBUG
  }
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingJSException(JSContext* cx) {
  AssertInOwningThread();
  MOZ_ASSERT(!mMightHaveUnreportedJSException,
             "Why didn't you tell us you planned to handle JS exceptions?");
  MOZ_ASSERT(mUnionState == HasJSException);

  JS::Rooted<JS::Value> exception(cx, mExtra.mJSException);
  if (JS_WrapValue(cx, &exception)) {
    JS_SetPendingException(cx, exception);
  }
  mExtra.mJSException = exception;
  // If JS_WrapValue failed, not much we can do about it...  No matter
  // what, go ahead and unroot mExtra.mJSException.
  js::RemoveRawValueRoot(cx, &mExtra.mJSException);

  mResult = NS_OK;
#ifdef DEBUG
  mUnionState = HasNothing;
#endif  // DEBUG
}

template <typename CleanupPolicy>
struct TErrorResult<CleanupPolicy>::DOMExceptionInfo {
  DOMExceptionInfo(nsresult rv, const nsACString& message)
      : mMessage(message), mRv(rv) {}

  nsCString mMessage;
  nsresult mRv;

  bool operator==(
      const TErrorResult<CleanupPolicy>::DOMExceptionInfo& aRight) const {
    return mRv == aRight.mRv && mMessage == aRight.mMessage;
  }
};

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SerializeDOMExceptionInfo(
    IPC::MessageWriter* aWriter) const {
  using namespace IPC;
  AssertInOwningThread();
  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
  MOZ_ASSERT(mExtra.mDOMExceptionInfo);
  WriteParam(aWriter, mExtra.mDOMExceptionInfo->mMessage);
  WriteParam(aWriter, mExtra.mDOMExceptionInfo->mRv);
}

template <typename CleanupPolicy>
bool TErrorResult<CleanupPolicy>::DeserializeDOMExceptionInfo(
    IPC::MessageReader* aReader) {
  using namespace IPC;
  AssertInOwningThread();
  nsCString message;
  nsresult rv;
  if (!ReadParam(aReader, &message) || !ReadParam(aReader, &rv)) {
    return false;
  }

  MOZ_ASSERT(mUnionState == HasNothing);
  MOZ_ASSERT(IsDOMException());
  InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
#ifdef DEBUG
  mUnionState = HasDOMExceptionInfo;
#endif  // DEBUG
  return true;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::ThrowDOMException(nsresult rv,
                                                    const nsACString& message) {
  AssertInOwningThread();
  ClearUnionData();

  mResult = NS_ERROR_INTERNAL_ERRORRESULT_DOMEXCEPTION;
  InitDOMExceptionInfo(new DOMExceptionInfo(rv, message));
#ifdef DEBUG
  mUnionState = HasDOMExceptionInfo;
#endif
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingDOMException(JSContext* cx,
                                                         const char* context) {
  AssertInOwningThread();
  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
  MOZ_ASSERT(mExtra.mDOMExceptionInfo,
             "SetPendingDOMException() can be called only once");

  if (context && !mExtra.mDOMExceptionInfo->mMessage.IsEmpty()) {
    // Prepend our context and ": "; see API documentation.
    nsAutoCString prefix(context);
    prefix.AppendLiteral(": ");
    mExtra.mDOMExceptionInfo->mMessage.Insert(prefix, 0);
  }

  dom::Throw(cx, mExtra.mDOMExceptionInfo->mRv,
             mExtra.mDOMExceptionInfo->mMessage);

  ClearDOMExceptionInfo();
  mResult = NS_OK;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::ClearDOMExceptionInfo() {
  AssertInOwningThread();
  MOZ_ASSERT(IsDOMException());
  MOZ_ASSERT(mUnionState == HasDOMExceptionInfo);
  delete mExtra.mDOMExceptionInfo;
  mExtra.mDOMExceptionInfo = nullptr;
#ifdef DEBUG
  mUnionState = HasNothing;
#endif  // DEBUG
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::ClearUnionData() {
  AssertInOwningThread();
  if (IsJSException()) {
    JSContext* cx = dom::danger::GetJSContext();
    MOZ_ASSERT(cx);
    mExtra.mJSException.setUndefined();
    js::RemoveRawValueRoot(cx, &mExtra.mJSException);
#ifdef DEBUG
    mUnionState = HasNothing;
#endif  // DEBUG
  } else if (IsErrorWithMessage()) {
    ClearMessage();
  } else if (IsDOMException()) {
    ClearDOMExceptionInfo();
  }
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingGenericErrorException(
    JSContext* cx) {
  AssertInOwningThread();
  MOZ_ASSERT(!IsErrorWithMessage());
  MOZ_ASSERT(!IsJSException());
  MOZ_ASSERT(!IsDOMException());
  dom::Throw(cx, ErrorCode());
  mResult = NS_OK;
}

template <typename CleanupPolicy>
TErrorResult<CleanupPolicy>& TErrorResult<CleanupPolicy>::operator=(
    TErrorResult<CleanupPolicy>&& aRHS) {
  AssertInOwningThread();
  aRHS.AssertInOwningThread();
  // Clear out any union members we may have right now, before we
  // start writing to it.
  ClearUnionData();

#ifdef DEBUG
  mMightHaveUnreportedJSException = aRHS.mMightHaveUnreportedJSException;
  aRHS.mMightHaveUnreportedJSException = false;
#endif
  if (aRHS.IsErrorWithMessage()) {
    InitMessage(aRHS.mExtra.mMessage);
    aRHS.mExtra.mMessage = nullptr;
  } else if (aRHS.IsJSException()) {
    JSContext* cx = dom::danger::GetJSContext();
    MOZ_ASSERT(cx);
    JS::Value& exn = InitJSException();
    if (!js::AddRawValueRoot(cx, &exn, "TErrorResult::mExtra::mJSException")) {
      MOZ_CRASH("Could not root mExtra.mJSException, we're about to OOM");
    }
    mExtra.mJSException = aRHS.mExtra.mJSException;
    aRHS.mExtra.mJSException.setUndefined();
    js::RemoveRawValueRoot(cx, &aRHS.mExtra.mJSException);
  } else if (aRHS.IsDOMException()) {
    InitDOMExceptionInfo(aRHS.mExtra.mDOMExceptionInfo);
    aRHS.mExtra.mDOMExceptionInfo = nullptr;
  } else {
    // Null out the union on both sides for hygiene purposes.  This is purely
    // precautionary, so InitMessage/placement-new is unnecessary.
    mExtra.mMessage = aRHS.mExtra.mMessage = nullptr;
  }

#ifdef DEBUG
  mUnionState = aRHS.mUnionState;
  aRHS.mUnionState = HasNothing;
#endif  // DEBUG

  // Note: It's important to do this last, since this affects the condition
  // checks above!
  mResult = aRHS.mResult;
  aRHS.mResult = NS_OK;
  return *this;
}

template <typename CleanupPolicy>
bool TErrorResult<CleanupPolicy>::operator==(const ErrorResult& aRight) const {
  auto right = reinterpret_cast<const TErrorResult<CleanupPolicy>*>(&aRight);

  if (mResult != right->mResult) {
    return false;
  }

  if (IsJSException()) {
    // js exceptions are always non-equal
    return false;
  }

  if (IsErrorWithMessage()) {
    return *mExtra.mMessage == *right->mExtra.mMessage;
  }

  if (IsDOMException()) {
    return *mExtra.mDOMExceptionInfo == *right->mExtra.mDOMExceptionInfo;
  }

  return true;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::CloneTo(TErrorResult& aRv) const {
  AssertInOwningThread();
  aRv.AssertInOwningThread();
  aRv.ClearUnionData();
  aRv.mResult = mResult;
#ifdef DEBUG
  aRv.mMightHaveUnreportedJSException = mMightHaveUnreportedJSException;
#endif

  if (IsErrorWithMessage()) {
#ifdef DEBUG
    aRv.mUnionState = HasMessage;
#endif
    Message* message = aRv.InitMessage(new Message());
    message->mArgs = mExtra.mMessage->mArgs.Clone();
    message->mErrorNumber = mExtra.mMessage->mErrorNumber;
  } else if (IsDOMException()) {
#ifdef DEBUG
    aRv.mUnionState = HasDOMExceptionInfo;
#endif
    auto* exnInfo = new DOMExceptionInfo(mExtra.mDOMExceptionInfo->mRv,
                                         mExtra.mDOMExceptionInfo->mMessage);
    aRv.InitDOMExceptionInfo(exnInfo);
  } else if (IsJSException()) {
#ifdef DEBUG
    aRv.mUnionState = HasJSException;
#endif
    JSContext* cx = dom::danger::GetJSContext();
    JS::Rooted<JS::Value> exception(cx, mExtra.mJSException);
    aRv.ThrowJSException(cx, exception);
  }
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SuppressException() {
  AssertInOwningThread();
  WouldReportJSException();
  ClearUnionData();
  // We don't use AssignErrorCode, because we want to override existing error
  // states, which AssignErrorCode is not allowed to do.
  mResult = NS_OK;
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::SetPendingException(JSContext* cx,
                                                      const char* context) {
  AssertInOwningThread();
  if (IsUncatchableException()) {
    // Note: ReportUncatchableException will clear any existing exception on cx.
    JS::ReportUncatchableException(cx);
    mResult = NS_OK;
    return;
  }
  if (IsJSContextException()) {
    // Whatever we need to throw is on the JSContext already.
    MOZ_ASSERT(JS_IsExceptionPending(cx));
    mResult = NS_OK;
    return;
  }
  if (IsErrorWithMessage()) {
    SetPendingExceptionWithMessage(cx, context);
    return;
  }
  if (IsJSException()) {
    SetPendingJSException(cx);
    return;
  }
  if (IsDOMException()) {
    SetPendingDOMException(cx, context);
    return;
  }
  SetPendingGenericErrorException(cx);
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::StealExceptionFromJSContext(JSContext* cx) {
  AssertInOwningThread();
  MOZ_ASSERT(mMightHaveUnreportedJSException,
             "Why didn't you tell us you planned to throw a JS exception?");

  JS::Rooted<JS::Value> exn(cx);
  if (!JS_GetPendingException(cx, &exn)) {
    ThrowUncatchableException();
    return;
  }

  ThrowJSException(cx, exn);
  JS_ClearPendingException(cx);
}

template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::NoteJSContextException(JSContext* aCx) {
  AssertInOwningThread();
  if (JS_IsExceptionPending(aCx)) {
    mResult = NS_ERROR_INTERNAL_ERRORRESULT_EXCEPTION_ON_JSCONTEXT;
  } else {
    mResult = NS_ERROR_UNCATCHABLE_EXCEPTION;
  }
}

/* static */
template <typename CleanupPolicy>
void TErrorResult<CleanupPolicy>::EnsureUTF8Validity(nsCString& aValue,
                                                     size_t aValidUpTo) {
  nsCString valid;
  if (NS_SUCCEEDED(UTF_8_ENCODING->DecodeWithoutBOMHandling(aValue, valid,
                                                            aValidUpTo))) {
    aValue = valid;
  } else {
    aValue.SetLength(aValidUpTo);
  }
}

template class TErrorResult<JustAssertCleanupPolicy>;
template class TErrorResult<AssertAndSuppressCleanupPolicy>;
template class TErrorResult<JustSuppressCleanupPolicy>;
template class TErrorResult<ThreadSafeJustSuppressCleanupPolicy>;

}  // namespace binding_danger

namespace dom {

bool DefineConstants(JSContext* cx, JS::Handle<JSObject*> obj,
                     const ConstantSpec* cs) {
  JS::Rooted<JS::Value> value(cx);
  for (; cs->name; ++cs) {
    value = cs->value;
    bool ok = JS_DefineProperty(
        cx, obj, cs->name, value,
        JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT);
    if (!ok) {
      return false;
    }
  }
  return true;
}

static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
                          const JSFunctionSpec* spec) {
  return JS_DefineFunctions(cx, obj, spec);
}
static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
                          const JSPropertySpec* spec) {
  return JS_DefineProperties(cx, obj, spec);
}
static inline bool Define(JSContext* cx, JS::Handle<JSObject*> obj,
                          const ConstantSpec* spec) {
  return DefineConstants(cx, obj, spec);
}

template <typename T>
bool DefinePrefable(JSContext* cx, JS::Handle<JSObject*> obj,
                    const Prefable<T>* props) {
  MOZ_ASSERT(props);
  MOZ_ASSERT(props->specs);
  do {
    // Define if enabled
    if (props->isEnabled(cx, obj)) {
      if (!Define(cx, obj, props->specs)) {
        return false;
      }
    }
  } while ((++props)->specs);
  return true;
}

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

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

bool InterfaceObjectJSNative(JSContext* cx, unsigned argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  return NativeHolderFromInterfaceObject(&args.callee())->mNative(cx, argc, vp);
}

bool LegacyFactoryFunctionJSNative(JSContext* cx, unsigned argc,
                                   JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  return NativeHolderFromLegacyFactoryFunction(&args.callee())
      ->mNative(cx, argc, vp);
}

// This creates a JSFunction and sets its length and name properties in the
// order that ECMAScript's CreateBuiltinFunction does.
static JSObject* CreateBuiltinFunctionForConstructor(
    JSContext* aCx, JSNative aNative, size_t aNativeReservedSlot,
    void* aNativeReserved, unsigned int aNargs, jsid aName,
    JS::Handle<JSObject*> aProto) {
  JSFunction* fun = js::NewFunctionByIdWithReservedAndProto(
      aCx, aNative, aProto, aNargs, JSFUN_CONSTRUCTOR, aName);
  if (!fun) {
    return nullptr;
  }

  JS::Rooted<JSObject*> constructor(aCx, JS_GetFunctionObject(fun));
  js::SetFunctionNativeReserved(constructor, aNativeReservedSlot,
                                JS::PrivateValue(aNativeReserved));

  // Eagerly force creation of the .length and .name properties, because
  // SpiderMonkey creates them lazily (see
  // https://bugzilla.mozilla.org/show_bug.cgi?id=1629803).
  bool unused;
  if (!JS_HasProperty(aCx, constructor, "length", &unused) ||
      !JS_HasProperty(aCx, constructor, "name", &unused)) {
    return nullptr;
  }

  return constructor;
}

static bool DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global,
                              JS::Handle<jsid> name,
                              JS::Handle<JSObject*> constructor) {
  bool alreadyDefined;
  if (!JS_AlreadyHasOwnPropertyById(cx, global, name, &alreadyDefined)) {
    return false;
  }

  // This is Enumerable: False per spec.
  return alreadyDefined ||
         JS_DefinePropertyById(cx, global, name, constructor, JSPROP_RESOLVING);
}

static bool DefineConstructor(JSContext* cx, JS::Handle<JSObject*> global,
                              const char* name,
                              JS::Handle<JSObject*> constructor) {
  JSString* nameStr = JS_AtomizeString(cx, name);
  if (!nameStr) {
    return false;
  }
  JS::Rooted<JS::PropertyKey> nameKey(cx, JS::PropertyKey::NonIntAtom(nameStr));
  return DefineConstructor(cx, global, nameKey, constructor);
}

static bool DefineToStringTag(JSContext* cx, JS::Handle<JSObject*> obj,
                              JS::Handle<JSString*> class_name) {
  JS::Rooted<jsid> toStringTagId(
      cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::toStringTag));
  return JS_DefinePropertyById(cx, obj, toStringTagId, class_name,
                               JSPROP_READONLY);
}

static bool InterfaceIsInstance(JSContext* cx, unsigned argc, JS::Value* vp) {
  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
  // If the thing we were passed is not an object, return false like
  // OrdinaryHasInstance does.
  if (!args.get(0).isObject()) {
    args.rval().setBoolean(false);
    return true;
  }

  // If "this" is not an object, likewise return false (again, like
  // OrdinaryHasInstance).
  if (!args.thisv().isObject()) {
    args.rval().setBoolean(false);
    return true;
  }

  // If "this" is not an interface object, likewise return false (again, like
  // OrdinaryHasInstance). But note that we should CheckedUnwrapStatic here,
  // because otherwise we won't get the right answers.
  // The static version is OK, because we're looking for interface objects,
  // which are not cross-origin objects.
  JS::Rooted<JSObject*> thisObj(
      cx, js::CheckedUnwrapStatic(&args.thisv().toObject()));
  if (!thisObj) {
    // Just fall back on the normal thing, in case it still happens to work.
    args.rval().setBoolean(false);
    return true;
  }

  if (!IsInterfaceObject(thisObj)) {
    args.rval().setBoolean(false);
    return true;
  }

  const DOMInterfaceInfo* interfaceInfo = InterfaceInfoFromObject(thisObj);

  // If "this" is a constructor for an interface without a prototype, just fall
  // back.
  if (interfaceInfo->mPrototypeID == prototypes::id::_ID_Count) {
    args.rval().setBoolean(false);
    return true;
  }

  JS::Rooted<JSObject*> instance(cx, &args[0].toObject());
  const DOMJSClass* domClass = GetDOMClass(
      js::UncheckedUnwrap(instance, /* stopAtWindowProxy = */ false));

  if (domClass && domClass->mInterfaceChain[interfaceInfo->mDepth] ==
                      interfaceInfo->mPrototypeID) {
    args.rval().setBoolean(true);
    return true;
  }

  if (IsRemoteObjectProxy(instance, interfaceInfo->mPrototypeID)) {
    args.rval().setBoolean(true);
    return true;
  }

  args.rval().setBoolean(false);
  return true;
}

bool InitInterfaceOrNamespaceObject(
    JSContext* cx, JS::Handle<JSObject*> obj,
    const NativeProperties* properties,
    const NativeProperties* chromeOnlyProperties, bool isChrome) {
  if (properties) {
    if (properties->HasStaticMethods() &&
        !DefinePrefable(cx, obj, properties->StaticMethods())) {
      return false;
    }

    if (properties->HasStaticAttributes() &&
        !DefinePrefable(cx, obj, properties->StaticAttributes())) {
      return false;
    }

    if (properties->HasConstants() &&
        !DefinePrefable(cx, obj, properties->Constants())) {
      return false;
    }
  }

  if (chromeOnlyProperties && isChrome) {
    if (chromeOnlyProperties->HasStaticMethods() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->StaticMethods())) {
      return false;
    }

    if (chromeOnlyProperties->HasStaticAttributes() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->StaticAttributes())) {
      return false;
    }

    if (chromeOnlyProperties->HasConstants() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
      return false;
    }
  }

  return true;
}

// name must be an atom (or JS::PropertyKey::NonIntAtom will assert).
static JSObject* CreateInterfaceObject(
    JSContext* cx, JS::Handle<JSObject*> global,
    JS::Handle<JSObject*> interfaceProto, const DOMInterfaceInfo* interfaceInfo,
    unsigned ctorNargs,
    const Span<const LegacyFactoryFunction>& legacyFactoryFunctions,
    JS::Handle<JSObject*> proto, const NativeProperties* properties,
    const NativeProperties* chromeOnlyProperties, JS::Handle<JSString*> name,
    bool isChrome, bool defineOnGlobal,
    const charconst* legacyWindowAliases) {
  MOZ_ASSERT(interfaceProto);
  MOZ_ASSERT(interfaceInfo);

  JS::Rooted<jsid> nameId(cx, JS::PropertyKey::NonIntAtom(name));

  JS::Rooted<JSObject*> constructor(
      cx, CreateBuiltinFunctionForConstructor(
              cx, InterfaceObjectJSNative, INTERFACE_OBJECT_INFO_RESERVED_SLOT,
              const_cast<DOMInterfaceInfo*>(interfaceInfo), ctorNargs, nameId,
              interfaceProto));
  if (!constructor) {
    return nullptr;
  }

  if (proto && !JS_LinkConstructorAndPrototype(cx, constructor, proto)) {
    return nullptr;
  }

  if (!InitInterfaceOrNamespaceObject(cx, constructor, properties,
                                      chromeOnlyProperties, isChrome)) {
    return nullptr;
  }

  if (defineOnGlobal && !DefineConstructor(cx, global, nameId, constructor)) {
    return nullptr;
  }

  if (interfaceInfo->wantsInterfaceIsInstance && isChrome &&
      !JS_DefineFunction(cx, constructor, "isInstance", InterfaceIsInstance, 1,
                         // Don't bother making it enumerable
                         0)) {
    return nullptr;
  }

  if (legacyWindowAliases && NS_IsMainThread()) {
    for (; *legacyWindowAliases; ++legacyWindowAliases) {
      if (!DefineConstructor(cx, global, *legacyWindowAliases, constructor)) {
        return nullptr;
      }
    }
  }

  int legacyFactoryFunctionSlot =
      INTERFACE_OBJECT_FIRST_LEGACY_FACTORY_FUNCTION;
  for (const LegacyFactoryFunction& lff : legacyFactoryFunctions) {
    JSString* fname = JS_AtomizeString(cx, lff.mName);
    if (!fname) {
      return nullptr;
    }

    nameId = JS::PropertyKey::NonIntAtom(fname);

    JS::Rooted<JSObject*> legacyFactoryFunction(
        cx, CreateBuiltinFunctionForConstructor(
                cx, LegacyFactoryFunctionJSNative,
                LEGACY_FACTORY_FUNCTION_RESERVED_SLOT,
                const_cast<LegacyFactoryFunction*>(&lff), lff.mNargs, nameId,
                nullptr));
    if (!legacyFactoryFunction ||
        !JS_DefineProperty(cx, legacyFactoryFunction, "prototype", proto,
                           JSPROP_PERMANENT | JSPROP_READONLY) ||
        (defineOnGlobal &&
         !DefineConstructor(cx, global, nameId, legacyFactoryFunction))) {
      return nullptr;
    }
    js::SetFunctionNativeReserved(constructor, legacyFactoryFunctionSlot,
                                  JS::ObjectValue(*legacyFactoryFunction));
    ++legacyFactoryFunctionSlot;
  }

  return constructor;
}

static JSObject* CreateInterfacePrototypeObject(
    JSContext* cx, JS::Handle<JSObject*> global,
    JS::Handle<JSObject*> parentProto, const JSClass* protoClass,
    const NativeProperties* properties,
    const NativeProperties* chromeOnlyProperties,
    const charconst* unscopableNames, JS::Handle<JSString*> name,
    bool isGlobal) {
  JS::Rooted<JSObject*> ourProto(
      cx, JS_NewObjectWithGivenProto(cx, protoClass, parentProto));
  if (!ourProto ||
      // We don't try to define properties on the global's prototype; those
      // properties go on the global itself.
      (!isGlobal &&
       !DefineProperties(cx, ourProto, properties, chromeOnlyProperties))) {
    return nullptr;
  }

  if (unscopableNames) {
    JS::Rooted<JSObject*> unscopableObj(
        cx, JS_NewObjectWithGivenProto(cx, nullptr, nullptr));
    if (!unscopableObj) {
      return nullptr;
    }

    for (; *unscopableNames; ++unscopableNames) {
      if (!JS_DefineProperty(cx, unscopableObj, *unscopableNames,
                             JS::TrueHandleValue, JSPROP_ENUMERATE)) {
        return nullptr;
      }
    }

    JS::Rooted<jsid> unscopableId(
        cx, JS::GetWellKnownSymbolKey(cx, JS::SymbolCode::unscopables));
    // Readonly and non-enumerable to match Array.prototype.
    if (!JS_DefinePropertyById(cx, ourProto, unscopableId, unscopableObj,
                               JSPROP_READONLY)) {
      return nullptr;
    }
  }

  if (!DefineToStringTag(cx, ourProto, name)) {
    return nullptr;
  }

  return ourProto;
}

bool DefineProperties(JSContext* cx, JS::Handle<JSObject*> obj,
                      const NativeProperties* properties,
                      const NativeProperties* chromeOnlyProperties) {
  if (properties) {
    if (properties->HasMethods() &&
        !DefinePrefable(cx, obj, properties->Methods())) {
      return false;
    }

    if (properties->HasAttributes() &&
        !DefinePrefable(cx, obj, properties->Attributes())) {
      return false;
    }

    if (properties->HasConstants() &&
        !DefinePrefable(cx, obj, properties->Constants())) {
      return false;
    }
  }

  if (chromeOnlyProperties) {
    if (chromeOnlyProperties->HasMethods() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->Methods())) {
      return false;
    }

    if (chromeOnlyProperties->HasAttributes() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->Attributes())) {
      return false;
    }

    if (chromeOnlyProperties->HasConstants() &&
        !DefinePrefable(cx, obj, chromeOnlyProperties->Constants())) {
      return false;
    }
  }

  return true;
}

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) {
  MOZ_ASSERT(protoClass || interfaceInfo, "Need at least a class or info!");
  MOZ_ASSERT(
      !((properties &&
         (properties->HasMethods() || properties->HasAttributes())) ||
        (chromeOnlyProperties && (chromeOnlyProperties->HasMethods() ||
                                  chromeOnlyProperties->HasAttributes()))) ||
          protoClass,
      "Methods or properties but no protoClass!");
  MOZ_ASSERT(!((properties && (properties->HasStaticMethods() ||
                               properties->HasStaticAttributes())) ||
               (chromeOnlyProperties &&
                (chromeOnlyProperties->HasStaticMethods() ||
                 chromeOnlyProperties->HasStaticAttributes()))) ||
                 interfaceInfo,
             "Static methods but no info!");
  MOZ_ASSERT(!protoClass == !protoCache,
             "If, and only if, there is an interface prototype object we need "
             "to cache it");
  MOZ_ASSERT(bool(interfaceInfo) == bool(constructorCache),
             "If, and only if, there is an interface object we need to cache "
             "it");
  MOZ_ASSERT(interfaceProto || !interfaceInfo,
             "Must have a interface proto if we plan to create an interface "
             "object");

  bool isChrome = nsContentUtils::ThreadsafeIsSystemCaller(cx);

  JS::Rooted<JSString*> nameStr(cx, JS_AtomizeString(cx, name));
  if (!nameStr) {
    return;
  }

  JS::Rooted<JSObject*> proto(cx);
  if (protoClass) {
    proto = CreateInterfacePrototypeObject(
        cx, global, protoProto, protoClass->ToJSClass(), properties,
        isChrome ? chromeOnlyProperties : nullptr, unscopableNames, nameStr,
        isGlobal);
    if (!proto) {
      return;
    }

    *protoCache = proto;
  } else {
    MOZ_ASSERT(!proto);
  }

  JSObject* interface;
  if (interfaceInfo) {
    interface = CreateInterfaceObject(
        cx, global, interfaceProto, interfaceInfo,
        (isChrome || !isConstructorChromeOnly) ? ctorNargs : 0,
        legacyFactoryFunctions, proto, properties, chromeOnlyProperties,
        nameStr, isChrome, defineOnGlobal, legacyWindowAliases);
    if (!interface) {
      if (protoCache) {
        // If we fail we need to make sure to clear the value of protoCache we
        // set above.
        *protoCache = nullptr;
      }
      return;
    }
    *constructorCache = interface;
  }
}

}  // namespace binding_detail

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) {
  JS::Rooted<JSString*> nameStr(cx, JS_AtomizeString(cx, name));
  if (!nameStr) {
    return;
  }
  JS::Rooted<jsid> nameId(cx, JS::PropertyKey::NonIntAtom(nameStr));

  JS::Rooted<JSObject*> namespaceObj(
      cx, JS_NewObjectWithGivenProto(cx, namespaceClass.ToJSClass(),
                                     namespaceProto));
  if (!namespaceObj) {
    return;
  }

  if (!InitInterfaceOrNamespaceObject(
          cx, namespaceObj, properties, chromeOnlyProperties,
          nsContentUtils::ThreadsafeIsSystemCaller(cx))) {
    return;
  }

  if (defineOnGlobal && !DefineConstructor(cx, global, nameId, namespaceObj)) {
    return;
  }

  if (!DefineToStringTag(cx, namespaceObj, nameStr)) {
    return;
  }

  *namespaceCache = namespaceObj;
}

// Only set aAllowNativeWrapper to false if you really know you need it; if in
// doubt use true. Setting it to false disables security wrappers.
static bool NativeInterface2JSObjectAndThrowIfFailed(
    JSContext* aCx, JS::Handle<JSObject*> aScope,
    JS::MutableHandle<JS::Value> aRetval, xpcObjectHelper& aHelper,
    const nsIID* aIID, bool aAllowNativeWrapper) {
  js::AssertSameCompartment(aCx, aScope);
  nsresult rv;
  // Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
  // on all threads.
  nsWrapperCache* cache = aHelper.GetWrapperCache();

  if (cache) {
    JS::Rooted<JSObject*> obj(aCx, cache->GetWrapper());
    if (!obj) {
      obj = cache->WrapObject(aCx, nullptr);
      if (!obj) {
        return Throw(aCx, NS_ERROR_UNEXPECTED);
      }
    }

    if (aAllowNativeWrapper && !JS_WrapObject(aCx, &obj)) {
      return false;
    }

    aRetval.setObject(*obj);
    return true;
  }

  MOZ_ASSERT(NS_IsMainThread());

  if (!XPCConvert::NativeInterface2JSObject(aCx, aRetval, aHelper, aIID,
                                            aAllowNativeWrapper, &rv)) {
    // I can't tell if NativeInterface2JSObject throws JS exceptions
    // or not.  This is a sloppy stab at the right semantics; the
    // method really ought to be fixed to behave consistently.
    if (!JS_IsExceptionPending(aCx)) {
      Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
    }
    return false;
  }
  return true;
}

bool TryPreserveWrapper(JS::Handle<JSObject*> obj) {
  MOZ_ASSERT(IsDOMObject(obj));

  // nsISupports objects are special cased because DOM proxies are nsISupports
  // and have addProperty hooks that do more than wrapper preservation (so we
  // don't want to call them).
  if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
    nsWrapperCache* cache = nullptr;
    CallQueryInterface(native, &cache);
    if (cache) {
      cache->PreserveWrapper(native);
    }
    return true;
  }

  // The addProperty hook for WebIDL classes does wrapper preservation, and
  // nothing else, so call it, if present.

  const JSClass* clasp = JS::GetClass(obj);
  const DOMJSClass* domClass = GetDOMClass(clasp);

  // We expect all proxies to be nsISupports.
  MOZ_RELEASE_ASSERT(clasp->isNativeObject(),
                     "Should not call addProperty for proxies.");

  JSAddPropertyOp addProperty = clasp->getAddProperty();
  if (!addProperty) {
    return true;
  }

  // The class should have an addProperty hook iff it is a CC participant.
  MOZ_RELEASE_ASSERT(domClass->mParticipant);

  JS::Rooted<jsid> dummyId(RootingCx());
  JS::Rooted<JS::Value> dummyValue(RootingCx());
  return addProperty(nullptr, obj, dummyId, dummyValue);
}

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

  nsWrapperCache* cache = nullptr;
  if (nsISupports* native = UnwrapDOMObjectToISupports(obj)) {
    CallQueryInterface(native, &cache);
  } else {
    const JSClass* clasp = JS::GetClass(obj);
    const DOMJSClass* domClass = GetDOMClass(clasp);

    // We expect all proxies to be nsISupports.
    MOZ_RELEASE_ASSERT(clasp->isNativeObject(),
                       "Should not call getWrapperCache for proxies.");

    WrapperCacheGetter getter = domClass->mWrapperCacheGetter;

    if (getter) {
      // If the class has a wrapper cache getter it must be a CC participant.
      MOZ_RELEASE_ASSERT(domClass->mParticipant);

      cache = getter(obj);
    }
  }

  return cache && !cache->PreservingWrapper();
}

// Can only be called with a DOM JSClass.
bool InstanceClassHasProtoAtDepth(const JSClass* clasp, uint32_t protoID,
                                  uint32_t depth) {
  const DOMJSClass* domClass = DOMJSClass::FromJSClass(clasp);
  return static_cast<uint32_t>(domClass->mInterfaceChain[depth]) == protoID;
}

// 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) {
  return NativeInterface2JSObjectAndThrowIfFailed(cx, scope, rval, helper, iid,
                                                  allowNativeWrapper);
}

bool VariantToJsval(JSContext* aCx, nsIVariant* aVariant,
                    JS::MutableHandle<JS::Value> aRetval) {
  nsresult rv;
  if (!XPCVariant::VariantDataToJS(aCx, aVariant, &rv, aRetval)) {
    // Does it throw?  Who knows
    if (!JS_IsExceptionPending(aCx)) {
      Throw(aCx, NS_FAILED(rv) ? rv : NS_ERROR_UNEXPECTED);
    }
    return false;
  }

  return true;
}

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

// {JSPropertySpec,JSFunctionSpec} use {JSPropertySpec,JSFunctionSpec}::Name
// and ConstantSpec uses `const char*` for name field.
static inline JSPropertySpec::Name ToPropertySpecName(
    JSPropertySpec::Name name) {
  return name;
}

static inline JSPropertySpec::Name ToPropertySpecName(const char* name) {
  return JSPropertySpec::Name(name);
}

template <typename SpecT>
static bool InitPropertyInfos(JSContext* cx, const Prefable<SpecT>* pref,
                              PropertyInfo* infos, PropertyType type) {
  MOZ_ASSERT(pref);
  MOZ_ASSERT(pref->specs);

  // Index of the Prefable that contains the id for the current PropertyInfo.
  uint32_t prefIndex = 0;

  do {
    // We ignore whether the set of ids is enabled and just intern all the IDs,
    // because this is only done once per application runtime.
    const SpecT* spec = pref->specs;
    // Index of the property/function/constant spec for our current PropertyInfo
    // in the "specs" array of the relevant Prefable.
    uint32_t specIndex = 0;
    do {
      jsid id;
      if (!JS::PropertySpecNameToPermanentId(cx, ToPropertySpecName(spec->name),
                                             &id)) {
        return false;
      }
      infos->SetId(id);
      infos->type = type;
      infos->prefIndex = prefIndex;
      infos->specIndex = specIndex++;
      ++infos;
    } while ((++spec)->name);
    ++prefIndex;
  } while ((++pref)->specs);

  return true;
}

#define INIT_PROPERTY_INFOS_IF_DEFINED(TypeName)                        \
  {                                                                     \
    if (nativeProperties->Has##TypeName##s() &&                         \
        !InitPropertyInfos(cx, nativeProperties->TypeName##s(),         \
                           nativeProperties->TypeName##PropertyInfos(), \
                           e##TypeName)) {                              \
      return false;                                                     \
    }                                                                   \
  }

static bool InitPropertyInfos(JSContext* cx,
                              const NativeProperties* nativeProperties) {
  INIT_PROPERTY_INFOS_IF_DEFINED(StaticMethod);
  INIT_PROPERTY_INFOS_IF_DEFINED(StaticAttribute);
  INIT_PROPERTY_INFOS_IF_DEFINED(Method);
  INIT_PROPERTY_INFOS_IF_DEFINED(Attribute);
  INIT_PROPERTY_INFOS_IF_DEFINED(UnforgeableMethod);
  INIT_PROPERTY_INFOS_IF_DEFINED(UnforgeableAttribute);
  INIT_PROPERTY_INFOS_IF_DEFINED(Constant);

  // Initialize and sort the index array.
  uint16_t* indices = nativeProperties->sortedPropertyIndices;
  auto count = nativeProperties->propertyInfoCount;
  for (auto i = 0; i < count; ++i) {
    indices[i] = i;
  }
  std::sort(indices, indices + count,
            [infos = nativeProperties->PropertyInfos()](const uint16_t left,
                                                        const uint16_t right) {
              // std::sort may call us with the same element by design but
              // PropertyInfo::Compare does not like that.
              if (left == right) {
                return false;
              }
              return PropertyInfo::Compare(infos[left], infos[right]) < 0;
            });

  return true;
}

#undef INIT_PROPERTY_INFOS_IF_DEFINED

static inline bool InitPropertyInfos(
    JSContext* aCx, const NativePropertiesHolder& nativeProperties) {
  MOZ_ASSERT(NS_IsMainThread());

  if (!*nativeProperties.inited) {
    if (nativeProperties.regular &&
        !InitPropertyInfos(aCx, nativeProperties.regular)) {
      return false;
    }
    if (nativeProperties.chromeOnly &&
        !InitPropertyInfos(aCx, nativeProperties.chromeOnly)) {
      return false;
    }
    *nativeProperties.inited = true;
  }

  return true;
}

void GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
                      nsWrapperCache* aCache, JS::Handle<JS::Value> aIID,
                      JS::MutableHandle<JS::Value> aRetval,
                      ErrorResult& aError) {
  Maybe<nsIID> iid = xpc::JSValue2ID(aCx, aIID);
  if (!iid) {
    aError.Throw(NS_ERROR_XPC_BAD_CONVERT_JS);
    return;
  }

  RefPtr<nsISupports> result;
  aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
  if (aError.Failed()) {
    return;
  }

  if (!WrapObject(aCx, result, iid.ptr(), aRetval)) {
    aError.Throw(NS_ERROR_FAILURE);
  }
}

bool ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp) {
  // Cast nullptr to void* to work around
  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100666
  return ThrowErrorMessage<MSG_ILLEGAL_CONSTRUCTOR>(cx, (void*)nullptr);
}

bool ThrowConstructorWithoutNew(JSContext* cx, const char* name) {
  return ThrowErrorMessage<MSG_CONSTRUCTOR_WITHOUT_NEW>(cx, name);
}

inline const NativePropertyHooks* GetNativePropertyHooksFromJSNative(
    JS::Handle<JSObject*> obj) {
  return NativeHolderFromObject(obj)->mPropertyHooks;
}

inline const NativePropertyHooks* GetNativePropertyHooks(
    JSContext* cx, JS::Handle<JSObject*> obj, DOMObjectType& type) {
  const JSClass* clasp = JS::GetClass(obj);

  const DOMJSClass* domClass = GetDOMClass(clasp);
  if (domClass) {
    bool isGlobal = (clasp->flags & JSCLASS_DOM_GLOBAL) != 0;
    type = isGlobal ? eGlobalInstance : eInstance;
    return domClass->mNativeHooks;
  }

  if (JS_ObjectIsFunction(obj)) {
    type = eInterface;
    return GetNativePropertyHooksFromJSNative(obj);
  }

  MOZ_ASSERT(IsDOMIfaceAndProtoClass(JS::GetClass(obj)));
  const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
      DOMIfaceAndProtoJSClass::FromJSClass(JS::GetClass(obj));
  type = ifaceAndProtoJSClass->mType;
  return ifaceAndProtoJSClass->mNativeHooks;
}

static JSObject* XrayCreateFunction(JSContext* cx,
                                    JS::Handle<JSObject*> wrapper,
                                    JSNativeWrapper native, unsigned nargs,
                                    JS::Handle<jsid> id) {
  JSFunction* fun;
  if (id.isString()) {
    fun = js::NewFunctionByIdWithReserved(cx, native.op, nargs, 0, id);
  } else {
    // Can't pass this id (probably a symbol) to NewFunctionByIdWithReserved;
    // just use an empty name for lack of anything better.
    fun = js::NewFunctionWithReserved(cx, native.op, nargs, 0, nullptr);
  }

  if (!fun) {
    return nullptr;
  }

  SET_JITINFO(fun, native.info);
  JSObject* obj = JS_GetFunctionObject(fun);
  js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_PARENT_WRAPPER_SLOT,
                                JS::ObjectValue(*wrapper));
#ifdef DEBUG
  js::SetFunctionNativeReserved(obj, XRAY_DOM_FUNCTION_NATIVE_SLOT_FOR_SELF,
                                JS::ObjectValue(*obj));
#endif
  return obj;
}

struct IdToIndexComparator {
  // The id we're searching for.
  const jsid& mId;
  // Whether we're searching for static operations.
  const bool mStatic;
  // The list of ids we're searching in.
  const PropertyInfo* mInfos;

  IdToIndexComparator(const jsid& aId, DOMObjectType aType,
                      const PropertyInfo* aInfos)
      : mId(aId),
        mStatic(aType == eInterface || aType == eNamespace),
        mInfos(aInfos) {}
  int operator()(const uint16_t aIndex) const {
    const PropertyInfo& info = mInfos[aIndex];
    if (mId.asRawBits() == info.Id().asRawBits()) {
      if (info.type != eMethod && info.type != eStaticMethod) {
        return 0;
      }

      if (mStatic == info.IsStaticMethod()) {
        // We're looking for static properties and we've found a static one for
        // the right name.
        return 0;
      }

      // Static operations are sorted before others by PropertyInfo::Compare.
      return mStatic ? -1 : 1;
    }

    return mId.asRawBits() < info.Id().asRawBits() ? -1 : 1;
  }
};

static const PropertyInfo* XrayFindOwnPropertyInfo(
    JSContext* cx, DOMObjectType type, JS::Handle<jsid> id,
    const NativeProperties* nativeProperties) {
  if ((type == eInterfacePrototype || type == eGlobalInstance) &&
      MOZ_UNLIKELY(nativeProperties->iteratorAliasMethodIndex >= 0) &&
      id.isWellKnownSymbol(JS::SymbolCode::iterator)) {
    return nativeProperties->MethodPropertyInfos() +
           nativeProperties->iteratorAliasMethodIndex;
  }

  size_t idx;
  const uint16_t* sortedPropertyIndices =
      nativeProperties->sortedPropertyIndices;
  const PropertyInfo* propertyInfos = nativeProperties->PropertyInfos();

  if (BinarySearchIf(sortedPropertyIndices, 0,
                     nativeProperties->propertyInfoCount,
                     IdToIndexComparator(id, type, propertyInfos), &idx)) {
    return propertyInfos + sortedPropertyIndices[idx];
  }

  return nullptr;
}

static bool XrayResolveAttribute(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    JS::Handle<jsid> id, const Prefable<const JSPropertySpec>& pref,
    const JSPropertySpec& attrSpec,
    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder) {
  if (!pref.isEnabled(cx, obj)) {
    return true;
  }

  MOZ_ASSERT(attrSpec.isAccessor());

  MOZ_ASSERT(
      !attrSpec.isSelfHosted(),
      "Bad JSPropertySpec declaration: unsupported self-hosted accessor");

  cacheOnHolder = true;

  JS::Rooted<jsid> getterId(cx);
  if (!JS::ToGetterId(cx, id, &getterId)) {
    return false;
  }

  // Because of centralization, we need to make sure we fault in the JitInfos as
  // well. At present, until the JSAPI changes, the easiest way to do this is
  // wrap them up as functions ourselves.

  // They all have getters, so we can just make it.
  JS::Rooted<JSObject*> getter(
      cx, XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.getter.native, 0,
                             getterId));
  if (!getter) {
    return false;
  }

  JS::Rooted<JSObject*> setter(cx);
  if (attrSpec.u.accessors.setter.native.op) {
    JS::Rooted<jsid> setterId(cx);
    if (!JS::ToSetterId(cx, id, &setterId)) {
      return false;
    }

    // We have a setter! Make it.
    setter = XrayCreateFunction(cx, wrapper, attrSpec.u.accessors.setter.native,
                                1, setterId);
    if (!setter) {
      return false;
    }
  }

  desc.set(Some(
      JS::PropertyDescriptor::Accessor(getter, setter, attrSpec.attributes())));
  return true;
}

static bool XrayResolveMethod(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    JS::Handle<jsid> id, const Prefable<const JSFunctionSpec>& pref,
    const JSFunctionSpec& methodSpec,
    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder) {
  if (!pref.isEnabled(cx, obj)) {
    return true;
  }

  cacheOnHolder = true;

  JSObject* funobj;
  if (methodSpec.selfHostedName) {
    JSFunction* fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName,
                                                id, methodSpec.nargs);
    if (!fun) {
      return false;
    }
    MOZ_ASSERT(!methodSpec.call.op,
               "Bad FunctionSpec declaration: non-null native");
    MOZ_ASSERT(!methodSpec.call.info,
               "Bad FunctionSpec declaration: non-null jitinfo");
    funobj = JS_GetFunctionObject(fun);
  } else {
    funobj =
        XrayCreateFunction(cx, wrapper, methodSpec.call, methodSpec.nargs, id);
    if (!funobj) {
      return false;
    }
  }

  desc.set(Some(JS::PropertyDescriptor::Data(JS::ObjectValue(*funobj),
                                             methodSpec.flags)));
  return true;
}

static bool XrayResolveConstant(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    JS::Handle<jsid>, const Prefable<const ConstantSpec>& pref,
    const ConstantSpec& constantSpec,
    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder) {
  if (!pref.isEnabled(cx, obj)) {
    return true;
  }

  cacheOnHolder = true;

  desc.set(Some(JS::PropertyDescriptor::Data(
      constantSpec.value, {JS::PropertyAttribute::Enumerable})));
  return true;
}

#define RESOLVE_CASE(PropType, SpecType, Resolver)                            \
  case e##PropType: {                                                         \
    MOZ_ASSERT(nativeProperties->Has##PropType##s());                         \
    const Prefable<const SpecType>& pref =                                    \
        nativeProperties->PropType##s()[propertyInfo.prefIndex];              \
    return Resolver(cx, wrapper, obj, id, pref,                               \
                    pref.specs[propertyInfo.specIndex], desc, cacheOnHolder); \
  }

static bool XrayResolveProperty(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    JS::Handle<jsid> id, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder, DOMObjectType type,
    const NativeProperties* nativeProperties,
    const PropertyInfo& propertyInfo) {
  MOZ_ASSERT(type != eGlobalInterfacePrototype);

  // Make sure we resolve for matched object type.
  switch (propertyInfo.type) {
    case eStaticMethod:
    case eStaticAttribute:
      if (type != eInterface && type != eNamespace) {
        return true;
      }
      break;
    case eMethod:
    case eAttribute:
      if (type != eGlobalInstance && type != eInterfacePrototype) {
        return true;
      }
      break;
    case eUnforgeableMethod:
    case eUnforgeableAttribute:
      if (!IsInstance(type)) {
        return true;
      }
      break;
    case eConstant:
      if (IsInstance(type)) {
        return true;
      }
      break;
  }

  switch (propertyInfo.type) {
    RESOLVE_CASE(StaticMethod, JSFunctionSpec, XrayResolveMethod)
    RESOLVE_CASE(StaticAttribute, JSPropertySpec, XrayResolveAttribute)
    RESOLVE_CASE(Method, JSFunctionSpec, XrayResolveMethod)
    RESOLVE_CASE(Attribute, JSPropertySpec, XrayResolveAttribute)
    RESOLVE_CASE(UnforgeableMethod, JSFunctionSpec, XrayResolveMethod)
    RESOLVE_CASE(UnforgeableAttribute, JSPropertySpec, XrayResolveAttribute)
    RESOLVE_CASE(Constant, ConstantSpec, XrayResolveConstant)
  }

  return true;
}

#undef RESOLVE_CASE

static bool ResolvePrototypeOrConstructor(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    size_t protoAndIfaceCacheIndex, unsigned attrs,
    JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder) {
  JS::Rooted<JSObject*> global(cx, JS::GetNonCCWObjectGlobal(obj));
  {
    JSAutoRealm ar(cx, global);
    ProtoAndIfaceCache& protoAndIfaceCache = *GetProtoAndIfaceCache(global);
    // This function is called when resolving the "constructor" and "prototype"
    // properties of Xrays for DOM prototypes and constructors respectively.
    // This means the relevant Xray exists, which means its _target_ exists.
    // And that means we managed to successfullly create the prototype or
    // constructor, respectively, and hence must have managed to create the
    // thing it's pointing to as well.  So our entry slot must exist.
    JSObject* protoOrIface =
        protoAndIfaceCache.EntrySlotMustExist(protoAndIfaceCacheIndex);
    MOZ_RELEASE_ASSERT(protoOrIface, "How can this object not exist?");

    cacheOnHolder = true;

    desc.set(Some(
        JS::PropertyDescriptor::Data(JS::ObjectValue(*protoOrIface), attrs)));
  }
  return JS_WrapPropertyDescriptor(cx, desc);
}

/* static */ bool XrayResolveOwnProperty(
    JSContext* cx, JS::Handle<JSObject*> wrapper, JS::Handle<JSObject*> obj,
    JS::Handle<jsid> id, JS::MutableHandle<Maybe<JS::PropertyDescriptor>> desc,
    bool& cacheOnHolder) {
  MOZ_ASSERT(desc.isNothing());
  cacheOnHolder = false;

  DOMObjectType type;
  const NativePropertyHooks* nativePropertyHooks =
      GetNativePropertyHooks(cx, obj, type);
  ResolveOwnProperty resolveOwnProperty =
      nativePropertyHooks->mIndexedOrNamedNativeProperties
          ? nativePropertyHooks->mIndexedOrNamedNativeProperties
                ->mResolveOwnProperty
          : nullptr;

  if (type == eNamedPropertiesObject) {
    MOZ_ASSERT(!resolveOwnProperty,
               "Shouldn't have any Xray-visible properties");
    return true;
  }

  const NativePropertiesHolder& nativePropertiesHolder =
      nativePropertyHooks->mNativeProperties;

  if (!InitPropertyInfos(cx, nativePropertiesHolder)) {
    return false;
  }

  const NativeProperties* nativeProperties = nullptr;
  const PropertyInfo* found = nullptr;

  if ((nativeProperties = nativePropertiesHolder.regular)) {
    found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties);
  }
  if (!found && (nativeProperties = nativePropertiesHolder.chromeOnly) &&
      xpc::AccessCheck::isChrome(JS::GetCompartment(wrapper))) {
    found = XrayFindOwnPropertyInfo(cx, type, id, nativeProperties);
  }

  if (IsInstance(type)) {
    // Check for unforgeable properties first to prevent names provided by
    // resolveOwnProperty callback from shadowing them.
    if (found && (found->type == eUnforgeableMethod ||
                  found->type == eUnforgeableAttribute)) {
      if (!XrayResolveProperty(cx, wrapper, obj, id, desc, cacheOnHolder, type,
                               nativeProperties, *found)) {
        return false;
      }

      if (desc.isSome()) {
        return true;
      }
    }

    if (resolveOwnProperty) {
      if (!resolveOwnProperty(cx, wrapper, obj, id, desc)) {
        return false;
      }

      if (desc.isSome()) {
        // None of these should be cached on the holder, since they're dynamic.
        return true;
      }
    }

    // For non-global instance Xrays there are no other properties, so return
    // here for them.
    if (type != eGlobalInstance) {
      return true;
    }
  } else if (type == eInterface) {
    if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_PROTOTYPE)) {
      return nativePropertyHooks->mPrototypeID == prototypes::id::_ID_Count ||
             ResolvePrototypeOrConstructor(
                 cx, wrapper, obj, nativePropertyHooks->mPrototypeID,
                 JSPROP_PERMANENT | JSPROP_READONLY, desc, cacheOnHolder);
    }

    if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_ISINSTANCE)) {
      if (IsInterfaceObject(obj) &&
          InterfaceInfoFromObject(obj)->wantsInterfaceIsInstance) {
        cacheOnHolder = true;

        JSObject* funObj = XrayCreateFunction(
            cx, wrapper, {InterfaceIsInstance, nullptr}, 1, id);
        if (!funObj) {
          return false;
        }

        desc.set(Some(JS::PropertyDescriptor::Data(
            JS::ObjectValue(*funObj), {JS::PropertyAttribute::Configurable,
                                       JS::PropertyAttribute::Writable})));
        return true;
      }
    }

    if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_NAME)) {
      const char* name = IsInterfaceObject(obj)
                             ? InterfaceInfoFromObject(obj)->mConstructorName
                             : LegacyFactoryFunctionFromObject(obj)->mName;
      JSString* nameStr = JS_NewStringCopyZ(cx, name);
      if (!nameStr) {
        return false;
      }
      desc.set(Some(JS::PropertyDescriptor::Data(
          JS::StringValue(nameStr), {JS::PropertyAttribute::Configurable,
                                     JS::PropertyAttribute::Enumerable})));
      return true;
    }

    if (id.get() == GetJSIDByIndex(cx, XPCJSContext::IDX_LENGTH)) {
      uint8_t length = IsInterfaceObject(obj)
                           ? InterfaceInfoFromObject(obj)->mConstructorArgs
                           : LegacyFactoryFunctionFromObject(obj)->mNargs;
      desc.set(Some(JS::PropertyDescriptor::Data(
--> --------------------

--> maximum size reached

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

98%


¤ Dauer der Verarbeitung: 0.40 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 ist noch experimentell.