Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  CustomElementRegistry.cpp

  Sprache: C
 

/* 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 "mozilla/dom/CustomElementRegistry.h"

#include "js/ForOfIterator.h"       // JS::ForOfIterator
#include "js/PropertyAndElement.h"  // JS_GetProperty, JS_GetUCProperty
#include "jsapi.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/CycleCollectedUniquePtr.h"
#include "mozilla/HoldDropJSObjects.h"
#include "mozilla/UseCounter.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/CustomElementRegistryBinding.h"
#include "mozilla/dom/CustomEvent.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/HTMLElement.h"
#include "mozilla/dom/HTMLElementBinding.h"
#include "mozilla/dom/LifecycleCallbackArgs.h"
#include "mozilla/dom/PrimitiveConversions.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/ShadowIncludingTreeIterator.h"
#include "mozilla/dom/ShadowRoot.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/XULElementBinding.h"
#include "nsContentUtils.h"
#include "nsHTMLTags.h"
#include "nsInterfaceHashtable.h"
#include "nsNameSpaceManager.h"
#include "nsPIDOMWindow.h"
#include "nsPIDOMWindowInlines.h"
#include "xpcprivate.h"

namespace mozilla::dom {

//-----------------------------------------------------
// CustomElementUpgradeReaction

class CustomElementUpgradeReaction final : public CustomElementReaction {
 public:
  explicit CustomElementUpgradeReaction(CustomElementDefinition* aDefinition)
      : mDefinition(aDefinition) {
    mIsUpgradeReaction = true;
  }

  virtual void Traverse(
      nsCycleCollectionTraversalCallback& aCb) const override {
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mDefinition");
    aCb.NoteNativeChild(
        mDefinition, NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
  }

  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override {
    // We don't really own mDefinition.
    return aMallocSizeOf(this);
  }

 private:
  MOZ_CAN_RUN_SCRIPT
  virtual void Invoke(Element* aElement, ErrorResult& aRv) override {
    CustomElementRegistry::Upgrade(aElement, mDefinition, aRv);
  }

  const RefPtr<CustomElementDefinition> mDefinition;
};

//-----------------------------------------------------
// CustomElementCallback

class CustomElementCallback final : public CustomElementReaction {
 public:
  CustomElementCallback(Element* aThisObject, ElementCallbackType aCallbackType,
                        CallbackFunction* aCallback,
                        const LifecycleCallbackArgs& aArgs);
  // Secondary callback is needed when moveBefore falls back to
  // disconnected/connected callbacks.
  void SetSecondaryCallback(ElementCallbackType aType,
                            CallbackFunction* aCallback);
  void Traverse(nsCycleCollectionTraversalCallback& aCb) const override;
  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;

  static UniquePtr<CustomElementCallback> Create(
      ElementCallbackType aType, Element* aCustomElement,
      const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition);

 private:
  MOZ_CAN_RUN_SCRIPT
  void Invoke(Element* aElement, ErrorResult& aRv) override;
  void Call(ElementCallbackType aType, RefPtr<CallbackFunction>& aCallback);
  // The this value to use for invocation of the callback.
  RefPtr<Element> mThisObject;
  RefPtr<CallbackFunction> mCallback;
  RefPtr<CallbackFunction> mSecondaryCallback;
  // The type of callback (eCreated, eAttached, etc.)
  ElementCallbackType mType;
  ElementCallbackType mSecondaryType;
  // Arguments to be passed to the callback,
  LifecycleCallbackArgs mArgs;
};

size_t LifecycleCallbackArgs::SizeOfExcludingThis(
    MallocSizeOf aMallocSizeOf) const {
  size_t n = mOldValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  n += mNewValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  n += mNamespaceURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  return n;
}

/* static */
UniquePtr<CustomElementCallback> CustomElementCallback::Create(
    ElementCallbackType aType, Element* aCustomElement,
    const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
  MOZ_ASSERT(aDefinition, "CustomElementDefinition should not be null");
  MOZ_ASSERT(aCustomElement->GetCustomElementData(),
             "CustomElementData should exist");

  // Let CALLBACK be the callback associated with the key NAME in CALLBACKS.
  CallbackFunction* func = nullptr;
  switch (aType) {
    case ElementCallbackType::eConnected:
      if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) {
        func = aDefinition->mCallbacks->mConnectedCallback.Value();
      }
      break;

    case ElementCallbackType::eDisconnected:
      if (aDefinition->mCallbacks->mDisconnectedCallback.WasPassed()) {
        func = aDefinition->mCallbacks->mDisconnectedCallback.Value();
      }
      break;

    case ElementCallbackType::eAdopted:
      if (aDefinition->mCallbacks->mAdoptedCallback.WasPassed()) {
        func = aDefinition->mCallbacks->mAdoptedCallback.Value();
      }
      break;

    case ElementCallbackType::eConnectedMove:
      if (aDefinition->mCallbacks->mConnectedMoveCallback.WasPassed()) {
        func = aDefinition->mCallbacks->mConnectedMoveCallback.Value();
      } else if (aDefinition->mCallbacks->mDisconnectedCallback.WasPassed()) {
        UniquePtr<CustomElementCallback> callback =
            MakeUnique<CustomElementCallback>(
                aCustomElement, ElementCallbackType::eDisconnected,
                aDefinition->mCallbacks->mDisconnectedCallback.Value(), aArgs);
        if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) {
          callback->SetSecondaryCallback(
              ElementCallbackType::eConnected,
              aDefinition->mCallbacks->mConnectedCallback.Value());
        }
        return callback;
      } else if (aDefinition->mCallbacks->mConnectedCallback.WasPassed()) {
        return MakeUnique<CustomElementCallback>(
            aCustomElement, ElementCallbackType::eConnected,
            aDefinition->mCallbacks->mConnectedCallback.Value(), aArgs);
      }
      break;

    case ElementCallbackType::eAttributeChanged:
      if (aDefinition->mCallbacks->mAttributeChangedCallback.WasPassed()) {
        func = aDefinition->mCallbacks->mAttributeChangedCallback.Value();
      }
      break;

    case ElementCallbackType::eFormAssociated:
      if (aDefinition->mFormAssociatedCallbacks->mFormAssociatedCallback
              .WasPassed()) {
        func = aDefinition->mFormAssociatedCallbacks->mFormAssociatedCallback
                   .Value();
      }
      break;

    case ElementCallbackType::eFormReset:
      if (aDefinition->mFormAssociatedCallbacks->mFormResetCallback
              .WasPassed()) {
        func =
            aDefinition->mFormAssociatedCallbacks->mFormResetCallback.Value();
      }
      break;

    case ElementCallbackType::eFormDisabled:
      if (aDefinition->mFormAssociatedCallbacks->mFormDisabledCallback
              .WasPassed()) {
        func = aDefinition->mFormAssociatedCallbacks->mFormDisabledCallback
                   .Value();
      }
      break;

    case ElementCallbackType::eFormStateRestore:
      if (aDefinition->mFormAssociatedCallbacks->mFormStateRestoreCallback
              .WasPassed()) {
        func = aDefinition->mFormAssociatedCallbacks->mFormStateRestoreCallback
                   .Value();
      }
      break;

    case ElementCallbackType::eGetCustomInterface:
      MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
      break;
  }

  // If there is no such callback, stop.
  if (!func) {
    return nullptr;
  }

  // Add CALLBACK to ELEMENT's callback queue.
  return MakeUnique<CustomElementCallback>(aCustomElement, aType, func, aArgs);
}

void CustomElementCallback::Invoke(Element* aElement, ErrorResult& aRv) {
  if (mCallback) {
    Call(mType, mCallback);
  }
  if (mSecondaryCallback) {
    Call(mSecondaryType, mSecondaryCallback);
  }
}

