diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 62a00caad7df..e57a2c021a71 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -966,11 +966,10 @@ nsresult Selection::AddRangesForUserSelectableNodes( GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1; for (size_t i = 0; i < rangesToAdd.Length(); ++i) { Maybe index; - const RefPtr selection{this}; // `MOZ_KnownLive` needed because of broken static analysis // (https://bugzilla.mozilla.org/show_bug.cgi?id=1622253#c1). nsresult rv = mStyledRanges.MaybeAddRangeAndTruncateOverlaps( - MOZ_KnownLive(rangesToAdd[i]), &index, *selection); + MOZ_KnownLive(rangesToAdd[i]), &index); NS_ENSURE_SUCCESS(rv, rv); if (i == newAnchorFocusIndex) { *aOutIndex = index; @@ -1007,13 +1006,11 @@ nsresult Selection::AddRangesForSelectableNodes( aDispatchSelectstartEvent); } - const RefPtr selection{this}; - return mStyledRanges.MaybeAddRangeAndTruncateOverlaps(aRange, aOutIndex, - *selection); + return mStyledRanges.MaybeAddRangeAndTruncateOverlaps(aRange, aOutIndex); } nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( - nsRange* aRange, Maybe* aOutIndex, Selection& aSelection) { + nsRange* aRange, Maybe* aOutIndex) { MOZ_ASSERT(aRange); MOZ_ASSERT(aRange->IsPositioned()); MOZ_ASSERT(aOutIndex); @@ -1024,7 +1021,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( // XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier. mRanges.AppendElement(StyledRange(aRange)); - aRange->RegisterSelection(aSelection); + aRange->RegisterSelection(MOZ_KnownLive(mSelection)); aOutIndex->emplace(0u); return NS_OK; @@ -1064,7 +1061,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( // XXX(Bug 1631371) Check if this should use a fallible operation as it // pretended earlier. mRanges.InsertElementAt(startIndex, StyledRange(aRange)); - aRange->RegisterSelection(aSelection); + aRange->RegisterSelection(MOZ_KnownLive(mSelection)); aOutIndex->emplace(startIndex); return NS_OK; } @@ -1083,7 +1080,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( // Remove all the overlapping ranges for (size_t i = startIndex; i < endIndex; ++i) { - mRanges[i].mRange->UnregisterSelection(); + mRanges[i].mRange->UnregisterSelection(mSelection); } mRanges.RemoveElementsAt(startIndex, endIndex - startIndex); @@ -1105,7 +1102,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps( mRanges.InsertElementsAt(startIndex, temp); for (uint32_t i = 0; i < temp.Length(); ++i) { - MOZ_KnownLive(temp[i].mRange)->RegisterSelection(aSelection); + MOZ_KnownLive(temp[i].mRange)->RegisterSelection(MOZ_KnownLive(mSelection)); // `MOZ_KnownLive` is required because of // https://bugzilla.mozilla.org/show_bug.cgi?id=1622253. } @@ -1131,7 +1128,7 @@ nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection( if (idx < 0) return NS_ERROR_DOM_NOT_FOUND_ERR; mRanges.RemoveElementAt(idx); - aRange.UnregisterSelection(); + aRange.UnregisterSelection(mSelection); return NS_OK; } nsresult Selection::RemoveCollapsedRanges() { @@ -1470,8 +1467,8 @@ nsresult Selection::SelectFramesOfInclusiveDescendantsOfContent( void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) { for (size_t i = 0; i < mStyledRanges.Length(); ++i) { nsRange* range = mStyledRanges.mRanges[i].mRange; - MOZ_ASSERT(range->IsInSelection()); - SelectFrames(aPresContext, range, range->IsInSelection()); + MOZ_ASSERT(range->IsInAnySelection()); + SelectFrames(aPresContext, range, range->IsInAnySelection()); } } @@ -1789,7 +1786,7 @@ void Selection::SetAncestorLimiter(nsIContent* aLimiter) { void Selection::StyledRanges::UnregisterSelection() { uint32_t count = mRanges.Length(); for (uint32_t i = 0; i < count; ++i) { - mRanges[i].mRange->UnregisterSelection(); + mRanges[i].mRange->UnregisterSelection(mSelection); } } @@ -1946,19 +1943,16 @@ void Selection::AddRangeAndSelectFramesAndNotifyListeners(nsRange& aRange, void Selection::AddRangeAndSelectFramesAndNotifyListeners(nsRange& aRange, Document* aDocument, ErrorResult& aRv) { - // If the given range is part of another Selection, we need to clone the - // range first. - RefPtr range; - if (aRange.IsInSelection()) { - // If we already have the range, we don't need to handle this. - if (aRange.GetSelection() == this) { + RefPtr range = &aRange; + if (aRange.IsInAnySelection()) { + if (aRange.IsInSelection(*this)) { + // If we already have the range, we don't need to handle this. return; } - // Because of performance reason, when there is a cached range, let's use - // it. Otherwise, clone the range. - range = aRange.CloneRange(); - } else { - range = &aRange; + if (mSelectionType != SelectionType::eNormal && + mSelectionType != SelectionType::eHighlight) { + range = aRange.CloneRange(); + } } nsINode* rangeRoot = range->GetRoot(); diff --git a/dom/base/Selection.h b/dom/base/Selection.h index b5537875401b..9d42f88583be 100644 --- a/dom/base/Selection.h +++ b/dom/base/Selection.h @@ -814,6 +814,7 @@ class Selection final : public nsSupportsWeakReference, void Disconnect(); struct StyledRanges { + explicit StyledRanges(Selection& aSelection) : mSelection(aSelection) {} void Clear(); StyledRange* FindRangeData(nsRange* aRange); @@ -871,8 +872,8 @@ class Selection final : public nsSupportsWeakReference, * it. Hence it'll always be in [0, mRanges.Length()). * This is nothing only when the method returns an error. */ - MOZ_CAN_RUN_SCRIPT nsresult MaybeAddRangeAndTruncateOverlaps( - nsRange* aRange, Maybe* aOutIndex, Selection& aSelection); + MOZ_CAN_RUN_SCRIPT nsresult + MaybeAddRangeAndTruncateOverlaps(nsRange* aRange, Maybe* aOutIndex); /** * GetCommonEditingHost() returns common editing host of all @@ -929,9 +930,11 @@ class Selection final : public nsSupportsWeakReference, // a possible solution, allowing the calculation of the overlap interval in // O(log n) time, though this would require rebalancing and other overhead. Elements mRanges; + + Selection& mSelection; }; - StyledRanges mStyledRanges; + StyledRanges mStyledRanges{*this}; RefPtr mAnchorFocusRange; RefPtr mFrameSelection; diff --git a/dom/base/nsINode.cpp b/dom/base/nsINode.cpp index 047669205fe9..925a2ea3f616 100644 --- a/dom/base/nsINode.cpp +++ b/dom/base/nsINode.cpp @@ -343,7 +343,6 @@ bool nsINode::IsSelected(const uint32_t aStartOffset, // Collect the selection objects for potential ranges. nsTHashSet ancestorSelections; - Selection* prevSelection = nullptr; for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection( n->GetParentNode())) { const LinkedList* ranges = @@ -352,14 +351,12 @@ bool nsINode::IsSelected(const uint32_t aStartOffset, continue; } for (const nsRange* range : *ranges) { - MOZ_ASSERT(range->IsInSelection(), - "Why is this range registeed with a node?"); + MOZ_ASSERT(range->IsInAnySelection(), + "Why is this range registered with a node?"); // Looks like that IsInSelection() assert fails sometimes... - if (range->IsInSelection()) { - Selection* selection = range->GetSelection(); - if (prevSelection != selection) { - prevSelection = selection; - ancestorSelections.Insert(selection); + if (range->IsInAnySelection()) { + for (const auto* selectionWrapper : range->GetSelections()) { + ancestorSelections.Insert(selectionWrapper->Get()); } } } diff --git a/dom/base/nsRange.cpp b/dom/base/nsRange.cpp index 2a57c558d098..9783a835bceb 100644 --- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -50,6 +50,11 @@ using namespace mozilla; using namespace mozilla::dom; +SelectionListWrapper::SelectionListWrapper(Selection* aSelection) + : mSelection(aSelection) {} +NS_IMPL_CYCLE_COLLECTION(SelectionListWrapper) +Selection* SelectionListWrapper::Get() const { return mSelection; } + template already_AddRefed nsRange::Create( const RangeBoundary& aStartBoundary, const RangeBoundary& aEndBoundary, ErrorResult& aRv); @@ -129,7 +134,7 @@ static void InvalidateAllFrames(nsINode* aNode) { nsTArray>* nsRange::sCachedRanges = nullptr; nsRange::~nsRange() { - NS_ASSERTION(!IsInSelection(), "deleting nsRange that is in use"); + NS_ASSERTION(!IsInAnySelection(), "deleting nsRange that is in use"); // we want the side effects (releases and list removals) DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr); @@ -141,7 +146,8 @@ nsRange::nsRange(nsINode* aNode) mNextStartRef(nullptr), mNextEndRef(nullptr) { // printf("Size of nsRange: %zu\n", sizeof(nsRange)); - static_assert(sizeof(nsRange) <= 208, + + static_assert(sizeof(nsRange) <= 216, "nsRange size shouldn't be increased as far as possible"); } @@ -188,6 +194,7 @@ NS_INTERFACE_MAP_END_INHERITING(AbstractRange) NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelections); // 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. @@ -204,6 +211,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange, AbstractRange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelections) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange, AbstractRange) @@ -259,7 +267,8 @@ static void UnmarkDescendants(nsINode* aNode) { void nsRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) { MOZ_ASSERT(aNode, "bad arg"); - MOZ_DIAGNOSTIC_ASSERT(IsInSelection(), "registering range not in selection"); + MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(), + "registering range not in selection"); mRegisteredClosestCommonInclusiveAncestor = aNode; @@ -437,7 +446,7 @@ void nsRange::CharacterDataChanged(nsIContent* aContent, } bool isCommonAncestor = - IsInSelection() && mStart.Container() == mEnd.Container(); + IsInAnySelection() && mStart.Container() == mEnd.Container(); if (isCommonAncestor) { UnregisterClosestCommonInclusiveAncestor(mStart.Container(), false); RegisterClosestCommonInclusiveAncestor(newStart.Container()); @@ -489,7 +498,7 @@ void nsRange::CharacterDataChanged(nsIContent* aContent, newEnd = {aInfo.mDetails->mNextSibling, newEndOffset}; bool isCommonAncestor = - IsInSelection() && mStart.Container() == mEnd.Container(); + IsInAnySelection() && mStart.Container() == mEnd.Container(); if (isCommonAncestor && !newStart.Container()) { // The split occurs inside the range. UnregisterClosestCommonInclusiveAncestor(mStart.Container(), false); @@ -557,7 +566,7 @@ void nsRange::ContentAppended(nsIContent* aFirstNewContent) { nsINode* container = aFirstNewContent->GetParentNode(); MOZ_ASSERT(container); - if (container->IsMaybeSelected() && IsInSelection()) { + if (container->IsMaybeSelected() && IsInAnySelection()) { nsINode* child = aFirstNewContent; while (child) { if (!child @@ -840,18 +849,60 @@ bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) { return false; } +/** + * @brief Helper class that creates a local copy of `nsRange::mSelections`. + * + * This class uses the RAII principle to create a local copy of + * `nsRange::mSelections`, which is safely iterable while modifications may + * occur on the original. + * When going out of scope, the local copy is being deleted. + */ +class MOZ_RAII SelectionListLocalCopy final { + public: + explicit SelectionListLocalCopy( + mozilla::LinkedList>& aSelectionList) { + for (const auto* elem : aSelectionList) { + mSelectionList.insertBack(new SelectionListWrapper(elem->Get())); + } + } + + mozilla::LinkedList>& Get() { + return mSelectionList; + } + + ~SelectionListLocalCopy() { mSelectionList.clear(); } + + private: + mozilla::LinkedList> mSelectionList; +}; + void nsRange::NotifySelectionListenersAfterRangeSet() { - if (mSelection) { + if (!mSelections.isEmpty()) { // Our internal code should not move focus with using this instance while // it's calling Selection::NotifySelectionListeners() which may move focus // or calls selection listeners. So, let's set mCalledByJS to false here // since non-*JS() methods don't set it to false. AutoCalledByJSRestore calledByJSRestorer(*this); mCalledByJS = false; - // Be aware, this range may be modified or stop being a range for selection - // after this call. Additionally, the selection instance may have gone. - RefPtr selection = mSelection.get(); - selection->NotifySelectionListeners(calledByJSRestorer.SavedValue()); + + // Notify all Selections. This may modify the range, + // remove it from the selection, or the selection itself may have gone after + // the call. Also, new selections may be added. + // To ensure that listeners are notified for all *current* selections, + // create a copy of the list of selections and use that for iterating. This + // way selections can be added or removed safely during iteration. + // To save allocation cost, the copy is only created if there is more than + // one Selection present (which will barely ever be the case). + if (mSelections.getFirst() != mSelections.getLast()) { + SelectionListLocalCopy copiedSelections{mSelections}; + for (const auto* selectionWrapper : copiedSelections.Get()) { + RefPtr selection = selectionWrapper->Get(); + selection->NotifySelectionListeners(calledByJSRestorer.SavedValue()); + } + } else { + RefPtr selection = mSelections.getFirst()->Get(); + selection->NotifySelectionListeners(calledByJSRestorer.SavedValue()); + } } } @@ -929,7 +980,7 @@ void nsRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, bool checkCommonAncestor = (mStart.Container() != aStartBoundary.Container() || mEnd.Container() != aEndBoundary.Container()) && - IsInSelection() && !aNotInsertedYet; + IsInAnySelection() && !aNotInsertedYet; // GetClosestCommonInclusiveAncestor is unreliable while we're unlinking // (could return null if our start/end have already been unlinked), so make @@ -948,7 +999,7 @@ void nsRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, RegisterClosestCommonInclusiveAncestor(newCommonAncestor); } else { MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes"); - mSelection = nullptr; + mSelections.clear(); MOZ_DIAGNOSTIC_ASSERT( !mRegisteredClosestCommonInclusiveAncestor, "How can we have a registered common ancestor when we " @@ -970,42 +1021,48 @@ void nsRange::DoSetRange(const RangeBoundaryBase& aStartBoundary, // the world could be observed by a selection listener while the range was in // an invalid state. So we run it off of a script runner to ensure it runs // after the mutation observers have finished running. - if (mSelection) { + if (!mSelections.isEmpty()) { nsContentUtils::AddScriptRunner( NewRunnableMethod("NotifySelectionListenersAfterRangeSet", this, &nsRange::NotifySelectionListenersAfterRangeSet)); } } -void nsRange::RegisterSelection(Selection& aSelection) { - // A range can belong to at most one Selection instance. - MOZ_ASSERT(!mSelection); - - if (mSelection == &aSelection) { - return; +bool nsRange::IsInSelection(const Selection& aSelection) const { + for (const auto* selectionWrapper : mSelections) { + if (selectionWrapper->Get() == &aSelection) { + return true; + } } - - // Extra step in case our parent failed to ensure the above precondition. - if (mSelection) { - const RefPtr range{this}; - const RefPtr selection{mSelection}; - selection->RemoveRangeAndUnselectFramesAndNotifyListeners(*range, - IgnoreErrors()); - } - - mSelection = &aSelection; - - nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(); - MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); - RegisterClosestCommonInclusiveAncestor(commonAncestor); + return false; } -Selection* nsRange::GetSelection() const { return mSelection; } +void nsRange::RegisterSelection(Selection& aSelection) { + if (IsInSelection(aSelection)) { + return; + } + bool isFirstSelection = mSelections.isEmpty(); + mSelections.insertBack(new SelectionListWrapper(&aSelection)); + if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) { + nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(); + MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes"); + RegisterClosestCommonInclusiveAncestor(commonAncestor); + } +} -void nsRange::UnregisterSelection() { - mSelection = nullptr; +const mozilla::LinkedList>& +nsRange::GetSelections() const { + return mSelections; +} - if (mRegisteredClosestCommonInclusiveAncestor) { +void nsRange::UnregisterSelection(Selection& aSelection) { + for (auto* selectionWrapper : mSelections) { + if (selectionWrapper->Get() == &aSelection) { + selectionWrapper->remove(); + break; + } + } + if (mSelections.isEmpty() && mRegisteredClosestCommonInclusiveAncestor) { UnregisterClosestCommonInclusiveAncestor( mRegisteredClosestCommonInclusiveAncestor, false); MOZ_DIAGNOSTIC_ASSERT( @@ -2948,7 +3005,7 @@ nsresult nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList& aResult, } nsINode* nsRange::GetRegisteredClosestCommonInclusiveAncestor() { - MOZ_ASSERT(IsInSelection(), + MOZ_ASSERT(IsInAnySelection(), "GetRegisteredClosestCommonInclusiveAncestor only valid for range " "in selection"); MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor); @@ -2970,7 +3027,7 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() { // with selections, ranges, etc. But if it still is, we should check whether // we have a different common ancestor now, and if so invalidate its subtree // so it paints the selection it's in now. - if (mRange->IsInSelection()) { + if (mRange->IsInAnySelection()) { nsINode* commonAncestor = mRange->GetRegisteredClosestCommonInclusiveAncestor(); // XXXbz can commonAncestor really be null here? I wouldn't think so! If diff --git a/dom/base/nsRange.h b/dom/base/nsRange.h index 134fa42db6be..3a53cc5f545e 100644 --- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -20,6 +20,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/LinkedList.h" #include "mozilla/RangeBoundary.h" +#include "mozilla/RefPtr.h" #include "mozilla/WeakPtr.h" namespace mozilla { @@ -33,6 +34,26 @@ class DOMRectList; class InspectorFontFace; class Selection; } // namespace dom +/** + * @brief Wrapper class to allow storing a |Selection| in a |LinkedList|. + * + * This helper allows an |nsRange| to store all |Selection|s associated with it + * in a |mozilla::LinkedList|. + */ +class SelectionListWrapper + : public LinkedListElement> { + NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(SelectionListWrapper) + NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(SelectionListWrapper) + public: + explicit SelectionListWrapper(dom::Selection* aSelection); + + /// Returns the stored |Selection|. + dom::Selection* Get() const; + + private: + ~SelectionListWrapper() = default; + WeakPtr mSelection; +}; } // namespace mozilla class nsRange final : public mozilla::dom::AbstractRange, @@ -92,20 +113,26 @@ class nsRange final : public mozilla::dom::AbstractRange, nsINode* GetRoot() const { return mRoot; } /** - * Return true iff this range is part of a Selection object + * Return true if this range is part of a Selection object * and isn't detached. */ - bool IsInSelection() const { return !!mSelection; } + bool IsInAnySelection() const { return !mSelections.isEmpty(); } MOZ_CAN_RUN_SCRIPT void RegisterSelection( mozilla::dom::Selection& aSelection); - void UnregisterSelection(); + void UnregisterSelection(mozilla::dom::Selection& aSelection); /** - * Returns pointer to a Selection if the range is associated with a Selection. + * Returns a list of all Selections the range is associated with. */ - mozilla::dom::Selection* GetSelection() const; + const mozilla::LinkedList>& + 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. @@ -415,7 +442,7 @@ class nsRange final : public mozilla::dom::AbstractRange, struct MOZ_STACK_CLASS AutoInvalidateSelection { explicit AutoInvalidateSelection(nsRange* aRange) : mRange(aRange) { - if (!mRange->IsInSelection() || sIsNested) { + if (!mRange->IsInAnySelection() || sIsNested) { return; } sIsNested = true; @@ -432,16 +459,18 @@ class nsRange final : public mozilla::dom::AbstractRange, #ifdef DEBUG bool IsCleared() const { return !mRoot && !mRegisteredClosestCommonInclusiveAncestor && - !mSelection && !mNextStartRef && !mNextEndRef; + mSelections.isEmpty() && !mNextStartRef && !mNextEndRef; } #endif // #ifdef DEBUG nsCOMPtr mRoot; // mRegisteredClosestCommonInclusiveAncestor is only non-null when the range - // IsInSelection(). It's kept alive via mStart/mEnd, + // 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; - mozilla::WeakPtr mSelection; + + // A Range can be part of multiple |Selection|s. This is a very rare use case. + mozilla::LinkedList> 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/editor/libeditor/AutoRangeArray.cpp b/editor/libeditor/AutoRangeArray.cpp index fdf138791742..094a9d519df5 100644 --- a/editor/libeditor/AutoRangeArray.cpp +++ b/editor/libeditor/AutoRangeArray.cpp @@ -429,7 +429,7 @@ AutoRangeArray::ShrinkRangesIfStartFromOrEndAfterAtomicContent( bool changed = false; for (auto& range : mRanges) { - MOZ_ASSERT(!range->IsInSelection(), + MOZ_ASSERT(!range->IsInAnySelection(), "Changing range in selection may cause running script"); Result result = WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent( @@ -939,7 +939,7 @@ AutoRangeArray::SplitTextAtEndBoundariesAndInlineAncestorsAtBothBoundaries( // Correct the range. // The new end parent becomes the parent node of the text. - MOZ_ASSERT(!range->IsInSelection()); + MOZ_ASSERT(!range->IsInAnySelection()); range->SetEnd(unwrappedSplitAtEndResult.AtNextContent() .ToRawRangeBoundary(), ignoredError); diff --git a/editor/libeditor/WSRunObject.cpp b/editor/libeditor/WSRunObject.cpp index 8d3c99c33165..53ae10723f2c 100644 --- a/editor/libeditor/WSRunObject.cpp +++ b/editor/libeditor/WSRunObject.cpp @@ -4298,7 +4298,7 @@ WSRunScanner::ShrinkRangeIfStartsFromOrEndsAfterAtomicContent( const HTMLEditor& aHTMLEditor, nsRange& aRange, const Element* aEditingHost) { MOZ_ASSERT(aRange.IsPositioned()); - MOZ_ASSERT(!aRange.IsInSelection(), + MOZ_ASSERT(!aRange.IsInAnySelection(), "Changing range in selection may cause running script"); if (NS_WARN_IF(!aRange.GetStartContainer()) || diff --git a/extensions/spellcheck/src/mozInlineSpellChecker.cpp b/extensions/spellcheck/src/mozInlineSpellChecker.cpp index 5d4856d250be..79956fa9f34f 100644 --- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -1834,11 +1834,12 @@ void mozInlineSpellChecker::UpdateRangesForMisspelledWords( const size_t indexOfOldRangeToKeep = aOldRangesForSomeWords.IndexOf( nodeOffsetRange, 0, CompareRangeAndNodeOffsetRange{}); if (indexOfOldRangeToKeep != aOldRangesForSomeWords.NoIndex && - aOldRangesForSomeWords[indexOfOldRangeToKeep]->GetSelection() == - &aSpellCheckerSelection /** TODO: warn in case the old range doesn't - belong to the selection. This is not critical, - because other code can always remove them - before the actual spellchecking happens. */) { + aOldRangesForSomeWords[indexOfOldRangeToKeep]->IsInSelection( + aSpellCheckerSelection)) { + /** TODO: warn in case the old range doesn't + belong to the selection. This is not critical, + because other code can always remove them + before the actual spellchecking happens. */ MOZ_LOG(sInlineSpellCheckerLog, LogLevel::Verbose, ("%s: reusing old range.", __FUNCTION__)); diff --git a/layout/generic/nsFrameSelection.cpp b/layout/generic/nsFrameSelection.cpp index e5df356303a7..0a019af7e47d 100644 --- a/layout/generic/nsFrameSelection.cpp +++ b/layout/generic/nsFrameSelection.cpp @@ -305,7 +305,7 @@ struct MOZ_RAII AutoPrepareFocusRange { while (i--) { nsRange* range = ranges[i].mRange; if (range->IsGenerated()) { - range->UnregisterSelection(); + range->UnregisterSelection(aSelection); aSelection.SelectFrames(presContext, range, false); ranges.RemoveElementAt(i); } diff --git a/testing/web-platform/mozilla/meta/dom/range-in-two-selections.html.ini b/testing/web-platform/mozilla/meta/dom/range-in-two-selections.html.ini new file mode 100644 index 000000000000..3602b3db5bfc --- /dev/null +++ b/testing/web-platform/mozilla/meta/dom/range-in-two-selections.html.ini @@ -0,0 +1 @@ +prefs: [dom.customHighlightAPI.enabled:true] diff --git a/testing/web-platform/mozilla/tests/dom/range-in-two-selections.html b/testing/web-platform/mozilla/tests/dom/range-in-two-selections.html new file mode 100644 index 000000000000..a37464d55a48 --- /dev/null +++ b/testing/web-platform/mozilla/tests/dom/range-in-two-selections.html @@ -0,0 +1,34 @@ + + + + + + + + + +One two + + + +