From 0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 03:47:29 +0200 Subject: Adding upstream version 115.8.0esr. Signed-off-by: Daniel Baumann --- dom/base/CustomElementRegistry.cpp | 1587 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1587 insertions(+) create mode 100644 dom/base/CustomElementRegistry.cpp (limited to 'dom/base/CustomElementRegistry.cpp') diff --git a/dom/base/CustomElementRegistry.cpp b/dom/base/CustomElementRegistry.cpp new file mode 100644 index 0000000000..d8b40b5bc5 --- /dev/null +++ b/dom/base/CustomElementRegistry.cpp @@ -0,0 +1,1587 @@ +/* -*- 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 "mozilla/dom/CustomElementRegistry.h" + +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/CycleCollectedJSContext.h" +#include "mozilla/dom/AutoEntryScript.h" +#include "mozilla/dom/CustomElementRegistryBinding.h" +#include "mozilla/dom/ElementBinding.h" +#include "mozilla/dom/HTMLElement.h" +#include "mozilla/dom/HTMLElementBinding.h" +#include "mozilla/dom/PrimitiveConversions.h" +#include "mozilla/dom/ShadowIncludingTreeIterator.h" +#include "mozilla/dom/XULElementBinding.h" +#include "mozilla/dom/Promise.h" +#include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/CustomEvent.h" +#include "mozilla/dom/ShadowRoot.h" +#include "mozilla/AutoRestore.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/UseCounter.h" +#include "nsContentUtils.h" +#include "nsHTMLTags.h" +#include "jsapi.h" +#include "js/ForOfIterator.h" // JS::ForOfIterator +#include "js/PropertyAndElement.h" // JS_GetProperty, JS_GetUCProperty +#include "xpcprivate.h" +#include "nsGlobalWindow.h" +#include "nsNameSpaceManager.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 mDefinition; +}; + +//----------------------------------------------------- +// CustomElementCallbackReaction + +class CustomElementCallback { + public: + CustomElementCallback(Element* aThisObject, ElementCallbackType aCallbackType, + CallbackFunction* aCallback, + const LifecycleCallbackArgs& aArgs); + void Traverse(nsCycleCollectionTraversalCallback& aCb) const; + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; + void Call(); + + static UniquePtr Create( + ElementCallbackType aType, Element* aCustomElement, + const LifecycleCallbackArgs& aArgs, CustomElementDefinition* aDefinition); + + private: + // The this value to use for invocation of the callback. + RefPtr mThisObject; + RefPtr mCallback; + // The type of callback (eCreated, eAttached, etc.) + ElementCallbackType mType; + // Arguments to be passed to the callback, + LifecycleCallbackArgs mArgs; +}; + +class CustomElementCallbackReaction final : public CustomElementReaction { + public: + explicit CustomElementCallbackReaction( + UniquePtr aCustomElementCallback) + : mCustomElementCallback(std::move(aCustomElementCallback)) {} + + virtual void Traverse( + nsCycleCollectionTraversalCallback& aCb) const override { + mCustomElementCallback->Traverse(aCb); + } + + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override { + size_t n = aMallocSizeOf(this); + + n += mCustomElementCallback->SizeOfIncludingThis(aMallocSizeOf); + + return n; + } + + private: + virtual void Invoke(Element* aElement, ErrorResult& aRv) override { + mCustomElementCallback->Call(); + } + + UniquePtr mCustomElementCallback; +}; + +//----------------------------------------------------- +// CustomElementCallback + +size_t LifecycleCallbackArgs::SizeOfExcludingThis( + MallocSizeOf aMallocSizeOf) const { + size_t n = mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += mOldValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += mNewValue.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + n += mNamespaceURI.SizeOfExcludingThisIfUnshared(aMallocSizeOf); + return n; +} + +/* static */ +UniquePtr 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::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::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(aCustomElement, aType, func, aArgs); +} + +void CustomElementCallback::Call() { + switch (mType) { + case ElementCallbackType::eConnected: + static_cast(mCallback.get()) + ->Call(mThisObject); + break; + case ElementCallbackType::eDisconnected: + static_cast(mCallback.get()) + ->Call(mThisObject); + break; + case ElementCallbackType::eAdopted: + static_cast(mCallback.get()) + ->Call(mThisObject, mArgs.mOldDocument, mArgs.mNewDocument); + break; + case ElementCallbackType::eAttributeChanged: + static_cast(mCallback.get()) + ->Call(mThisObject, mArgs.mName, mArgs.mOldValue, mArgs.mNewValue, + mArgs.mNamespaceURI); + break; + case ElementCallbackType::eFormAssociated: + static_cast(mCallback.get()) + ->Call(mThisObject, mArgs.mForm); + break; + case ElementCallbackType::eFormReset: + static_cast(mCallback.get()) + ->Call(mThisObject); + break; + case ElementCallbackType::eFormDisabled: + static_cast(mCallback.get()) + ->Call(mThisObject, mArgs.mDisabled); + 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); +} + +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 += 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) {} + +//----------------------------------------------------- +// 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>& 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>& 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) + : mWindow(aWindow), mIsCustomDefinitionRunning(false) { + MOZ_ASSERT(aWindow); + + mozilla::HoldJSObjects(this); +} + +CustomElementRegistry::~CustomElementRegistry() { + mozilla::DropJSObjects(this); +} + +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 definition = + mRegistry->mCustomDefinitions.Get(mAtom); + MOZ_ASSERT(definition, "Callback should define the definition of type."); + MOZ_ASSERT(!mRegistry->mElementCreationCallbacks.GetWeak(mAtom), + "Callback should be removed."); + + mozilla::UniquePtr>> elements; + mRegistry->mElementCreationCallbacksUpgradeCandidatesMap.Remove(mAtom, + &elements); + MOZ_ASSERT(elements, "There should be a list"); + + for (const auto& key : *elements) { + nsCOMPtr 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; +} + +CustomElementDefinition* CustomElementRegistry::LookupCustomElementDefinition( + nsAtom* aNameAtom, int32_t aNameSpaceID, nsAtom* aTypeAtom) { + CustomElementDefinition* data = mCustomDefinitions.GetWeak(aTypeAtom); + + if (!data) { + RefPtr callback; + mElementCreationCallbacks.Get(aTypeAtom, getter_AddRefs(callback)); + if (callback) { + mElementCreationCallbacks.Remove(aTypeAtom); + mElementCreationCallbacksUpgradeCandidatesMap.GetOrInsertNew(aTypeAtom); + RefPtr runnable = + new RunCustomElementCreationCallback(this, aTypeAtom, callback); + nsContentUtils::AddScriptRunner(runnable.forget()); + data = mCustomDefinitions.GetWeak(aTypeAtom); + } + } + + if (data && data->mLocalName == aNameAtom && + data->mNamespaceID == aNameSpaceID) { + return data; + } + + 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 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.