void CustomElementCallback::Call(ElementCallbackType aType,
                                 RefPtr<CallbackFunction>& aCallback) {
  switch (aType) {
    case ElementCallbackType::eConnected:
      static_cast<LifecycleConnectedCallback*>(aCallback.get())
          ->Call(mThisObject);
      break;
    case ElementCallbackType::eDisconnected:
      static_cast<LifecycleDisconnectedCallback*>(aCallback.get())
          ->Call(mThisObject);
      break;
    case ElementCallbackType::eAdopted:
      static_cast<LifecycleAdoptedCallback*>(aCallback.get())
          ->Call(mThisObject, mArgs.mOldDocument, mArgs.mNewDocument);
      break;
    case ElementCallbackType::eConnectedMove:
      static_cast<LifecycleConnectedMoveCallback*>(aCallback.get())
          ->Call(mThisObject);
      break;
    case ElementCallbackType::eAttributeChanged:
      static_cast<LifecycleAttributeChangedCallback*>(aCallback.get())
          ->Call(mThisObject, nsDependentAtomString(mArgs.mName),
                 mArgs.mOldValue, mArgs.mNewValue, mArgs.mNamespaceURI);
      break;
    case ElementCallbackType::eFormAssociated:
      static_cast<LifecycleFormAssociatedCallback*>(aCallback.get())
          ->Call(mThisObject, mArgs.mForm);
      break;
    case ElementCallbackType::eFormReset:
      static_cast<LifecycleFormResetCallback*>(aCallback.get())
          ->Call(mThisObject);
      break;
    case ElementCallbackType::eFormDisabled:
      static_cast<LifecycleFormDisabledCallback*>(aCallback.get())
          ->Call(mThisObject, mArgs.mDisabled);
      break;
    case ElementCallbackType::eFormStateRestore: {
      if (mArgs.mState.IsNull()) {
        MOZ_ASSERT_UNREACHABLE(
            "A null state should never be restored to a form-associated "
            "custom element");
        return;
      }

      const OwningFileOrUSVStringOrFormData& owningValue = mArgs.mState.Value();
      Nullable<FileOrUSVStringOrFormData> value;
      if (owningValue.IsFormData()) {
        value.SetValue().SetAsFormData() = owningValue.GetAsFormData();
      } else if (owningValue.IsFile()) {
        value.SetValue().SetAsFile() = owningValue.GetAsFile();
      } else {
        value.SetValue().SetAsUSVString() = owningValue.GetAsUSVString();
      }
      static_cast<LifecycleFormStateRestoreCallback*>(aCallback.get())
          ->Call(mThisObject, value, mArgs.mReason);
    } break;
    case ElementCallbackType::eGetCustomInterface:
      MOZ_ASSERT_UNREACHABLE("Don't call GetCustomInterface through callback");
      break;
  }
}

void CustomElementCallback::Traverse(
    nsCycleCollectionTraversalCallback& aCb) const {
  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mThisObject");
  aCb.NoteXPCOMChild(mThisObject);

  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCallback");
  aCb.NoteXPCOMChild(mCallback);

  NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSecondaryCallback");
  aCb.NoteXPCOMChild(mSecondaryCallback);
}

size_t CustomElementCallback::SizeOfIncludingThis(
    MallocSizeOf aMallocSizeOf) const {
  size_t n = aMallocSizeOf(this);

  // We don't uniquely own mThisObject.

  // We own mCallback but it doesn't have any special memory reporting we can do
  // for it other than report its own size.
  n += aMallocSizeOf(mCallback);

  n += aMallocSizeOf(mSecondaryCallback);

  n += mArgs.SizeOfExcludingThis(aMallocSizeOf);

  return n;
}

CustomElementCallback::CustomElementCallback(
    Element* aThisObject, ElementCallbackType aCallbackType,
    mozilla::dom::CallbackFunction* aCallback,
    const LifecycleCallbackArgs& aArgs)
    : mThisObject(aThisObject),
      mCallback(aCallback),
      mType(aCallbackType),
      mArgs(aArgs) {}

void CustomElementCallback::SetSecondaryCallback(
    ElementCallbackType aType, mozilla::dom::CallbackFunction* aCallback) {
  mSecondaryType = aType;
  mSecondaryCallback = aCallback;
}

//-----------------------------------------------------
// CustomElementData

CustomElementData::CustomElementData(nsAtom* aType)
    : CustomElementData(aType, CustomElementData::State::eUndefined) {}

CustomElementData::CustomElementData(nsAtom* aType, State aState)
    : mState(aState), mType(aType) {}

void CustomElementData::SetCustomElementDefinition(
    CustomElementDefinition* aDefinition) {
  // Only allow reset definition to nullptr if the custom element state is
  // "failed".
  MOZ_ASSERT(aDefinition ? !mCustomElementDefinition
                         : mState == State::eFailed);
  MOZ_ASSERT_IF(aDefinition, aDefinition->mType == mType);

  mCustomElementDefinition = aDefinition;
}

void CustomElementData::AttachedInternals() {
  MOZ_ASSERT(!mIsAttachedInternals);

  mIsAttachedInternals = true;
}

CustomElementDefinition* CustomElementData::GetCustomElementDefinition() const {
  // Per spec, if there is a definition, the custom element state should be
  // either "failed" (during upgrade) or "customized".
  MOZ_ASSERT_IF(mCustomElementDefinition, mState != State::eUndefined);

  return mCustomElementDefinition;
}

bool CustomElementData::IsFormAssociated() const {
  // https://html.spec.whatwg.org/#form-associated-custom-element
  return mCustomElementDefinition &&
         !mCustomElementDefinition->IsCustomBuiltIn() &&
         mCustomElementDefinition->mFormAssociated;
}

void CustomElementData::Traverse(
    nsCycleCollectionTraversalCallback& aCb) const {
  for (uint32_t i = 0; i < mReactionQueue.Length(); i++) {
    if (mReactionQueue[i]) {
      mReactionQueue[i]->Traverse(aCb);
    }
  }

  if (mCustomElementDefinition) {
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mCustomElementDefinition");
    aCb.NoteNativeChild(
        mCustomElementDefinition,
        NS_CYCLE_COLLECTION_PARTICIPANT(CustomElementDefinition));
  }

  if (mElementInternals) {
    NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mElementInternals");
    aCb.NoteXPCOMChild(ToSupports(mElementInternals.get()));
  }
}

void CustomElementData::Unlink() {
  mReactionQueue.Clear();
  if (mElementInternals) {
    mElementInternals->Unlink();
    mElementInternals = nullptr;
  }
  mCustomElementDefinition = nullptr;
}

size_t CustomElementData::SizeOfIncludingThis(
    MallocSizeOf aMallocSizeOf) const {
  size_t n = aMallocSizeOf(this);

  n += mReactionQueue.ShallowSizeOfExcludingThis(aMallocSizeOf);

  for (auto& reaction : mReactionQueue) {
    // "reaction" can be null if we're being called indirectly from
    // InvokeReactions (e.g. due to a reaction causing a memory report to be
    // captured somehow).
    if (reaction) {
      n += reaction->SizeOfIncludingThis(aMallocSizeOf);
    }
  }

  return n;
}

//-----------------------------------------------------
// CustomElementRegistry

namespace {

class MOZ_RAII AutoConstructionStackEntry final {
 public:
  AutoConstructionStackEntry(nsTArray<RefPtr<Element>>& aStack,
                             Element* aElement)
      : mStack(aStack) {
    MOZ_ASSERT(aElement->IsHTMLElement() || aElement->IsXULElement());

#ifdef DEBUG
    mIndex = mStack.Length();
#endif
    mStack.AppendElement(aElement);
  }

  ~AutoConstructionStackEntry() {
    MOZ_ASSERT(mIndex == mStack.Length() - 1,
               "Removed element should be the last element");
    mStack.RemoveLastElement();
  }

 private:
  nsTArray<RefPtr<Element>>& mStack;
#ifdef DEBUG
  uint32_t mIndex;
#endif
};

}  // namespace

NS_IMPL_CYCLE_COLLECTION_CLASS(CustomElementRegistry)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CustomElementRegistry)
  tmp->mConstructors.clear();
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCustomDefinitions)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWhenDefinedPromiseMap)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mElementCreationCallbacks)
  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CustomElementRegistry)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomDefinitions)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWhenDefinedPromiseMap)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElementCreationCallbacks)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CustomElementRegistry)
  for (auto iter = tmp->mConstructors.iter(); !iter.done(); iter.next()) {
    aCallbacks.Trace(&iter.get().mutableKey(), "mConstructors key", aClosure);
  }
  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(CustomElementRegistry)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CustomElementRegistry)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CustomElementRegistry)
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

CustomElementRegistry::CustomElementRegistry(nsPIDOMWindowInner* aWindow,
                                             bool aIsScoped)
    : mWindow(aWindow),
      mIsCustomDefinitionRunning(false),
      mIsScoped(aIsScoped) {
  MOZ_ASSERT(aWindow);

  mozilla::HoldJSObjects(this);
}

CustomElementRegistry::~CustomElementRegistry() {
  mozilla::DropJSObjects(this);
}

// https://html.spec.whatwg.org/#dom-customelementregistry
already_AddRefed<CustomElementRegistry> CustomElementRegistry::Constructor(
    const GlobalObject& aGlobal) {
  nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(aGlobal.GetAsSupports());
  // The new CustomElementRegistry() constructor steps are to set this's is
  // scoped to true.
  return MakeAndAddRef<CustomElementRegistry>(win, true);
}

