diff --git a/dom/base/AbstractRange.cpp b/dom/base/AbstractRange.cpp index 13957561c7cd..791d00c0293d 100644 --- a/dom/base/AbstractRange.cpp +++ b/dom/base/AbstractRange.cpp @@ -12,6 +12,7 @@ #include "mozilla/RangeUtils.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/StaticRange.h" +#include "mozilla/dom/Selection.h" #include "nsContentUtils.h" #include "nsCycleCollectionParticipant.h" #include "nsGkAtoms.h" @@ -65,18 +66,73 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange) // mStart and mEnd may depend on or be depended on some other members in // concrete classes so that they should be unlinked in sub classes. NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER + tmp->mSelections.Clear(); + // Unregistering of the common inclusive ancestors would by design + // also happen when the actual implementations unlink `mStart`/`mEnd`. + // This may introduce additional overhead which is not needed when unlinking, + // therefore this is done here beforehand. + if (tmp->mRegisteredClosestCommonInclusiveAncestor) { + tmp->UnregisterClosestCommonInclusiveAncestor( + tmp->mRegisteredClosestCommonInclusiveAncestor, true); + } + MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(), + "Shouldn't be registered now that we're unlinking"); + NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +void AbstractRange::MarkDescendants(const nsINode& aNode) { + // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on + // aNode's descendants unless aNode is already marked as a range common + // ancestor or a descendant of one, in which case all of our descendants have + // the bit set already. + if (!aNode.IsMaybeSelected()) { + // don't set the Descendant bit on |aNode| itself + nsINode* node = aNode.GetNextNode(&aNode); + while (node) { + node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); + if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { + node = node->GetNextNode(&aNode); + } else { + // optimize: skip this sub-tree since it's marked already. + node = node->GetNextNonChildNode(&aNode); + } + } + } +} + +void AbstractRange::UnmarkDescendants(const nsINode& aNode) { + // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection + // on aNode's descendants unless aNode is a descendant of another range common + // ancestor. Also, exclude descendants of range common ancestors (but not the + // common ancestor itself). + if (!aNode + .IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { + // we know |aNode| doesn't have any bit set + nsINode* node = aNode.GetNextNode(&aNode); + while (node) { + node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); + if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { + node = node->GetNextNode(&aNode); + } else { + // We found an ancestor of an overlapping range, skip its descendants. + node = node->GetNextNonChildNode(&aNode); + } + } + } +} + // NOTE: If you need to change default value of members of AbstractRange, // update nsRange::Create(nsINode* aNode) and ClearForReuse() too. AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange) - : mIsPositioned(false), + : mRegisteredClosestCommonInclusiveAncestor(nullptr), + mIsPositioned(false), mIsGenerated(false), mCalledByJS(false), mIsDynamicRange(aIsDynamicRange) { @@ -206,6 +262,122 @@ nsresult AbstractRange::SetStartAndEndInternal( return NS_OK; } +bool AbstractRange::IsInSelection(const Selection& aSelection) const { + return mSelections.Contains(&aSelection); +} + +void AbstractRange::RegisterSelection(Selection& aSelection) { + if (IsInSelection(aSelection)) { + return; + } + bool isFirstSelection = mSelections.IsEmpty(); + mSelections.AppendElement(&aSelection); + if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) { + nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(); + MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); + RegisterClosestCommonInclusiveAncestor(commonAncestor); + } +} + +const nsTArray>& AbstractRange::GetSelections() const { + return mSelections; +} + +void AbstractRange::UnregisterSelection(const Selection& aSelection) { + mSelections.RemoveElement(&aSelection); + if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) { + UnregisterClosestCommonInclusiveAncestor( + mRegisteredClosestCommonInclusiveAncestor, false); + MOZ_DIAGNOSTIC_ASSERT( + !mRegisteredClosestCommonInclusiveAncestor, + "How can we have a registered common ancestor when we " + "just unregistered?"); + MOZ_DIAGNOSTIC_ASSERT( + !isInList(), + "Shouldn't be registered if we have no " + "mRegisteredClosestCommonInclusiveAncestor after unregistering"); + } +} + +void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) { + MOZ_ASSERT(aNode, "bad arg"); + + MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(), + "registering range not in selection"); + + mRegisteredClosestCommonInclusiveAncestor = aNode; + + MarkDescendants(*aNode); + + UniquePtr>& ranges = + aNode->GetClosestCommonInclusiveAncestorRangesPtr(); + if (!ranges) { + ranges = MakeUnique>(); + } + + MOZ_DIAGNOSTIC_ASSERT(!isInList()); + ranges->insertBack(this); + aNode->SetClosestCommonInclusiveAncestorForRangeInSelection(); +} + +void AbstractRange::UnregisterClosestCommonInclusiveAncestor( + nsINode* aNode, bool aIsUnlinking) { + MOZ_ASSERT(aNode, "bad arg"); + NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(), + "wrong node"); + MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor, + "wrong node"); + LinkedList* ranges = + aNode->GetExistingClosestCommonInclusiveAncestorRanges(); + MOZ_ASSERT(ranges); + + mRegisteredClosestCommonInclusiveAncestor = nullptr; + +#ifdef DEBUG + bool found = false; + for (AbstractRange* range : *ranges) { + if (range == this) { + found = true; + break; + } + } + MOZ_ASSERT(found, + "We should be in the list on our registered common ancestor"); +#endif // DEBUG + + remove(); + + // We don't want to waste time unmarking flags on nodes that are + // being unlinked anyway. + if (!aIsUnlinking && ranges->isEmpty()) { + aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection(); + UnmarkDescendants(*aNode); + } +} + +void AbstractRange::UpdateCommonAncestorIfNecessary() { + nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor; + nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor(); + if (newCommonAncestor != oldCommonAncestor) { + if (oldCommonAncestor) { + UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false); + } + if (newCommonAncestor) { + RegisterClosestCommonInclusiveAncestor(newCommonAncestor); + } else { + MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes"); + mSelections.Clear(); + MOZ_DIAGNOSTIC_ASSERT( + !mRegisteredClosestCommonInclusiveAncestor, + "How can we have a registered common ancestor when we " + "didn't register ourselves?"); + MOZ_DIAGNOSTIC_ASSERT(!isInList(), + "Shouldn't be registered if we have no " + "mRegisteredClosestCommonInclusiveAncestor"); + } + } +} + nsINode* AbstractRange::GetParentObject() const { return mOwner; } JSObject* AbstractRange::WrapObject(JSContext* aCx, diff --git a/dom/base/AbstractRange.h b/dom/base/AbstractRange.h index 50485ab8b981..c70aaf19ec8d 100644 --- a/dom/base/AbstractRange.h +++ b/dom/base/AbstractRange.h @@ -15,6 +15,7 @@ #include "mozilla/Maybe.h" #include "mozilla/RangeBoundary.h" #include "mozilla/RefPtr.h" +#include "mozilla/WeakPtr.h" #include "nsCycleCollectionParticipant.h" #include "nsISupports.h" #include "nsWrapperCache.h" @@ -26,10 +27,14 @@ class nsRange; struct JSContext; namespace mozilla::dom { -class StaticRange; class Document; +class Selection; +class StaticRange; -class AbstractRange : public nsISupports, public nsWrapperCache { +class AbstractRange : public nsISupports, + public nsWrapperCache, + // For linking together selection-associated ranges. + public mozilla::LinkedListElement { protected: explicit AbstractRange(nsINode* aNode, bool aIsDynamicRange); virtual ~AbstractRange(); @@ -106,6 +111,27 @@ class AbstractRange : public nsISupports, public nsWrapperCache { inline StaticRange* AsStaticRange(); inline const StaticRange* AsStaticRange() const; + /** + * Return true if this range is part of a Selection object + * and isn't detached. + */ + bool IsInAnySelection() const { return !mSelections.IsEmpty(); } + + MOZ_CAN_RUN_SCRIPT void RegisterSelection( + mozilla::dom::Selection& aSelection); + + void UnregisterSelection(const mozilla::dom::Selection& aSelection); + + /** + * Returns a list of all Selections the range is associated with. + */ + const nsTArray>& GetSelections() const; + + /** + * Return true if this range is in |aSelection|. + */ + bool IsInSelection(const mozilla::dom::Selection& aSelection) const; + protected: template @@ -133,6 +159,21 @@ class AbstractRange : public nsISupports, public nsWrapperCache { << (aRange.mIsDynamicRange ? "true" : "false") << " }"; } + /** + * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor + */ + void RegisterClosestCommonInclusiveAncestor(nsINode* aNode); + /** + * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor + */ + void UnregisterClosestCommonInclusiveAncestor(nsINode* aNode, + bool aIsUnlinking); + + void UpdateCommonAncestorIfNecessary(); + + static void MarkDescendants(const nsINode& aNode); + static void UnmarkDescendants(const nsINode& aNode); + private: void ClearForReuse(); @@ -140,6 +181,13 @@ class AbstractRange : public nsISupports, public nsWrapperCache { RefPtr mOwner; RangeBoundary mStart; RangeBoundary mEnd; + + // A Range can be part of multiple |Selection|s. This is a very rare use case. + AutoTArray, 1> mSelections; + // mRegisteredClosestCommonInclusiveAncestor is only non-null when the range + // IsInAnySelection(). + nsCOMPtr mRegisteredClosestCommonInclusiveAncestor; + // `true` if `mStart` and `mEnd` are set for StaticRange or set and valid // for nsRange. bool mIsPositioned; diff --git a/dom/base/Element.cpp b/dom/base/Element.cpp index f1e15e3bf749..4a8b86149fe8 100644 --- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3003,12 +3003,12 @@ void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const { static_cast(State().GetInternalValue())); fprintf(out, " flags=[%08x]", static_cast(GetFlags())); if (IsClosestCommonInclusiveAncestorForRangeInSelection()) { - const LinkedList* ranges = + const LinkedList* ranges = GetExistingClosestCommonInclusiveAncestorRanges(); int32_t count = 0; if (ranges) { // Can't use range-based iteration on a const LinkedList, unfortunately. - for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) { + for (const AbstractRange* r = ranges->getFirst(); r; r = r->getNext()) { ++count; } } diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 9cafd6b486f5..71b9ba86481a 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -1234,9 +1234,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( // Remove all the overlapping ranges for (size_t i = startIndex; i < endIndex; ++i) { - if (mRanges[i].mRange->IsDynamicRange()) { - mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection); - } + mRanges[i].mRange->UnregisterSelection(mSelection); } mRanges.RemoveElementsAt(startIndex, endIndex - startIndex); @@ -1287,9 +1285,8 @@ nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection( if (idx < 0) return NS_ERROR_DOM_NOT_FOUND_ERR; mRanges.RemoveElementAt(idx); - if (aRange.IsDynamicRange()) { - aRange.AsDynamicRange()->UnregisterSelection(mSelection); - } + aRange.UnregisterSelection(mSelection); + return NS_OK; } nsresult Selection::RemoveCollapsedRanges() { @@ -1987,9 +1984,7 @@ void Selection::SetAncestorLimiter(nsIContent* aLimiter) { void Selection::StyledRanges::UnregisterSelection() { uint32_t count = mRanges.Length(); for (uint32_t i = 0; i < count; ++i) { - if (mRanges[i].mRange->IsDynamicRange()) { - mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection); - } + mRanges[i].mRange->UnregisterSelection(mSelection); } } @@ -2237,10 +2232,8 @@ void Selection::AddHighlightRangeAndSelectFramesAndNotifyListeners( MOZ_ASSERT(mSelectionType == SelectionType::eHighlight); mStyledRanges.mRanges.AppendElement(StyledRange{&aRange}); - if (aRange.IsDynamicRange()) { - RefPtr range = aRange.AsDynamicRange(); - range->RegisterSelection(*this); - } + aRange.RegisterSelection(*this); + if (!mFrameSelection) { return; // nothing to do } diff --git a/dom/base/StaticRange.cpp b/dom/base/StaticRange.cpp index 1197cd11ead3..c5dc3cc80620 100644 --- a/dom/base/StaticRange.cpp +++ b/dom/base/StaticRange.cpp @@ -90,14 +90,25 @@ already_AddRefed StaticRange::Create( return staticRange.forget(); } +StaticRange::~StaticRange() { + DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr); +} + template void StaticRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, const RangeBoundaryBase& aEndBoundary, nsINode* aRootNode) { + bool checkCommonAncestor = + IsInAnySelection() && (mStart.Container() != aStartBoundary.Container() || + mEnd.Container() != aEndBoundary.Container()); mStart.CopyFrom(aStartBoundary, RangeBoundaryIsMutationObserved::No); mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::No); MOZ_ASSERT(mStart.IsSet() == mEnd.IsSet()); mIsPositioned = mStart.IsSet() && mEnd.IsSet(); + + if (checkCommonAncestor) { + UpdateCommonAncestorIfNecessary(); + } } /* static */ diff --git a/dom/base/StaticRange.h b/dom/base/StaticRange.h index 1ed90d040864..92fcb492248a 100644 --- a/dom/base/StaticRange.h +++ b/dom/base/StaticRange.h @@ -75,7 +75,7 @@ class StaticRange final : public AbstractRange { protected: explicit StaticRange(nsINode* aNode) : AbstractRange(aNode, /* aIsDynamicRange = */ false) {} - virtual ~StaticRange() = default; + virtual ~StaticRange(); public: NS_DECL_ISUPPORTS_INHERITED diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index f95fd2bef1f3..1f18279ece1e 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -345,12 +345,12 @@ bool nsINode::IsSelected(const uint32_t aStartOffset, nsTHashSet ancestorSelections; for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection( n->GetParentNode())) { - const LinkedList* ranges = + const LinkedList* ranges = n->GetExistingClosestCommonInclusiveAncestorRanges(); if (!ranges) { continue; } - for (const nsRange* range : *ranges) { + for (const AbstractRange* range : *ranges) { MOZ_ASSERT(range->IsInAnySelection(), "Why is this range registered with a node?"); // Looks like that IsInSelection() assert fails sometimes... diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index 6d1cc9dda3a1..be77a5e084b1 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -76,6 +76,7 @@ inline bool IsSpaceCharacter(char aChar) { return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' || aChar == '\f'; } +class AbstractRange; class AccessibleNode; template class AncestorsOfTypeIterator; @@ -1350,7 +1351,7 @@ class nsINode : public mozilla::dom::EventTarget { * pushing DOMSlots up to the next allocation bucket size, at the cost of * some complexity. */ - mozilla::UniquePtr> + mozilla::UniquePtr> mClosestCommonInclusiveAncestorRanges; }; @@ -2211,7 +2212,7 @@ class nsINode : public mozilla::dom::EventTarget { /** * See nsSlots::mClosestCommonInclusiveAncestorRanges. */ - const mozilla::LinkedList* + const mozilla::LinkedList* GetExistingClosestCommonInclusiveAncestorRanges() const { if (!HasSlots()) { return nullptr; @@ -2222,7 +2223,7 @@ class nsINode : public mozilla::dom::EventTarget { /** * See nsSlots::mClosestCommonInclusiveAncestorRanges. */ - mozilla::LinkedList* + mozilla::LinkedList* GetExistingClosestCommonInclusiveAncestorRanges() { if (!HasSlots()) { return nullptr; @@ -2233,7 +2234,7 @@ class nsINode : public mozilla::dom::EventTarget { /** * See nsSlots::mClosestCommonInclusiveAncestorRanges. */ - mozilla::UniquePtr>& + mozilla::UniquePtr>& GetClosestCommonInclusiveAncestorRangesPtr() { return Slots()->mClosestCommonInclusiveAncestorRanges; } diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 172b02a18984..bbeda20207b2 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -166,7 +166,6 @@ nsRange::~nsRange() { nsRange::nsRange(nsINode* aNode) : AbstractRange(aNode, /* aIsDynamicRange = */ true), - mRegisteredClosestCommonInclusiveAncestor(nullptr), mNextStartRef(nullptr), mNextEndRef(nullptr) { // printf("Size of nsRange: %zu\n", sizeof(nsRange)); @@ -218,19 +217,8 @@ NS_INTERFACE_MAP_END_INHERITING(AbstractRange) NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange) - tmp->mSelections.Clear(); - // We _could_ just rely on Reset() to - // UnregisterClosestCommonInclusiveAncestor(), but it wouldn't know we're - // calling it from Unlink and so would do more work than it really needs to. - if (tmp->mRegisteredClosestCommonInclusiveAncestor) { - tmp->UnregisterClosestCommonInclusiveAncestor( - tmp->mRegisteredClosestCommonInclusiveAncestor, true); - } - + // `Reset()` unlinks `mStart`, `mEnd` and `mRoot`. tmp->Reset(); - - MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(), - "Shouldn't be registered now that we're unlinking"); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange, AbstractRange) @@ -246,103 +234,6 @@ bool nsRange::MaybeInterruptLastRelease() { return interrupt; } -static void MarkDescendants(nsINode* aNode) { - // Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on - // aNode's descendants unless aNode is already marked as a range common - // ancestor or a descendant of one, in which case all of our descendants have - // the bit set already. - if (!aNode->IsMaybeSelected()) { - // don't set the Descendant bit on |aNode| itself - nsINode* node = aNode->GetNextNode(aNode); - while (node) { - node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); - if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { - node = node->GetNextNode(aNode); - } else { - // optimize: skip this sub-tree since it's marked already. - node = node->GetNextNonChildNode(aNode); - } - } - } -} - -static void UnmarkDescendants(nsINode* aNode) { - // Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection - // on aNode's descendants unless aNode is a descendant of another range common - // ancestor. Also, exclude descendants of range common ancestors (but not the - // common ancestor itself). - if (!aNode - ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { - // we know |aNode| doesn't have any bit set - nsINode* node = aNode->GetNextNode(aNode); - while (node) { - node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); - if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) { - node = node->GetNextNode(aNode); - } else { - // We found an ancestor of an overlapping range, skip its descendants. - node = node->GetNextNonChildNode(aNode); - } - } - } -} - -void nsRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) { - MOZ_ASSERT(aNode, "bad arg"); - - MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(), - "registering range not in selection"); - - mRegisteredClosestCommonInclusiveAncestor = aNode; - - MarkDescendants(aNode); - - UniquePtr>& ranges = - aNode->GetClosestCommonInclusiveAncestorRangesPtr(); - if (!ranges) { - ranges = MakeUnique>(); - } - - MOZ_DIAGNOSTIC_ASSERT(!isInList()); - ranges->insertBack(this); - aNode->SetClosestCommonInclusiveAncestorForRangeInSelection(); -} - -void nsRange::UnregisterClosestCommonInclusiveAncestor(nsINode* aNode, - bool aIsUnlinking) { - MOZ_ASSERT(aNode, "bad arg"); - NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(), - "wrong node"); - MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor, - "wrong node"); - LinkedList* ranges = - aNode->GetExistingClosestCommonInclusiveAncestorRanges(); - MOZ_ASSERT(ranges); - - mRegisteredClosestCommonInclusiveAncestor = nullptr; - -#ifdef DEBUG - bool found = false; - for (nsRange* range : *ranges) { - if (range == this) { - found = true; - break; - } - } - MOZ_ASSERT(found, - "We should be in the list on our registered common ancestor"); -#endif // DEBUG - - remove(); - - // We don't want to waste time unmarking flags on nodes that are - // being unlinked anyway. - if (!aIsUnlinking && ranges->isEmpty()) { - aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection(); - UnmarkDescendants(aNode); - } -} - void nsRange::AdjustNextRefsOnCharacterDataSplit( const nsIContent& aContent, const CharacterDataChangeInfo& aInfo) { // If the splitted text node is immediately before a range boundary point @@ -594,7 +485,7 @@ void nsRange::ContentAppended(nsIContent* aFirstNewContent) { while (child) { if (!child ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { - MarkDescendants(child); + MarkDescendants(*child); child ->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); } @@ -646,7 +537,7 @@ void nsRange::ContentInserted(nsIContent* aChild) { if (container->IsMaybeSelected() && !aChild ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { - MarkDescendants(aChild); + MarkDescendants(*aChild); aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); } @@ -739,7 +630,7 @@ void nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling) { ->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) { aChild ->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection(); - UnmarkDescendants(aChild); + UnmarkDescendants(*aChild); } } @@ -985,26 +876,7 @@ void nsRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::Yes); if (checkCommonAncestor) { - nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor; - nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor(); - if (newCommonAncestor != oldCommonAncestor) { - if (oldCommonAncestor) { - UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false); - } - if (newCommonAncestor) { - RegisterClosestCommonInclusiveAncestor(newCommonAncestor); - } else { - MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes"); - mSelections.Clear(); - MOZ_DIAGNOSTIC_ASSERT( - !mRegisteredClosestCommonInclusiveAncestor, - "How can we have a registered common ancestor when we " - "didn't register ourselves?"); - MOZ_DIAGNOSTIC_ASSERT(!isInList(), - "Shouldn't be registered if we have no " - "mRegisteredClosestCommonInclusiveAncestor"); - } - } + UpdateCommonAncestorIfNecessary(); } // This needs to be the last thing this function does, other than notifying @@ -1034,43 +906,6 @@ void nsRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, } } -bool nsRange::IsInSelection(const Selection& aSelection) const { - return mSelections.Contains(&aSelection); -} - -void nsRange::RegisterSelection(Selection& aSelection) { - if (IsInSelection(aSelection)) { - return; - } - bool isFirstSelection = mSelections.IsEmpty(); - mSelections.AppendElement(&aSelection); - if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) { - nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(); - MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); - RegisterClosestCommonInclusiveAncestor(commonAncestor); - } -} - -const nsTArray>& nsRange::GetSelections() const { - return mSelections; -} - -void nsRange::UnregisterSelection(Selection& aSelection) { - mSelections.RemoveElement(&aSelection); - if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) { - UnregisterClosestCommonInclusiveAncestor( - mRegisteredClosestCommonInclusiveAncestor, false); - MOZ_DIAGNOSTIC_ASSERT( - !mRegisteredClosestCommonInclusiveAncestor, - "How can we have a registered common ancestor when we " - "just unregistered?"); - MOZ_DIAGNOSTIC_ASSERT( - !isInList(), - "Shouldn't be registered if we have no " - "mRegisteredClosestCommonInclusiveAncestor after unregistering"); - } -} - void nsRange::Reset() { DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr); } diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 55a9cc3b4c86..97756d3afc37 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -20,7 +20,6 @@ #include "mozilla/ErrorResult.h" #include "mozilla/RangeBoundary.h" #include "mozilla/RefPtr.h" -#include "mozilla/WeakPtr.h" namespace mozilla { class RectCallback; @@ -36,9 +35,7 @@ class Selection; } // namespace mozilla class nsRange final : public mozilla::dom::AbstractRange, - public nsStubMutationObserver, - // For linking together selection-associated ranges. - public mozilla::LinkedListElement { + public nsStubMutationObserver { using ErrorResult = mozilla::ErrorResult; using AbstractRange = mozilla::dom::AbstractRange; using DocGroup = mozilla::dom::DocGroup; @@ -91,28 +88,6 @@ class nsRange final : public mozilla::dom::AbstractRange, nsINode* GetRoot() const { return mRoot; } - /** - * Return true if this range is part of a Selection object - * and isn't detached. - */ - bool IsInAnySelection() const { return !mSelections.IsEmpty(); } - - MOZ_CAN_RUN_SCRIPT void RegisterSelection( - mozilla::dom::Selection& aSelection); - - void UnregisterSelection(mozilla::dom::Selection& aSelection); - - /** - * Returns a list of all Selections the range is associated with. - */ - const nsTArray>& GetSelections() - const; - - /** - * Return true if this range is in |aSelection|. - */ - bool IsInSelection(const mozilla::dom::Selection& aSelection) const; - /** * Return true if this range was generated. * @see SetIsGenerated @@ -377,16 +352,6 @@ class nsRange final : public mozilla::dom::AbstractRange, nsINode* GetRegisteredClosestCommonInclusiveAncestor(); protected: - /** - * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor - */ - void RegisterClosestCommonInclusiveAncestor(nsINode* aNode); - /** - * https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor - */ - void UnregisterClosestCommonInclusiveAncestor(nsINode* aNode, - bool aIsUnlinking); - /** * DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets * mStart and mEnd, or some other internal methods modify `mStart` and/or @@ -448,13 +413,6 @@ class nsRange final : public mozilla::dom::AbstractRange, #endif // #ifdef DEBUG nsCOMPtr mRoot; - // mRegisteredClosestCommonInclusiveAncestor is only non-null when the range - // IsInAnySelection(). It's kept alive via mStart/mEnd, - // because we update it any time those could become disconnected from it. - nsINode* MOZ_NON_OWNING_REF mRegisteredClosestCommonInclusiveAncestor; - - // A Range can be part of multiple |Selection|s. This is a very rare use case. - AutoTArray, 1> mSelections; // These raw pointers are used to remember a child that is about // to be inserted between a CharacterData call and a subsequent diff --git a/dom/base/nsTextNode.cpp b/dom/base/nsTextNode.cpp index 526f415c9cd0..f21a332012c1 100644 --- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -133,15 +133,9 @@ void nsTextNode::List(FILE* out, int32_t aIndent) const { fprintf(out, "Text@%p", static_cast(this)); fprintf(out, " flags=[%08x]", static_cast(GetFlags())); if (IsClosestCommonInclusiveAncestorForRangeInSelection()) { - const LinkedList* ranges = + const LinkedList* ranges = GetExistingClosestCommonInclusiveAncestorRanges(); - int32_t count = 0; - if (ranges) { - // Can't use range-based iteration on a const LinkedList, unfortunately. - for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) { - ++count; - } - } + uint32_t count = ranges ? ranges->length() : 0; fprintf(out, " ranges:%d", count); } fprintf(out, " primaryframe=%p", static_cast(GetPrimaryFrame())); diff --git a/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html.ini b/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html.ini deleted file mode 100644 index d69fae88f9a2..000000000000 --- a/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-002.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[custom-highlight-painting-staticrange-002.html] - expected: FAIL diff --git a/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html.ini b/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html.ini deleted file mode 100644 index 081fd33e2f66..000000000000 --- a/testing/web-platform/meta/css/css-highlight-api/painting/custom-highlight-painting-staticrange-003.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[custom-highlight-painting-staticrange-003.html] - expected: FAIL