From 36d22d82aa202bb199967e9512281e9a53db42c9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 21:33:14 +0200 Subject: Adding upstream version 115.7.0esr. Signed-off-by: Daniel Baumann --- dom/base/nsINode.cpp | 3703 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3703 insertions(+) create mode 100644 dom/base/nsINode.cpp (limited to 'dom/base/nsINode.cpp') diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp new file mode 100644 index 0000000000..1f18279ece --- /dev/null +++ b/dom/base/nsINode.cpp @@ -0,0 +1,3703 @@ +/* -*- 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/. */ + +/* + * Base class for all DOM nodes. + */ + +#include "nsINode.h" + +#include "AccessCheck.h" +#include "jsapi.h" +#include "js/ForOfIterator.h" // JS::ForOfIterator +#include "js/JSON.h" // JS_ParseJSON +#include "mozAutoDocUpdate.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/CORSMode.h" +#include "mozilla/EventDispatcher.h" +#include "mozilla/EventListenerManager.h" +#include "mozilla/HTMLEditor.h" +#include "mozilla/InternalMutationEvent.h" +#include "mozilla/Likely.h" +#include "mozilla/Maybe.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/PresShell.h" +#include "mozilla/ServoBindings.h" +#include "mozilla/Telemetry.h" +#include "mozilla/TextControlElement.h" +#include "mozilla/TextEditor.h" +#include "mozilla/TimeStamp.h" +#include "mozilla/dom/BindContext.h" +#include "mozilla/dom/CharacterData.h" +#include "mozilla/dom/ChildIterator.h" +#include "mozilla/dom/CustomElementRegistry.h" +#include "mozilla/dom/DebuggerNotificationBinding.h" +#include "mozilla/dom/DocumentType.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/Event.h" +#include "mozilla/dom/Exceptions.h" +#include "mozilla/dom/Link.h" +#include "mozilla/dom/HTMLImageElement.h" +#include "mozilla/dom/HTMLMediaElement.h" +#include "mozilla/dom/HTMLTemplateElement.h" +#include "mozilla/dom/MutationObservers.h" +#include "mozilla/dom/Selection.h" +#include "mozilla/dom/ShadowRoot.h" +#include "mozilla/dom/SVGUseElement.h" +#include "mozilla/dom/ScriptSettings.h" +#include "mozilla/dom/L10nOverlays.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/StaticPrefs_layout.h" +#include "nsAttrValueOrString.h" +#include "nsCCUncollectableMarker.h" +#include "nsContentCreatorFunctions.h" +#include "nsContentList.h" +#include "nsContentUtils.h" +#include "nsCOMArray.h" +#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/Attr.h" +#include "nsDOMAttributeMap.h" +#include "nsDOMCID.h" +#include "nsDOMCSSAttrDeclaration.h" +#include "nsError.h" +#include "nsDOMMutationObserver.h" +#include "nsDOMString.h" +#include "nsDOMTokenList.h" +#include "nsFocusManager.h" +#include "nsFrameSelection.h" +#include "nsGenericHTMLElement.h" +#include "nsGkAtoms.h" +#include "nsIAnonymousContentCreator.h" +#include "nsAtom.h" +#include "nsIContentInlines.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "nsIFrameInlines.h" +#include "mozilla/dom/NodeInfo.h" +#include "mozilla/dom/NodeInfoInlines.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScrollableFrame.h" +#include "nsView.h" +#include "nsViewManager.h" +#include "nsIWidget.h" +#include "nsLayoutUtils.h" +#include "nsNameSpaceManager.h" +#include "nsNodeInfoManager.h" +#include "nsObjectLoadingContent.h" +#include "nsPIDOMWindow.h" +#include "nsPresContext.h" +#include "nsPrintfCString.h" +#include "nsRange.h" +#include "nsString.h" +#include "nsStyleConsts.h" +#include "nsTextNode.h" +#include "nsUnicharUtils.h" +#include "nsWindowSizes.h" +#include "mozilla/Preferences.h" +#include "xpcpublic.h" +#include "HTMLLegendElement.h" +#include "nsWrapperCacheInlines.h" +#include "WrapperFactory.h" +#include +#include "nsGlobalWindow.h" +#include "GeometryUtils.h" +#include "nsIAnimationObserver.h" +#include "nsChildContentList.h" +#include "mozilla/dom/NodeBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/AncestorIterator.h" +#include "xpcprivate.h" + +#include "XPathGenerator.h" + +#ifdef ACCESSIBILITY +# include "mozilla/dom/AccessibleNode.h" +#endif + +using namespace mozilla; +using namespace mozilla::dom; + +static bool ShouldUseNACScope(const nsINode* aNode) { + return aNode->IsInNativeAnonymousSubtree(); +} + +static bool ShouldUseUAWidgetScope(const nsINode* aNode) { + return aNode->HasBeenInUAWidget(); +} + +void* nsINode::operator new(size_t aSize, nsNodeInfoManager* aManager) { + if (StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { + MOZ_ASSERT(aManager, "nsNodeInfoManager needs to be initialized"); + return aManager->Allocate(aSize); + } + return ::operator new(aSize); +} +void nsINode::operator delete(void* aPtr) { free_impl(aPtr); } + +bool nsINode::IsInclusiveDescendantOf(const nsINode* aNode) const { + MOZ_ASSERT(aNode, "The node is nullptr."); + + if (aNode == this) { + return true; + } + + if (!aNode->HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN)) { + return GetParentNode() == aNode; + } + + for (nsINode* node : Ancestors(*this)) { + if (node == aNode) { + return true; + } + } + return false; +} + +bool nsINode::IsInclusiveFlatTreeDescendantOf(const nsINode* aNode) const { + MOZ_ASSERT(aNode, "The node is nullptr."); + + for (nsINode* node : InclusiveFlatTreeAncestors(*this)) { + if (node == aNode) { + return true; + } + } + return false; +} + +bool nsINode::IsShadowIncludingInclusiveDescendantOf( + const nsINode* aNode) const { + MOZ_ASSERT(aNode, "The node is nullptr."); + + if (this->GetComposedDoc() == aNode) { + return true; + } + + const nsINode* node = this; + do { + if (node == aNode) { + return true; + } + + node = node->GetParentOrShadowHostNode(); + } while (node); + + return false; +} + +nsINode::nsSlots::nsSlots() : mWeakReference(nullptr) {} + +nsINode::nsSlots::~nsSlots() { + if (mChildNodes) { + mChildNodes->InvalidateCacheIfAvailable(); + } + + if (mWeakReference) { + mWeakReference->NoticeNodeDestruction(); + } +} + +void nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback& cb) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildNodes"); + cb.NoteXPCOMChild(mChildNodes); +} + +void nsINode::nsSlots::Unlink(nsINode&) { + if (mChildNodes) { + mChildNodes->InvalidateCacheIfAvailable(); + ImplCycleCollectionUnlink(mChildNodes); + } +} + +//---------------------------------------------------------------------- + +#ifdef MOZILLA_INTERNAL_API +nsINode::nsINode(already_AddRefed&& aNodeInfo) + : mNodeInfo(std::move(aNodeInfo)), + mParent(nullptr) +# ifndef BOOL_FLAGS_ON_WRAPPER_CACHE + , + mBoolFlags(0) +# endif + , + mChildCount(0), + mPreviousOrLastSibling(nullptr), + mSubtreeRoot(this), + mSlots(nullptr) { +} +#endif + +nsINode::~nsINode() { + MOZ_ASSERT(!HasSlots(), "LastRelease was not called?"); + MOZ_ASSERT(mSubtreeRoot == this, "Didn't restore state properly?"); +} + +#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED +void nsINode::AssertInvariantsOnNodeInfoChange() { + MOZ_DIAGNOSTIC_ASSERT(!IsInComposedDoc()); + if (nsCOMPtr link = do_QueryInterface(this)) { + MOZ_DIAGNOSTIC_ASSERT(!link->HasPendingLinkUpdate()); + } +} +#endif + +void* nsINode::GetProperty(const nsAtom* aPropertyName, + nsresult* aStatus) const { + if (!HasProperties()) { // a fast HasFlag() test + if (aStatus) { + *aStatus = NS_PROPTABLE_PROP_NOT_THERE; + } + return nullptr; + } + return OwnerDoc()->PropertyTable().GetProperty(this, aPropertyName, aStatus); +} + +nsresult nsINode::SetProperty(nsAtom* aPropertyName, void* aValue, + NSPropertyDtorFunc aDtor, bool aTransfer) { + nsresult rv = OwnerDoc()->PropertyTable().SetProperty( + this, aPropertyName, aValue, aDtor, nullptr, aTransfer); + if (NS_SUCCEEDED(rv)) { + SetFlags(NODE_HAS_PROPERTIES); + } + + return rv; +} + +void nsINode::RemoveProperty(const nsAtom* aPropertyName) { + OwnerDoc()->PropertyTable().RemoveProperty(this, aPropertyName); +} + +void* nsINode::TakeProperty(const nsAtom* aPropertyName, nsresult* aStatus) { + return OwnerDoc()->PropertyTable().TakeProperty(this, aPropertyName, aStatus); +} + +nsIContentSecurityPolicy* nsINode::GetCsp() const { + return OwnerDoc()->GetCsp(); +} + +nsINode::nsSlots* nsINode::CreateSlots() { return new nsSlots(); } + +static const nsINode* GetClosestCommonInclusiveAncestorForRangeInSelection( + const nsINode* aNode) { + while (aNode && + !aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) { + if (!aNode + ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { + return nullptr; + } + aNode = aNode->GetParentNode(); + } + return aNode; +} + +/** + * A Comparator suitable for mozilla::BinarySearchIf for searching a collection + * of nsRange* for an overlap of (mNode, mStartOffset) .. (mNode, mEndOffset). + */ +class IsItemInRangeComparator { + public: + // @param aStartOffset has to be less or equal to aEndOffset. + IsItemInRangeComparator(const nsINode& aNode, const uint32_t aStartOffset, + const uint32_t aEndOffset, + nsContentUtils::ComparePointsCache* aCache) + : mNode(aNode), + mStartOffset(aStartOffset), + mEndOffset(aEndOffset), + mCache(aCache) { + MOZ_ASSERT(aStartOffset <= aEndOffset); + } + + int operator()(const AbstractRange* const aRange) const { + int32_t cmp = nsContentUtils::ComparePoints_Deprecated( + &mNode, mEndOffset, aRange->GetStartContainer(), aRange->StartOffset(), + nullptr, mCache); + if (cmp == 1) { + cmp = nsContentUtils::ComparePoints_Deprecated( + &mNode, mStartOffset, aRange->GetEndContainer(), aRange->EndOffset(), + nullptr, mCache); + if (cmp == -1) { + return 0; + } + return 1; + } + return -1; + } + + private: + const nsINode& mNode; + const uint32_t mStartOffset; + const uint32_t mEndOffset; + nsContentUtils::ComparePointsCache* mCache; +}; + +bool nsINode::IsSelected(const uint32_t aStartOffset, + const uint32_t aEndOffset) const { + MOZ_ASSERT(aStartOffset <= aEndOffset); + + const nsINode* n = GetClosestCommonInclusiveAncestorForRangeInSelection(this); + NS_ASSERTION(n || !IsMaybeSelected(), + "A node without a common inclusive ancestor for a range in " + "Selection is for sure not selected."); + + // Collect the selection objects for potential ranges. + nsTHashSet ancestorSelections; + for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection( + n->GetParentNode())) { + const LinkedList* ranges = + n->GetExistingClosestCommonInclusiveAncestorRanges(); + if (!ranges) { + continue; + } + for (const AbstractRange* range : *ranges) { + MOZ_ASSERT(range->IsInAnySelection(), + "Why is this range registered with a node?"); + // Looks like that IsInSelection() assert fails sometimes... + if (range->IsInAnySelection()) { + for (const WeakPtr& selection : range->GetSelections()) { + ancestorSelections.Insert(selection); + } + } + } + } + + nsContentUtils::ComparePointsCache cache; + IsItemInRangeComparator comparator{*this, aStartOffset, aEndOffset, &cache}; + for (Selection* selection : ancestorSelections) { + // Binary search the sorted ranges in this selection. + // (Selection::GetRangeAt returns its ranges ordered). + size_t low = 0; + size_t high = selection->RangeCount(); + + while (high != low) { + size_t middle = low + (high - low) / 2; + + const AbstractRange* const range = selection->GetAbstractRangeAt(middle); + int result = comparator(range); + if (result == 0) { + if (!range->Collapsed()) { + return true; + } + + const AbstractRange* middlePlus1; + const AbstractRange* middleMinus1; + // if node end > start of middle+1, result = 1 + if (middle + 1 < high && + (middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) && + nsContentUtils::ComparePoints_Deprecated( + this, aEndOffset, middlePlus1->GetStartContainer(), + middlePlus1->StartOffset(), nullptr, &cache) > 0) { + result = 1; + // if node start < end of middle - 1, result = -1 + } else if (middle >= 1 && + (middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) && + nsContentUtils::ComparePoints_Deprecated( + this, aStartOffset, middleMinus1->GetEndContainer(), + middleMinus1->EndOffset(), nullptr, &cache) < 0) { + result = -1; + } else { + break; + } + } + + if (result < 0) { + high = middle; + } else { + low = middle + 1; + } + } + } + + return false; +} + +Element* nsINode::GetAnonymousRootElementOfTextEditor( + TextEditor** aTextEditor) { + if (aTextEditor) { + *aTextEditor = nullptr; + } + RefPtr textControlElement; + if (IsInNativeAnonymousSubtree()) { + textControlElement = TextControlElement::FromNodeOrNull( + GetClosestNativeAnonymousSubtreeRootParentOrHost()); + } else { + textControlElement = TextControlElement::FromNode(this); + } + if (!textControlElement) { + return nullptr; + } + RefPtr textEditor = textControlElement->GetTextEditor(); + if (!textEditor) { + // The found `TextControlElement` may be an input element which is not a + // text control element. In this case, such element must not be in a + // native anonymous tree of a `TextEditor` so this node is not in any + // `TextEditor`. + return nullptr; + } + + Element* rootElement = textEditor->GetRoot(); + if (aTextEditor) { + textEditor.forget(aTextEditor); + } + return rootElement; +} + +void nsINode::QueueDevtoolsAnonymousEvent(bool aIsRemove) { + MOZ_ASSERT(IsRootOfNativeAnonymousSubtree()); + MOZ_ASSERT(OwnerDoc()->DevToolsAnonymousAndShadowEventsEnabled()); + AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher( + this, aIsRemove ? u"anonymousrootremoved"_ns : u"anonymousrootcreated"_ns, + CanBubble::eYes, ChromeOnlyDispatch::eYes, Composed::eYes); + dispatcher->PostDOMEvent(); +} + +nsINode* nsINode::GetRootNode(const GetRootNodeOptions& aOptions) { + if (aOptions.mComposed) { + if (Document* doc = GetComposedDoc()) { + return doc; + } + + nsINode* node = this; + while (node) { + node = node->SubtreeRoot(); + ShadowRoot* shadow = ShadowRoot::FromNode(node); + if (!shadow) { + break; + } + node = shadow->GetHost(); + } + + return node; + } + + return SubtreeRoot(); +} + +nsIContent* nsINode::GetFirstChildOfTemplateOrNode() { + if (IsTemplateElement()) { + DocumentFragment* frag = static_cast(this)->Content(); + return frag->GetFirstChild(); + } + + return GetFirstChild(); +} + +nsINode* nsINode::SubtreeRoot() const { + auto RootOfNode = [](const nsINode* aStart) -> nsINode* { + const nsINode* node = aStart; + const nsINode* iter = node; + while ((iter = iter->GetParentNode())) { + node = iter; + } + return const_cast(node); + }; + + // There are four cases of interest here. nsINodes that are really: + // 1. Document nodes - Are always in the document. + // 2.a nsIContent nodes not in a shadow tree - Are either in the document, + // or mSubtreeRoot is updated in BindToTree/UnbindFromTree. + // 2.b nsIContent nodes in a shadow tree - Are never in the document, + // ignore mSubtreeRoot and return the containing shadow root. + // 4. Attr nodes - Are never in the document, and mSubtreeRoot + // is always 'this' (as set in nsINode's ctor). + nsINode* node; + if (IsInUncomposedDoc()) { + node = OwnerDocAsNode(); + } else if (IsContent()) { + ShadowRoot* containingShadow = AsContent()->GetContainingShadow(); + node = containingShadow ? containingShadow : mSubtreeRoot; + if (!node) { + NS_WARNING("Using SubtreeRoot() on unlinked element?"); + node = RootOfNode(this); + } + } else { + node = mSubtreeRoot; + } + MOZ_ASSERT(node, "Should always have a node here!"); +#ifdef DEBUG + { + const nsINode* slowNode = RootOfNode(this); + MOZ_ASSERT(slowNode == node, "These should always be in sync!"); + } +#endif + return node; +} + +static nsIContent* GetRootForContentSubtree(nsIContent* aContent) { + NS_ENSURE_TRUE(aContent, nullptr); + + // Special case for ShadowRoot because the ShadowRoot itself is + // the root. This is necessary to prevent selection from crossing + // the ShadowRoot boundary. + // + // FIXME(emilio): The NAC check should probably be done before this? We can + // have NAC inside shadow DOM. + if (ShadowRoot* containingShadow = aContent->GetContainingShadow()) { + return containingShadow; + } + if (nsIContent* nativeAnonRoot = + aContent->GetClosestNativeAnonymousSubtreeRoot()) { + return nativeAnonRoot; + } + if (Document* doc = aContent->GetUncomposedDoc()) { + return doc->GetRootElement(); + } + return nsIContent::FromNode(aContent->SubtreeRoot()); +} + +nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell) { + NS_ENSURE_TRUE(aPresShell, nullptr); + + if (IsDocument()) return AsDocument()->GetRootElement(); + if (!IsContent()) return nullptr; + + if (GetComposedDoc() != aPresShell->GetDocument()) { + return nullptr; + } + + if (AsContent()->HasIndependentSelection() || IsInNativeAnonymousSubtree()) { + // This node should be an inclusive descendant of input/textarea editor. + // In that case, the anonymous
for TextEditor should be always the + // selection root. + // FIXME: If Selection for the document is collapsed in or + //