NS_IMETHODIMP
CustomElementRegistry::RunCustomElementCreationCallback::Run() {
  ErrorResult er;
  nsDependentAtomString value(mAtom);
  mCallback->Call(value, er);
  MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()),
             "chrome JavaScript error in the callback.");

  RefPtr<CustomElementDefinition> definition =
      mRegistry->mCustomDefinitions.Get(mAtom);
  if (!definition) {
    // Callback should set the definition of the type.
    MOZ_DIAGNOSTIC_CRASH("Callback should set the definition of the type.");
    return NS_ERROR_FAILURE;
  }

  MOZ_ASSERT(!mRegistry->mElementCreationCallbacks.GetWeak(mAtom),
             "Callback should be removed.");

  mozilla::UniquePtr<nsTHashSet<RefPtr<nsIWeakReference>>> elements;
  mRegistry->mElementCreationCallbacksUpgradeCandidatesMap.Remove(mAtom,
                                                                  &elements);
  MOZ_ASSERT(elements, "There should be a list");

  for (const auto& key : *elements) {
    nsCOMPtr<Element> elem = do_QueryReferent(key);
    if (!elem) {
      continue;
    }

    CustomElementRegistry::Upgrade(elem, definition, er);
    MOZ_ASSERT(NS_SUCCEEDED(er.StealNSResult()),
               "chrome JavaScript error in custom element construction.");
  }

  return NS_OK;
}

/* https://html.spec.whatwg.org/#look-up-a-custom-element-definition
 * Steps 1 and 2 are handled by nsContentUtils::LookupCustomElementDefinition.
 */

CustomElementDefinition* CustomElementRegistry::LookupCustomElementDefinition(
    nsAtom* aNameAtom, int32_t aNameSpaceID, nsAtom* aTypeAtom) {
  CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom);

  // XXX Chrome-only creation callback mechanism; not in spec.
  if (!data) {
    RefPtr<CustomElementCreationCallback> callback;
    mElementCreationCallbacks.Get(aTypeAtom, getter_AddRefs(callback));
    if (callback) {
      mElementCreationCallbacks.Remove(aTypeAtom);
      mElementCreationCallbacksUpgradeCandidatesMap.GetOrInsertNew(aTypeAtom);
      RefPtr<Runnable> runnable =
          new RunCustomElementCreationCallback(this, aTypeAtom, callback);
      nsContentUtils::AddScriptRunner(runnable.forget());
      data = mCustomDefinitions.GetWeak(aTypeAtom);
    }
  }

  // 3. If registry's custom element definition set contains an item with name
  //    and local name both equal to localName, then return that item.
  // 4. If registry's custom element definition set contains an item with name
  //    equal to is and local name equal to localName, then return that item.
  if (data && data->mLocalName == aNameAtom &&
      data->mNamespaceID == aNameSpaceID) {
    return data;
  }

  // 5. Return null.
  return nullptr;
}

CustomElementDefinition* CustomElementRegistry::LookupCustomElementDefinition(
    JSContext* aCx, JSObject* aConstructor) const {
  // We're looking up things that tested true for JS::IsConstructor,
  // so doing a CheckedUnwrapStatic is fine here.
  JS::Rooted<JSObject*> constructor(aCx, js::CheckedUnwrapStatic(aConstructor));

  const auto& ptr = mConstructors.lookup(constructor);
  if (!ptr) {
    return nullptr;
  }

  CustomElementDefinition* definition =
      mCustomDefinitions.GetWeak(ptr->value());
  MOZ_ASSERT(definition, "Definition must be found in mCustomDefinitions");

  return definition;
}

void CustomElementRegistry::RegisterUnresolvedElement(Element* aElement,
                                                      nsAtom* aTypeName) {
  // We don't have a use-case for a Custom Element inside NAC, and continuing
  // here causes performance issues for NAC + XBL anonymous content.
  if (aElement->IsInNativeAnonymousSubtree()) {
    return;
  }

  mozilla::dom::NodeInfo* info = aElement->NodeInfo();

  // Candidate may be a custom element through extension,
  // in which case the custom element type name will not
  // match the element tag name. e.g. <button is="x-button">.
  RefPtr<nsAtom> typeName = aTypeName;
  if (!typeName) {
    typeName = info->NameAtom();
  }

  if (mCustomDefinitions.GetWeak(typeName)) {
    return;
  }

  nsTHashSet<RefPtr<nsIWeakReference>>* unresolved =
      mCandidatesMap.GetOrInsertNew(typeName);
  nsWeakPtr elem = do_GetWeakReference(aElement);
  unresolved->Insert(elem);
}

void CustomElementRegistry::UnregisterUnresolvedElement(Element* aElement,
                                                        nsAtom* aTypeName) {
  nsIWeakReference* weak = aElement->GetExistingWeakReference();
  if (!weak) {
    return;
  }

#ifdef DEBUG
  {
    nsWeakPtr weakPtr = do_GetWeakReference(aElement);
    MOZ_ASSERT(
        weak == weakPtr.get(),
        "do_GetWeakReference should reuse the existing nsIWeakReference.");
  }
#endif

  nsTHashSet<RefPtr<nsIWeakReference>>* candidates = nullptr;
  if (mCandidatesMap.Get(aTypeName, &candidates)) {
    MOZ_ASSERT(candidates);
    candidates->Remove(weak);
  }
}

/* https://html.spec.whatwg.org/#enqueue-a-custom-element-callback-reaction */
/* static */
void CustomElementRegistry::EnqueueLifecycleCallback(
    ElementCallbackType aType, Element* aCustomElement,
    const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition) {
  // 1. Let definition be element's custom element definition.
  CustomElementDefinition* definition = aDefinition;
  if (!definition) {
    definition = aCustomElement->GetCustomElementDefinition();
    if (!definition ||
        definition->mLocalName != aCustomElement->NodeInfo()->NameAtom()) {
      return;
    }

    if (!definition->mCallbacks && !definition->mFormAssociatedCallbacks) {
      // definition has been unlinked.  Don't try to mess with it.
      return;
    }
  }

  // XXX: Steps 2-3 performed by CustomElementCallback::Create:
  // 2. Let callback be the value of the entry in definition's lifecycle
  //    callbacks with key callbackName.
  // 3. If callbackName is "connectedMoveCallback" and callback is null...
  auto callback =
      CustomElementCallback::Create(aType, aCustomElement, aArgs, definition);
  // 4. If callback is null, then return.
  if (!callback) {
    return;
  }

  DocGroup* docGroup = aCustomElement->OwnerDoc()->GetDocGroup();
  if (!docGroup) {
    return;
  }

  // 5. If callbackName is "attributeChangedCallback":
  //    5.1. Let attributeName be the first element of args.
  //    5.2. If definition's observed attributes does not contain attributeName,
  //         then return.
  // Callers must perform this check themselves before calling.
  MOZ_ASSERT(aType != ElementCallbackType::eAttributeChanged ||
                 definition->IsInObservedAttributeList(aArgs.mName),
             "Caller must check IsInObservedAttributeList for "
             "eAttributeChanged");

  // 6. Add a new callback reaction to element's custom element reaction queue,
  //    with callback function callback and arguments args.
  CustomElementReactionsStack* reactionsStack =
      docGroup->CustomElementReactionsStack();

  // 7. Enqueue an element on the appropriate element queue given element.
  reactionsStack->EnqueueCallbackReaction(aCustomElement, std::move(callback));
}

using ScopedRegistryMap =
    nsRefPtrHashtable<nsPtrHashKey<nsINode>, CustomElementRegistry>;

static StaticAutoPtr<ScopedRegistryMap> gScopedRegistryMap;

/* static */
already_AddRefed<CustomElementRegistry>
CustomElementRegistry::GetScopedRegistry(nsINode& aNode) {
  if (!gScopedRegistryMap) {
    return nullptr;
  }
  RefPtr<CustomElementRegistry> registry = gScopedRegistryMap->Get(&aNode);
  if (registry) {
    return registry.forget();
  }
  return nullptr;
}

/* static */
void CustomElementRegistry::SetScopedRegistry(
    nsINode& aNode, CustomElementRegistry& aRegistry) {
  MOZ_ASSERT(aRegistry.IsScoped());
  if (!gScopedRegistryMap) {
    gScopedRegistryMap = new ScopedRegistryMap();
    ClearOnShutdown(&gScopedRegistryMap);
  }
  gScopedRegistryMap->InsertOrUpdate(&aNode, &aRegistry);
}

/* static */
void CustomElementRegistry::RemoveScopedRegistry(nsINode& aNode) {
  if (gScopedRegistryMap) {
    gScopedRegistryMap->Remove(&aNode);
  }
}

namespace {

class CandidateFinder {
 public:
  CandidateFinder(nsTHashSet<RefPtr<nsIWeakReference>>& aCandidates,
                  Document* aDoc);
  nsTArray<nsCOMPtr<Element>> OrderedCandidates();

 private:
  nsCOMPtr<Document> mDoc;
  nsInterfaceHashtable<nsPtrHashKey<Element>, Element> mCandidates;
};

CandidateFinder::CandidateFinder(
    nsTHashSet<RefPtr<nsIWeakReference>>& aCandidates, Document* aDoc)
    : mDoc(aDoc), mCandidates(aCandidates.Count()) {
  MOZ_ASSERT(mDoc);
  for (const auto& candidate : aCandidates) {
    nsCOMPtr<Element> elem = do_QueryReferent(candidate);
    if (!elem) {
      continue;
    }

    Element* key = elem.get();
    mCandidates.InsertOrUpdate(key, elem.forget());
  }
}

nsTArray<nsCOMPtr<Element>> CandidateFinder::OrderedCandidates() {
  if (mCandidates.Count() == 1) {
    // Fast path for one candidate.
    auto iter = mCandidates.Iter();
    nsTArray<nsCOMPtr<Element>> rval({std::move(iter.Data())});
    iter.Remove();
    return rval;
  }

  nsTArray<nsCOMPtr<Element>> orderedElements(mCandidates.Count());
  for (nsINode* node : ShadowIncludingTreeIterator(*mDoc)) {
    Element* element = Element::FromNode(node);
    if (!element) {
      continue;
    }

    nsCOMPtr<Element> elem;
    if (mCandidates.Remove(element, getter_AddRefs(elem))) {
      orderedElements.AppendElement(std::move(elem));
      if (mCandidates.Count() == 0) {
        break;
      }
    }
  }

  return orderedElements;
}

}  // namespace

// https://html.spec.whatwg.org/#upgrade-particular-elements-within-a-document
void CustomElementRegistry::UpgradeCandidates(
    nsAtom* aKey, CustomElementDefinition* aDefinition, ErrorResult& aRv) {
  DocGroup* docGroup = mWindow->GetDocGroup();
  if (!docGroup) {
    aRv.Throw(NS_ERROR_UNEXPECTED);
    return;
  }

  // 1. Let upgradeCandidates be all elements that are shadow-including
  //    descendants of document, whose custom element registry is registry,
  //    whose namespace is the HTML namespace, and whose local name is
  //    localName, in shadow-including tree order. Additionally, if name is not
  //    localName, only include elements whose is value is equal to name.
  // TODO(keithamus): The "whose custom element registry is registry" filter is
  // not yet implemented (scoped registries).
  mozilla::UniquePtr<nsTHashSet<RefPtr<nsIWeakReference>>> candidates;
  if (mCandidatesMap.Remove(aKey, &candidates)) {
    MOZ_ASSERT(candidates);
    CustomElementReactionsStack* reactionsStack =
        docGroup->CustomElementReactionsStack();

    CandidateFinder finder(*candidates, mWindow->GetExtantDoc());
    // 2. For each element element of upgradeCandidates: enqueue a custom
    //    element upgrade reaction given element and definition.
    for (auto& elem : finder.OrderedCandidates()) {
      reactionsStack->EnqueueUpgradeReaction(elem, aDefinition);
    }
  }
}

JSObject* CustomElementRegistry::WrapObject(JSContext* aCx,
                                            JS::Handle<JSObject*> aGivenProto) {
  return CustomElementRegistry_Binding::Wrap(aCx, this, aGivenProto);
}

nsISupports* CustomElementRegistry::GetParentObject() const { return mWindow; }

DocGroup* CustomElementRegistry::GetDocGroup() const {
  return mWindow ? mWindow->GetDocGroup() : nullptr;
}

int32_t CustomElementRegistry::InferNamespace(
    JSContext* aCx, JS::Handle<JSObject*> constructor) {
  JS::Rooted<JSObject*> XULConstructor(
      aCx, XULElement_Binding::GetConstructorObjectHandle(aCx));

  JS::Rooted<JSObject*> proto(aCx, constructor);
  while (proto) {
    if (proto == XULConstructor) {
      return kNameSpaceID_XUL;
    }

    JS_GetPrototype(aCx, proto, &proto);
  }

  return kNameSpaceID_XHTML;
}

bool CustomElementRegistry::JSObjectToAtomArray(
    JSContext* aCx, JS::Handle<JSObject*> aConstructor, const nsString& aName,
    nsTArray<RefPtr<nsAtom>>& aArray, ErrorResult& aRv) {
  JS::Rooted<JS::Value> iterable(aCx, JS::UndefinedValue());
  if (!JS_GetUCProperty(aCx, aConstructor, aName.get(), aName.Length(),
                        &iterable)) {
    aRv.NoteJSContextException(aCx);
    return false;
  }

  if (!iterable.isUndefined()) {
    if (!iterable.isObject()) {
      aRv.ThrowTypeError<MSG_CONVERSION_ERROR>(NS_ConvertUTF16toUTF8(aName),
                                               "sequence");
      return false;
    }

    JS::ForOfIterator iter(aCx);
    if (!iter.init(iterable, JS::ForOfIterator::AllowNonIterable)) {
      aRv.NoteJSContextException(aCx);
      return false;
    }

    if (!iter.valueIsIterable()) {
      aRv.ThrowTypeError<MSG_CONVERSION_ERROR>(NS_ConvertUTF16toUTF8(aName),
                                               "sequence");
      return false;
    }

    JS::Rooted<JS::Value> attribute(aCx);
    while (true) {
      bool done;
      if (!iter.next(&attribute, &done)) {
        aRv.NoteJSContextException(aCx);
        return false;
      }
      if (done) {
        break;
      }

      nsAutoString attrStr;
      if (!ConvertJSValueToString(aCx, attribute, eStringify, eStringify,
                                  attrStr)) {
        aRv.NoteJSContextException(aCx);
        return false;
      }

      // XXX(Bug 1631371) Check if this should use a fallible operation as it
      // pretended earlier.
      aArray.AppendElement(NS_Atomize(attrStr));
    }
  }

  return true;
}

// https://html.spec.whatwg.org/#dom-customelementregistry-define
void CustomElementRegistry::Define(
    JSContext* aCx, const nsAString& aName,
    CustomElementConstructor& aFunctionConstructor,
    const ElementDefinitionOptions& aOptions, ErrorResult& aRv) {
  JS::Rooted<JSObject*> constructor(aCx, aFunctionConstructor.CallableOrNull());

  // We need to do a dynamic unwrap in order to throw the right exception.  We
  // could probably avoid that if we just threw MSG_NOT_CONSTRUCTOR if unwrap
  // fails.
  //
  // In any case, aCx represents the global we want to be using for the unwrap
  // here.
  JS::Rooted<JSObject*> constructorUnwrapped(
      aCx, js::CheckedUnwrapDynamic(constructor, aCx));
  if (!constructorUnwrapped) {
    // If the caller's compartment does not have permission to access the
    // unwrapped constructor then throw.
    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
    return;
  }

  /**
   * 1. If IsConstructor(constructor) is false, then throw a TypeError and abort
   *    these steps.
   */

  if (!JS::IsConstructor(constructorUnwrapped)) {
    aRv.ThrowTypeError<MSG_NOT_CONSTRUCTOR>("Argument 2");
    return;
  }

  int32_t nameSpaceID = InferNamespace(aCx, constructor);

  /**
   * 2. If name is not a valid custom element name, then throw a "SyntaxError"
   *    DOMException and abort these steps.
   */

  Document* doc = mWindow->GetExtantDoc();
  RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
  if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
    aRv.ThrowSyntaxError(
        nsPrintfCString("'%s' is not a valid custom element name",
                        NS_ConvertUTF16toUTF8(aName).get()));
    return;
  }

  /**
   * 3. If this CustomElementRegistry contains an entry with name name, then
   *    throw a "NotSupportedError" DOMException and abort these steps.
   */

  if (mCustomDefinitions.GetWeak(nameAtom)) {
    aRv.ThrowNotSupportedError(
        nsPrintfCString("'%s' has already been defined as a custom element",
                        NS_ConvertUTF16toUTF8(aName).get()));
    return;
  }

  /**
   * 4. If this CustomElementRegistry contains an entry with constructor
   * constructor, then throw a "NotSupportedError" DOMException and abort these
   * steps.
   */

  const auto& ptr = mConstructors.lookup(constructorUnwrapped);
  if (ptr) {
    MOZ_ASSERT(mCustomDefinitions.GetWeak(ptr->value()),
               "Definition must be found in mCustomDefinitions");
    nsAutoCString name;
    ptr->value()->ToUTF8String(name);
    aRv.ThrowNotSupportedError(
        nsPrintfCString("'%s' and '%s' have the same constructor",
                        NS_ConvertUTF16toUTF8(aName).get(), name.get()));
    return;
  }

  /**
   * 5. Let localName be name.
   * 6. Let extends be the value of the extends member of options, or null if
   *    no such member exists.
   * 7. If extends is not null, then:
   *    1. If this's is scoped is true, then throw a "NotSupportedError"
   *       DOMException.
   *    2. If extends is a valid custom element name, then throw a
   *       "NotSupportedError" DOMException.
   *    3. If the element interface for extends and the HTML namespace is
   *       HTMLUnknownElement (e.g., if extends does not indicate an element
   *       definition in this specification), then throw a "NotSupportedError"
   *       DOMException.
   *    4. Set localName to extends.
   *
   * Special note for XUL elements:
   *
   * For step 7.2, we'll subject XUL to the same rules as HTML, so that a
   * custom built-in element will not be extending from a dashed name.
   * Step 7.3 is disregarded. But, we do check if the name is a dashed name
   * (i.e. step 2) given that there is no reason for a custom built-in element
   * type to take on a non-dashed name.
   * This also ensures the name of the built-in custom element type can never
   * be the same as the built-in element name, so we don't break the assumption
   * elsewhere.
   */

  RefPtr<nsAtom> localNameAtom = nameAtom;
  if (aOptions.mExtends.WasPassed()) {
    doc->SetUseCounter(eUseCounter_custom_CustomizedBuiltin);

    // TODO(keithamus): 7.1 is not yet implemented (scoped registries).

    RefPtr<nsAtom> extendsAtom(NS_Atomize(aOptions.mExtends.Value()));
    if (nsContentUtils::IsCustomElementName(extendsAtom, kNameSpaceID_XHTML)) {
      aRv.ThrowNotSupportedError(
          nsPrintfCString("'%s' cannot extend a custom element",
                          NS_ConvertUTF16toUTF8(aName).get()));
      return;
    }

    if (nameSpaceID == kNameSpaceID_XHTML) {
      // bgsound and multicol are unknown html element.
      int32_t tag = nsHTMLTags::CaseSensitiveAtomTagToId(extendsAtom);
      if (tag == eHTMLTag_userdefined || tag == eHTMLTag_bgsound ||
          tag == eHTMLTag_multicol) {
        aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
        return;
      }
    } else {  // kNameSpaceID_XUL
      // As stated above, ensure the name of the customized built-in element
      // (the one that goes to the |is| attribute) is a dashed name.
      if (!nsContentUtils::IsNameWithDash(nameAtom)) {
        aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
        return;
      }
    }

    localNameAtom = NS_Atomize(aOptions.mExtends.Value());
  }

  /**
   * 8. If this CustomElementRegistry's element definition is running flag is
   * set, then throw a "NotSupportedError" DOMException and abort these steps.
   */

  if (mIsCustomDefinitionRunning) {
    aRv.ThrowNotSupportedError(
        "Cannot define a custom element while defining another custom element");
    return;
  }

  auto callbacksHolder = MakeUnique<LifecycleCallbacks>();
  auto formAssociatedCallbacksHolder =
      MakeUnique<FormAssociatedLifecycleCallbacks>();
  nsTArray<RefPtr<nsAtom>> observedAttributes;
  AutoTArray<RefPtr<nsAtom>, 2> disabledFeatures;
  bool formAssociated = false;
  bool disableInternals = false;
  bool disableShadow = false;
  {  // Set mIsCustomDefinitionRunning.
    /**
     * 9. Set this CustomElementRegistry's element definition is running flag.
     */

    AutoRestore<bool> restoreRunning(mIsCustomDefinitionRunning);
    mIsCustomDefinitionRunning = true;

    /**
     * 14.1. Let prototype be Get(constructor, "prototype"). Rethrow any
     * exceptions.
     */

    // The .prototype on the constructor passed could be an "expando" of a
    // wrapper. So we should get it from wrapper instead of the underlying
    // object.
    JS::Rooted<JS::Value> prototype(aCx);
    if (!JS_GetProperty(aCx, constructor, "prototype", &prototype)) {
      aRv.NoteJSContextException(aCx);
      return;
    }

    /**
     * 14.2. If Type(prototype) is not Object, then throw a TypeError exception.
     */

    if (!prototype.isObject()) {
      aRv.ThrowTypeError<MSG_NOT_OBJECT>("constructor.prototype");
      return;
    }

    /**
     * 14.3. Let lifecycleCallbacks be a map with the four keys
     *       "connectedCallback", "disconnectedCallback", "adoptedCallback", and
     *       "attributeChangedCallback", each of which belongs to an entry whose
     *       value is null. The 'getCustomInterface' callback is also included
     *       for chrome usage.
     * 14.4. For each of the four keys callbackName in lifecycleCallbacks:
     *       1. Let callbackValue be Get(prototype, callbackName). Rethrow any
     *          exceptions.
     *       2. If callbackValue is not undefined, then set the value of the
     *          entry in lifecycleCallbacks with key callbackName to the result
     *          of converting callbackValue to the Web IDL Function callback
     * type. Rethrow any exceptions from the conversion.
     */

    if (!callbacksHolder->Init(aCx, prototype)) {
      aRv.NoteJSContextException(aCx);
      return;
    }

    /**
     * 14.5. If the value of the entry in lifecycleCallbacks with key
     *       "attributeChangedCallback" is not null, then:
     *       1. Let observedAttributesIterable be Get(constructor,
     *          "observedAttributes"). Rethrow any exceptions.
     *       2. If observedAttributesIterable is not undefined, then set
     *          observedAttributes to the result of converting
     *          observedAttributesIterable to a sequence<DOMString>. Rethrow
     *          any exceptions from the conversion.
     */

    if (callbacksHolder->mAttributeChangedCallback.WasPassed()) {
      if (!JSObjectToAtomArray(aCx, constructor, u"observedAttributes"_ns,
                               observedAttributes, aRv)) {
        return;
      }
    }

    /**
     * 14.6. Let disabledFeatures be an empty sequence<DOMString>.
     * 14.7. Let disabledFeaturesIterable be Get(constructor,
     *       "disabledFeatures"). Rethrow any exceptions.
     * 14.8. If disabledFeaturesIterable is not undefined, then set
     *       disabledFeatures to the result of converting
     *       disabledFeaturesIterable to a sequence<DOMString>.
     *       Rethrow any exceptions from the conversion.
     */

    if (!JSObjectToAtomArray(aCx, constructor, u"disabledFeatures"_ns,
                             disabledFeatures, aRv)) {
      return;
    }

    // 14.9. Set disableInternals to true if disabledFeaturesSequence contains
    //       "internals".
    disableInternals = disabledFeatures.Contains(
        static_cast<nsStaticAtom*>(nsGkAtoms::internals));

    // 14.10. Set disableShadow to true if disabledFeaturesSequence contains
    //        "shadow".
    disableShadow = disabledFeatures.Contains(
        static_cast<nsStaticAtom*>(nsGkAtoms::shadow));

    // 14.11. Let formAssociatedValue be Get(constructor, "formAssociated").
    //        Rethrow any exceptions.
    JS::Rooted<JS::Value> formAssociatedValue(aCx);
    if (!JS_GetProperty(aCx, constructor, "formAssociated",
                        &formAssociatedValue)) {
      aRv.NoteJSContextException(aCx);
      return;
    }

    // 14.12. Set formAssociated to the result of converting
    //        formAssociatedValue to a boolean. Rethrow any exceptions from
    //        the conversion.
    if (!ValueToPrimitive<bool, eDefault>(aCx, formAssociatedValue,
                                          "formAssociated", &formAssociated)) {
      aRv.NoteJSContextException(aCx);
      return;
    }

    /**
     * 14.13. If formAssociated is true, for each of "formAssociatedCallback",
     *        "formResetCallback", "formDisabledCallback", and
     *        "formStateRestoreCallback" callbackName:
     *        1. Let callbackValue be ? Get(prototype, callbackName).
     *        2. If callbackValue is not undefined, then set the value of the
     *           entry in lifecycleCallbacks with key callbackName to the result
     *           of converting callbackValue to the Web IDL Function callback
     *           type. Rethrow any exceptions from the conversion.
     */

    if (formAssociated &&
        !formAssociatedCallbacksHolder->Init(aCx, prototype)) {
      aRv.NoteJSContextException(aCx);
      return;
    }
  }  // Unset mIsCustomDefinitionRunning

  /**
   * 15. Let definition be a new custom element definition with name name,
   *     local name localName, constructor constructor, prototype prototype,
   *     observed attributes observedAttributes, and lifecycle callbacks
   *     lifecycleCallbacks.
   * 16. Add definition to this CustomElementRegistry.
   */

  if (!mConstructors.put(constructorUnwrapped, nameAtom)) {
    aRv.Throw(NS_ERROR_FAILURE);
    return;
  }

  RefPtr<CustomElementDefinition> definition = new CustomElementDefinition(
      nameAtom, localNameAtom, nameSpaceID, &aFunctionConstructor,
      std::move(observedAttributes), std::move(callbacksHolder),
      std::move(formAssociatedCallbacksHolder), formAssociated,
      disableInternals, disableShadow);

  CustomElementDefinition* def = definition.get();
  mCustomDefinitions.InsertOrUpdate(nameAtom, std::move(definition));

  MOZ_ASSERT(mCustomDefinitions.Count() == mConstructors.count(),
             "Number of entries should be the same");

  /**
   * 17. If this's is scoped is true, then for each document of this's scoped
   *     document set: upgrade particular elements within a document given
   *     this, document, definition, and localName.
   * 18. Otherwise, upgrade particular elements within a document given this,
   *     this's relevant global object's associated Document, definition,
   *     localName, and name.
   *
   * TODO(keithamus): Step 17 is not yet implemented (scoped registries). We
   * currently always take the step 18 path.
   */

  UpgradeCandidates(nameAtom, def, aRv);

  /**
   * 19. If this's when-defined promise map contains an entry with key name:
   *     1. Resolve this's when-defined promise map[name] with constructor.
   *     2. Remove this's when-defined promise map[name].
   */

  RefPtr<Promise> promise;
  mWhenDefinedPromiseMap.Remove(nameAtom, getter_AddRefs(promise));
  if (promise) {
    promise->MaybeResolve(def->mConstructor);
  }

  // Dispatch a "customelementdefined" event for DevTools.
  BrowsingContext* browsingContext = mWindow->GetBrowsingContext();
  if (browsingContext && browsingContext->WatchedByDevTools()) {
    JSString* nameJsStr =
        JS_NewUCStringCopyN(aCx, aName.BeginReading(), aName.Length());

    JS::Rooted<JS::Value> detail(aCx, JS::StringValue(nameJsStr));
    RefPtr<CustomEvent> event = NS_NewDOMCustomEvent(doc, nullptr, nullptr);
    event->InitCustomEvent(aCx, u"customelementdefined"_ns,
                           /* CanBubble */ true,
                           /* Cancelable */ true, detail);
    event->SetTrusted(true);

    AsyncEventDispatcher* dispatcher =
        new AsyncEventDispatcher(doc, event.forget());
    dispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;

    dispatcher->PostDOMEvent();
  }

  /**
   * Clean-up mElementCreationCallbacks (if it exists)
   */

  mElementCreationCallbacks.Remove(nameAtom);
}

void CustomElementRegistry::SetElementCreationCallback(
    const nsAString& aName, CustomElementCreationCallback& aCallback,
    ErrorResult& aRv) {
  RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
  if (mElementCreationCallbacks.GetWeak(nameAtom) ||
      mCustomDefinitions.GetWeak(nameAtom)) {
    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
    return;
  }

  RefPtr<CustomElementCreationCallback> callback = &aCallback;

  if (mCandidatesMap.Contains(nameAtom)) {
    mElementCreationCallbacksUpgradeCandidatesMap.GetOrInsertNew(nameAtom);
    RefPtr<Runnable> runnable =
        new RunCustomElementCreationCallback(this, nameAtom, callback);
    nsContentUtils::AddScriptRunner(runnable.forget());
  } else {
    mElementCreationCallbacks.InsertOrUpdate(nameAtom, std::move(callback));
  }
}

// https://html.spec.whatwg.org/#dom-customelementregistry-upgrade
void CustomElementRegistry::Upgrade(nsINode& aRoot) {
  // 1. For each shadow-including inclusive descendant candidate of root, in
  //    shadow-including tree order:
  for (nsINode* node : ShadowIncludingTreeIterator(aRoot)) {
    // 1.1. If candidate is not an Element node, then continue.
    Element* element = Element::FromNode(node);
    if (!element) {
      continue;
    }

    // TODO(keithamus): 1.2. If candidate's custom element registry is not this,
    // then continue. Not yet implemented -- we don't check the element's
    // registry against |this|. We always look up via the document's registry
    // (scoped registries).
    CustomElementData* ceData = element->GetCustomElementData();
    if (ceData) {
      // 1.3. Try to upgrade candidate.
      NodeInfo* nodeInfo = element->NodeInfo();
      nsAtom* typeAtom = ceData->GetCustomElementType();
      CustomElementDefinition* definition =
          nsContentUtils::LookupCustomElementDefinition(
              nodeInfo->GetDocument(), nodeInfo->NameAtom(),
              nodeInfo->NamespaceID(), typeAtom);
      if (definition) {
        nsContentUtils::EnqueueUpgradeReaction(element, definition);
      }
    }
  }
}

/* https://html.spec.whatwg.org/#dom-customelementregistry-initialize */
void CustomElementRegistry::Initialize(nsINode& aRoot, ErrorResult& aRv) {
  MOZ_ASSERT(StaticPrefs::dom_scoped_custom_element_registries_enabled());

  // Step 1: If this's is scoped is false and either root is a Document node or
  // root's node document's custom element registry is not this, then throw a
  // "NotSupportedError" DOMException.
  if (!mIsScoped) {
    if (aRoot.IsDocument()) {
      aRv.ThrowNotSupportedError(
          "Global registry cannot initialize a Document");
      return;
    }
    CustomElementRegistry* docRegistry =
        aRoot.OwnerDoc()->GetCustomElementRegistry();
    if (docRegistry != this) {
      aRv.ThrowNotSupportedError(
          "Global registry can only initialize nodes whose owning document "
          "uses this registry");
      return;
    }
  }

  // Step 2: If root is a Document node whose custom element registry is null,
  // then set root's custom element registry to this.
  // Step 3: Otherwise, if root is a ShadowRoot node whose custom element
  // registry is null, then set root's custom element registry to this.
  if (aRoot.IsDocument()) {
    Document* doc = aRoot.AsDocument();
    if (!doc->GetCustomElementRegistry()) {
      CustomElementRegistry::SetScopedRegistry(*doc, *this);
    }
  } else if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(aRoot)) {
    if (!shadowRoot->GetCustomElementRegistry()) {
      shadowRoot->SetCustomElementRegistry(this);
    }
  }

  // Step 4: For each inclusive descendant inclusiveDescendant of root, in tree
  // order:
  const nsINode* root = &aRoot;
  for (nsINode* node = &aRoot; node; node = node->GetNextNode(root)) {
    // Step 4.1: If inclusiveDescendant is not an Element node, then continue.
    if (!node->IsElement()) {
      continue;
    }
    Element* element = node->AsElement();
    CustomElementRegistry* registry = element->GetCustomElementRegistry();
    // Step 4.2: If inclusiveDescendant's custom element registry is null:
    if (!registry) {
      // Step 4.2.1: Set inclusiveDescendant's custom element registry to this.
      element->SetCustomElementRegistry(this);
      // TODO(keithamus, bug 2018913): Step 4.2.2: If this's is scoped is true,
      // then append inclusiveDescendant's node document to this's scoped
      // document set.
    } else if (registry != this) {
      // Step 4.3: If inclusiveDescendant's custom element registry is not this,
      //           then continue.
      continue;
    }
    // Step 4.4: Try to upgrade inclusiveDescendant.
    // Only try to upgrade if element has custom element data (is an upgrade
    // candidate).
    if (element->GetCustomElementData()) {
      nsContentUtils::TryToUpgradeElement(element);
    }
  }
}

/* https://html.spec.whatwg.org/#dom-customelementregistry-get */
void CustomElementRegistry::Get(
    const nsAString& aName,
    OwningCustomElementConstructorOrUndefined& aRetVal) {
  RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
  CustomElementDefinition* data = mCustomDefinitions.GetWeak(nameAtom);

  // 1. If this's custom element definition set contains an item with name
  //    name, then return that item's constructor.
  // 2. Return undefined.
  if (!data) {
    aRetVal.SetUndefined();
    return;
  }

  aRetVal.SetAsCustomElementConstructor() = data->mConstructor;
}

/* https://html.spec.whatwg.org/#dom-customelementregistry-getname */
void CustomElementRegistry::GetName(JSContext* aCx,
                                    CustomElementConstructor& aConstructor,
                                    nsAString& aResult) {
  CustomElementDefinition* aDefinition =
      LookupCustomElementDefinition(aCx, aConstructor.CallableOrNull());

  // 1. If this's custom element definition set contains an item with
  //    constructor constructor, then return that item's name.
  // 2. Return null.
  if (aDefinition) {
    aDefinition->mType->ToString(aResult);
  } else {
    aResult.SetIsVoid(true);
  }
}

// https://html.spec.whatwg.org/#dom-customelementregistry-whendefined
already_AddRefed<Promise> CustomElementRegistry::WhenDefined(
    const nsAString& aName, ErrorResult& aRv) {
  // Define a function that lazily creates a Promise and perform some action on
  // it when creation succeeded. It's needed in multiple cases below, but not in
  // all of them.
  auto createPromise = [&](auto&& action) -> already_AddRefed<Promise> {
    nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mWindow);
    RefPtr<Promise> promise = Promise::Create(global, aRv);

    if (aRv.Failed()) {
      return nullptr;
    }

    action(promise);

    return promise.forget();
  };

  // 1. If name is not a valid custom element name, then return a promise
  //    rejected with a "SyntaxError" DOMException.
  RefPtr<nsAtom> nameAtom(NS_Atomize(aName));
  Document* doc = mWindow->GetExtantDoc();
  uint32_t nameSpaceID =
      doc ? doc->GetDefaultNamespaceID() : kNameSpaceID_XHTML;
  if (!nsContentUtils::IsCustomElementName(nameAtom, nameSpaceID)) {
    aRv.ThrowSyntaxError(
        nsPrintfCString("'%s' is not a valid custom element name",
                        NS_ConvertUTF16toUTF8(aName).get()));
    return nullptr;
  }

  // 2. If this's custom element definition set contains an item with name name,
  //    then return a promise resolved with that item's constructor.
  if (CustomElementDefinition* definition =
          mCustomDefinitions.GetWeak(nameAtom)) {
    return createPromise([&](const RefPtr<Promise>& promise) {
      promise->MaybeResolve(definition->mConstructor);
    });
  }

  // 3. If this's when-defined promise map[name] does not exist, then set this's
  //    when-defined promise map[name] to a new promise.
  // 4. Return this's when-defined promise map[name].
  return mWhenDefinedPromiseMap.WithEntryHandle(
      nameAtom, [&](auto&& entry) -> already_AddRefed<Promise> {
        if (!entry) {
          return createPromise([&entry](const RefPtr<Promise>& promise) {
            entry.Insert(promise);
          });
        }
        return do_AddRef(entry.Data());
      });
}

namespace {

/* Part of https://html.spec.whatwg.org/#concept-upgrade-an-element step 9 */
MOZ_CAN_RUN_SCRIPT
static void DoUpgrade(Element* aElement, CustomElementDefinition* aDefinition,
                      CustomElementConstructor* aConstructor,
                      ErrorResult& aRv) {
  // 9.1. If definition's disable shadow is true and element's shadow root is
  //      non-null, then throw a "NotSupportedError" DOMException.
  if (aDefinition->mDisableShadow && aElement->GetShadowRoot()) {
    aRv.ThrowNotSupportedError(nsPrintfCString(
        "Custom element upgrade to '%s' is disabled because a shadow root "
        "already exists",
        NS_ConvertUTF16toUTF8(aDefinition->mType->GetUTF16String()).get()));
    return;
  }

  // 9.2. Set element's custom element state to "precustomized".
  CustomElementData* data = aElement->GetCustomElementData();
  MOZ_ASSERT(data, "CustomElementData should exist");
  data->mState = CustomElementData::State::ePrecustomized;

  // 9.3. Let constructResult be the result of constructing C, with no
  //      arguments.
  JS::Rooted<JS::Value> constructResult(RootingCx());
  // Rethrow the exception since it might actually throw the exception from the
  // upgrade steps back out to the caller of document.createElement.
  aConstructor->Construct(&constructResult, aRv, "Custom Element Upgrade",
                          CallbackFunction::eRethrowExceptions);
  if (aRv.Failed()) {
    return;
  }

  // 9.4. If SameValue(constructResult, element) is false, then throw a
  //      TypeError.
  Element* element;
  // constructResult is an ObjectValue because construction with a callback
  // always forms the return value from a JSObject.
  if (NS_FAILED(UNWRAP_OBJECT(Element, &constructResult, element)) ||
      element != aElement) {
    aRv.ThrowTypeError("Custom element constructor returned a wrong element");
    return;
  }
}

}  // anonymous namespace

/* https://html.spec.whatwg.org/#concept-upgrade-an-element */
/* static */
void CustomElementRegistry::Upgrade(Element* aElement,
                                    CustomElementDefinition* aDefinition,
                                    ErrorResult& aRv) {
  CustomElementData* data = aElement->GetCustomElementData();
  MOZ_ASSERT(data, "CustomElementData should exist");

  // 1. If element's custom element state is not "undefined" or "uncustomized",
  //    then return.
  if (data->mState != CustomElementData::State::eUndefined) {
    return;
  }

  // 2. Set element's custom element definition to definition.
  aElement->SetCustomElementDefinition(aDefinition);

  // 3. Set element's custom element state to "failed".
  data->mState = CustomElementData::State::eFailed;

  // 4. For each attribute in element's attribute list, in order, enqueue a
  //    custom element callback reaction with element, callback name
  //    "attributeChangedCallback", and arguments.
  if (!aDefinition->mObservedAttributes.IsEmpty()) {
    uint32_t count = aElement->GetAttrCount();
    for (uint32_t i = 0; i < count; i++) {
      mozilla::dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(i);

      const nsAttrName* name = info.mName;
      nsAtom* attrName = name->LocalName();

      if (aDefinition->IsInObservedAttributeList(attrName)) {
        int32_t namespaceID = name->NamespaceID();
        nsAutoString attrValue, namespaceURI;
        info.mValue->ToString(attrValue);
        nsNameSpaceManager::GetInstance()->GetNameSpaceURI(namespaceID,
                                                           namespaceURI);

        LifecycleCallbackArgs args;
        args.mName = attrName;
        args.mOldValue = VoidString();
        args.mNewValue = std::move(attrValue);
        args.mNamespaceURI =
            (namespaceURI.IsEmpty() ? VoidString() : std::move(namespaceURI));

        nsContentUtils::EnqueueLifecycleCallback(
            ElementCallbackType::eAttributeChanged, aElement, args,
            aDefinition);
      }
    }
  }

  // 5. If element is connected, then enqueue a custom element callback reaction
  //    with element, callback name "connectedCallback", and an empty list.
  if (aElement->IsInComposedDoc()) {
    nsContentUtils::EnqueueLifecycleCallback(ElementCallbackType::eConnected,
                                             aElement, {}, aDefinition);
  }

  // 6. Add element to the end of definition's construction stack.
  AutoConstructionStackEntry acs(aDefinition->mConstructionStack, aElement);

  // XXX: Steps 7-9 performed by DoUpgrade:
  // 7. Let C be definition's constructor.
  // TODO(keithamus): 8. Set the active custom element constructor map[C] to
  // element's custom element registry. Not yet using element's registry (scoped
  // registries).
  // 9. Run the following steps while catching any exceptions:
  DoUpgrade(aElement, aDefinition, MOZ_KnownLive(aDefinition->mConstructor),
            aRv);
  if (aRv.Failed()) {
    MOZ_ASSERT(data->mState == CustomElementData::State::eFailed ||
               data->mState == CustomElementData::State::ePrecustomized);
    // Spec doesn't set custom element state to failed here, but without this we
    // would have inconsistent state on a custom elemet that is failed to
    // upgrade, see https://github.com/whatwg/html/issues/6929, and
    // https://github.com/web-platform-tests/wpt/pull/29911 for the test.
    data->mState = CustomElementData::State::eFailed;
    aElement->SetCustomElementDefinition(nullptr);
    // Empty element's custom element reaction queue.
    data->mReactionQueue.Clear();
    return;
  }

  // 11. Set element's custom element state to "custom".
  data->mState = CustomElementData::State::eCustom;
  aElement->SetDefined(true);

  // 10. If element is a form-associated custom element, then:
  if (data->IsFormAssociated()) {
    // 10.1. Reset the form owner of element.
    // 10.2. ...
    ElementInternals* internals = data->GetElementInternals();
    MOZ_ASSERT(internals);
    MOZ_ASSERT(aElement->IsHTMLElement());
    MOZ_ASSERT(!aDefinition->IsCustomBuiltIn());

    internals->UpdateFormOwner();
  }
}

already_AddRefed<nsISupports> CustomElementRegistry::CallGetCustomInterface(
    Element* aElement, const nsIID& aIID) {
  MOZ_ASSERT(aElement);

  if (!nsContentUtils::IsChromeDoc(aElement->OwnerDoc())) {
    return nullptr;
  }

  // Try to get our GetCustomInterfaceCallback callback.
  CustomElementDefinition* definition = aElement->GetCustomElementDefinition();
  if (!definition || !definition->mCallbacks ||
      !definition->mCallbacks->mGetCustomInterfaceCallback.WasPassed() ||
      (definition->mLocalName != aElement->NodeInfo()->NameAtom())) {
    return nullptr;
  }
  LifecycleGetCustomInterfaceCallback* func =
      definition->mCallbacks->mGetCustomInterfaceCallback.Value();

  // Initialize a AutoJSAPI to enter the compartment of the callback.
  AutoJSAPI jsapi;
  JS::Rooted<JSObject*> funcGlobal(RootingCx(), func->CallbackGlobalOrNull());
  if (!funcGlobal || !jsapi.Init(funcGlobal)) {
    return nullptr;
  }

  // Grab our JSContext.
  JSContext* cx = jsapi.cx();

  // Convert our IID to a JSValue to call our callback.
  JS::Rooted<JS::Value> jsiid(cx);
  if (!xpc::ID2JSValue(cx, aIID, &jsiid)) {
    return nullptr;
  }

  JS::Rooted<JSObject*> customInterface(cx);
  func->Call(aElement, jsiid, &customInterface);
  if (!customInterface) {
    return nullptr;
  }

  // Wrap our JSObject into a nsISupports through XPConnect
  nsCOMPtr<nsISupports> wrapper;
  nsresult rv = nsContentUtils::XPConnect()->WrapJSAggregatedToNative(
      aElement, cx, customInterface, aIID, getter_AddRefs(wrapper));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return nullptr;
  }

  return wrapper.forget();
}

void CustomElementRegistry::TraceDefinitions(JSTracer* aTrc) {
  for (const RefPtr<CustomElementDefinition>& definition :
       mCustomDefinitions.Values()) {
    if (definition && definition->mConstructor) {
      mozilla::TraceScriptHolder(definition->mConstructor, aTrc);
    }
  }
}

//-----------------------------------------------------
// CustomElementReactionsStack

void CustomElementReactionsStack::CreateAndPushElementQueue() {
  MOZ_ASSERT(mRecursionDepth);
  MOZ_ASSERT(!mIsElementQueuePushedForCurrentRecursionDepth);

  // Push an element queue onto the custom element reactions stack, reusing
  // the cached one if available to avoid a heap allocation.
  if (mCachedElementQueue) {
    MOZ_ASSERT(mCachedElementQueue->IsEmpty());
    mReactionsStack.AppendElement(std::move(mCachedElementQueue));
  } else {
    mReactionsStack.AppendElement(MakeUnique<ElementQueue>());
  }
  mIsElementQueuePushedForCurrentRecursionDepth = true;
}

void CustomElementReactionsStack::PopAndInvokeElementQueue() {
  MOZ_ASSERT(mRecursionDepth);
  MOZ_ASSERT(mIsElementQueuePushedForCurrentRecursionDepth);
  MOZ_ASSERT(!mReactionsStack.IsEmpty(), "Reaction stack shouldn't be empty");

  // Pop the element queue from the custom element reactions stack,
  // and invoke custom element reactions in that queue.
  const uint32_t lastIndex = mReactionsStack.Length() - 1;
  ElementQueue* elementQueue = mReactionsStack.ElementAt(lastIndex).get();
  // Check element queue size in order to reduce function call overhead.
  if (!elementQueue->IsEmpty()) {
    // It is still not clear what error reporting will look like in custom
    // element, see https://github.com/w3c/webcomponents/issues/635.
    // We usually report the error to entry global in gecko, so just follow the
    // same behavior here.
    // This may be null if it's called from parser, see the case of
    // attributeChangedCallback in
    // https://html.spec.whatwg.org/multipage/parsing.html#create-an-element-for-the-token
    // In that case, the exception of callback reactions will be automatically
    // reported in CallSetup.
    nsIGlobalObject* global = GetEntryGlobal();
    InvokeReactions(elementQueue, MOZ_KnownLive(global));
  }

  // InvokeReactions() might create other custom element reactions, but those
  // new reactions should be already consumed and removed at this point.
  MOZ_ASSERT(
      lastIndex == mReactionsStack.Length() - 1,
      "reactions created by InvokeReactions() should be consumed and removed");

  UniquePtr<ElementQueue> popped = std::move(mReactionsStack.LastElement());
  mReactionsStack.RemoveLastElement();
  // Cache the popped queue for reuse, but only if it still uses inline
  // storage so we don't hold on to a grown heap buffer.
  if (!mCachedElementQueue && popped->Capacity() == kElementQueueInlineSize) {
    popped->ClearAndRetainStorage();
    mCachedElementQueue = std::move(popped);
  }
  mIsElementQueuePushedForCurrentRecursionDepth = false;
}

void CustomElementReactionsStack::EnqueueUpgradeReaction(
    Element* aElement, CustomElementDefinition* aDefinition) {
  Enqueue(aElement, new CustomElementUpgradeReaction(aDefinition));
}

void CustomElementReactionsStack::EnqueueCallbackReaction(
    Element* aElement,
    UniquePtr<CustomElementCallback> aCustomElementCallback) {
  Enqueue(aElement, aCustomElementCallback.release());
}

void CustomElementReactionsStack::Enqueue(Element* aElement,
                                          CustomElementReaction* aReaction) {
  CustomElementData* elementData = aElement->GetCustomElementData();
  MOZ_ASSERT(elementData, "CustomElementData should exist");

  if (mRecursionDepth) {
    // If the element queue is not created for current recursion depth, create
    // and push an element queue to reactions stack first.
    if (!mIsElementQueuePushedForCurrentRecursionDepth) {
      CreateAndPushElementQueue();
    }

    MOZ_ASSERT(!mReactionsStack.IsEmpty());
    // Add element to the current element queue.
    mReactionsStack.LastElement()->AppendElement(aElement);
    elementData->mReactionQueue.AppendElement(aReaction);
    return;
  }

  // If the custom element reactions stack is empty, then:
  // Add element to the backup element queue.
  MOZ_ASSERT(mReactionsStack.IsEmpty(),
             "custom element reactions stack should be empty");
  mBackupQueue.AppendElement(aElement);
  elementData->mReactionQueue.AppendElement(aReaction);

  if (mIsBackupQueueProcessing) {
    return;
  }

  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
  RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
  context->DispatchToMicroTask(bqmt.forget());
}

void CustomElementReactionsStack::InvokeBackupQueue() {
  // Check backup queue size in order to reduce function call overhead.
  if (!mBackupQueue.IsEmpty()) {
    // Upgrade reactions won't be scheduled in backup queue and the exception of
    // callback reactions will be automatically reported in CallSetup.
    // If the reactions are invoked from backup queue (in microtask check
    // point), we don't need to pass global object for error reporting.
    InvokeReactions(&mBackupQueue, nullptr);
  }
  MOZ_ASSERT(
      mBackupQueue.IsEmpty(),
      "There are still some reactions in BackupQueue not being consumed!?!");
}

void CustomElementReactionsStack::InvokeReactions(ElementQueue* aElementQueue,
                                                  nsIGlobalObject* aGlobal) {
  // This is used for error reporting.
  Maybe<AutoEntryScript> aes;
  if (aGlobal) {
    aes.emplace(aGlobal, "custom elements reaction invocation");
  }

  // Note: It's possible to re-enter this method.
  for (uint32_t i = 0; i < aElementQueue->Length(); ++i) {
    Element* element = aElementQueue->ElementAt(i);
    // ElementQueue hold a element's strong reference, it should not be a
    // nullptr.
    MOZ_ASSERT(element);

    CustomElementData* elementData = element->GetCustomElementData();
    if (!elementData || !element->GetRelevantGlobal()) {
      // This happens when the document is destroyed and the element is already
      // unlinked, no need to fire the callbacks in this case.
      continue;
    }

    auto& reactions = elementData->mReactionQueue;
    for (uint32_t j = 0; j < reactions.Length(); ++j) {
      // Transfer the ownership of the entry due to reentrant invocation of
      // this function.
      auto reaction(std::move(reactions.ElementAt(j)));
      if (reaction) {
        if (!aGlobal && reaction->IsUpgradeReaction()) {
          nsIGlobalObject* global = element->GetRelevantGlobal();
          MOZ_ASSERT(!aes);
          aes.emplace(global, "custom elements reaction invocation");
        }
        ErrorResult rv;
        reaction->Invoke(MOZ_KnownLive(element), rv);
        if (aes) {
          JSContext* cx = aes->cx();
          if (rv.MaybeSetPendingException(cx)) {
            aes->ReportException();
          }
          MOZ_ASSERT(!JS_IsExceptionPending(cx));
          if (!aGlobal && reaction->IsUpgradeReaction()) {
            aes.reset();
          }
        }
        MOZ_ASSERT(!rv.Failed());
      }
    }
    reactions.Clear();
  }
  aElementQueue->Clear();
}

//-----------------------------------------------------
// CustomElementDefinition

NS_IMPL_CYCLE_COLLECTION(CustomElementDefinition, mConstructor, mCallbacks,
                         mFormAssociatedCallbacks, mConstructionStack)

CustomElementDefinition::CustomElementDefinition(
    nsAtom* aType, nsAtom* aLocalName, int32_t aNamespaceID,
    CustomElementConstructor* aConstructor,
    nsTArray<RefPtr<nsAtom>>&& aObservedAttributes,
    UniquePtr<LifecycleCallbacks>&& aCallbacks,
    UniquePtr<FormAssociatedLifecycleCallbacks>&& aFormAssociatedCallbacks,
    bool aFormAssociated, bool aDisableInternals, bool aDisableShadow)
    : mType(aType),
      mLocalName(aLocalName),
      mNamespaceID(aNamespaceID),
      mConstructor(aConstructor),
      mObservedAttributes(std::move(aObservedAttributes)),
      mCallbacks(std::move(aCallbacks)),
      mFormAssociatedCallbacks(std::move(aFormAssociatedCallbacks)),
      mFormAssociated(aFormAssociated),
      mDisableInternals(aDisableInternals),
      mDisableShadow(aDisableShadow) {}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=86 H=94 G=89

¤ Dauer der Verarbeitung: 0.28 Sekunden  (vorverarbeitet am  2026-08-22) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=738142