diff options
Diffstat (limited to '')
-rw-r--r-- | dom/base/Selection.cpp | 273 |
1 files changed, 207 insertions, 66 deletions
diff --git a/dom/base/Selection.cpp b/dom/base/Selection.cpp index 69986e6b78..7983ef98f9 100644 --- a/dom/base/Selection.cpp +++ b/dom/base/Selection.cpp @@ -22,9 +22,11 @@ #include "mozilla/CaretAssociationHint.h" #include "mozilla/ContentIterator.h" #include "mozilla/dom/Element.h" +#include "mozilla/dom/ChildIterator.h" #include "mozilla/dom/SelectionBinding.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/StaticRange.h" +#include "mozilla/dom/ShadowIncludingTreeIterator.h" #include "mozilla/ErrorResult.h" #include "mozilla/HTMLEditor.h" #include "mozilla/IntegerRange.h" @@ -778,30 +780,39 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(Selection) NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Selection, Disconnect()) -const RangeBoundary& Selection::AnchorRef() const { +const RangeBoundary& Selection::AnchorRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { if (!mAnchorFocusRange) { static RangeBoundary sEmpty; return sEmpty; } if (GetDirection() == eDirNext) { - return mAnchorFocusRange->StartRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef() + : mAnchorFocusRange->StartRef(); } - return mAnchorFocusRange->EndRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef() + : mAnchorFocusRange->EndRef(); } -const RangeBoundary& Selection::FocusRef() const { +const RangeBoundary& Selection::FocusRef( + AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary) const { if (!mAnchorFocusRange) { static RangeBoundary sEmpty; return sEmpty; } if (GetDirection() == eDirNext) { - return mAnchorFocusRange->EndRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryEndRef() + : mAnchorFocusRange->EndRef(); } - - return mAnchorFocusRange->StartRef(); + return aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes + ? mAnchorFocusRange->MayCrossShadowBoundaryStartRef() + : mAnchorFocusRange->StartRef(); } void Selection::SetAnchorFocusRange(size_t aIndex) { @@ -818,8 +829,8 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode, uint32_t aCompareOffset, const AbstractRange& aRange, nsContentUtils::NodeIndexCache* aCache) { - MOZ_ASSERT(aRange.GetStartContainer()); - nsINode* start = aRange.GetStartContainer(); + MOZ_ASSERT(aRange.GetMayCrossShadowBoundaryStartContainer()); + nsINode* start = aRange.GetMayCrossShadowBoundaryStartContainer(); // If the nodes that we're comparing are not in the same document, assume that // aCompareNode will fall at the end of the ranges. if (aCompareNode.GetComposedDoc() != start->GetComposedDoc() || @@ -830,8 +841,9 @@ static int32_t CompareToRangeStart(const nsINode& aCompareNode, } // The points are in the same subtree, hence there has to be an order. - return *nsContentUtils::ComparePoints(&aCompareNode, aCompareOffset, start, - aRange.StartOffset(), aCache); + return *nsContentUtils::ComparePoints( + &aCompareNode, aCompareOffset, start, + aRange.MayCrossShadowBoundaryStartOffset(), aCache); } static int32_t CompareToRangeStart(const nsINode& aCompareNode, @@ -844,7 +856,7 @@ static int32_t CompareToRangeEnd(const nsINode& aCompareNode, uint32_t aCompareOffset, const AbstractRange& aRange) { MOZ_ASSERT(aRange.IsPositioned()); - nsINode* end = aRange.GetEndContainer(); + nsINode* end = aRange.GetMayCrossShadowBoundaryEndContainer(); // If the nodes that we're comparing are not in the same document or in the // same subtree, assume that aCompareNode will fall at the end of the ranges. if (aCompareNode.GetComposedDoc() != end->GetComposedDoc() || @@ -855,8 +867,9 @@ static int32_t CompareToRangeEnd(const nsINode& aCompareNode, } // The points are in the same subtree, hence there has to be an order. - return *nsContentUtils::ComparePoints(&aCompareNode, aCompareOffset, end, - aRange.EndOffset()); + return *nsContentUtils::ComparePoints( + &aCompareNode, aCompareOffset, end, + aRange.MayCrossShadowBoundaryEndOffset()); } // static @@ -1323,7 +1336,18 @@ nsresult Selection::RemoveCollapsedRanges() { nsresult Selection::StyledRanges::RemoveCollapsedRanges() { uint32_t i = 0; while (i < mRanges.Length()) { - if (mRanges[i].mRange->Collapsed()) { + const AbstractRange* range = mRanges[i].mRange; + // If nsRange::mCrossShadowBoundaryRange exists, it means + // there's a cross boundary selection, so obviously + // we shouldn't remove this range. + const bool collapsed = + range->Collapsed() && !range->MayCrossShadowBoundary(); + // Cross boundary range should always be uncollapsed. + MOZ_ASSERT_IF( + range->MayCrossShadowBoundary(), + !range->AsDynamicRange()->CrossShadowBoundaryRangeCollapsed()); + + if (collapsed) { nsresult rv = RemoveRangeAndUnregisterSelection(*mRanges[i].mRange); NS_ENSURE_SUCCESS(rv, rv); } else { @@ -1616,7 +1640,8 @@ nsresult Selection::StyledRanges::GetIndicesForInterval( // the given interval's start point, but that range isn't collapsed (a // collapsed range should be included in the returned results). const AbstractRange* beginRange = mRanges[beginsAfterIndex].mRange; - if (beginRange->EndRef().Equals(aBeginNode, aBeginOffset) && + if (beginRange->MayCrossShadowBoundaryEndRef().Equals(aBeginNode, + aBeginOffset) && !beginRange->Collapsed()) { beginsAfterIndex++; } @@ -1627,7 +1652,8 @@ nsresult Selection::StyledRanges::GetIndicesForInterval( // included if (endsBeforeIndex < mRanges.Length()) { const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange; - if (endRange->StartRef().Equals(aEndNode, aEndOffset) && + if (endRange->MayCrossShadowBoundaryStartRef().Equals(aEndNode, + aEndOffset) && endRange->Collapsed()) { endsBeforeIndex++; } @@ -1710,6 +1736,16 @@ nsresult Selection::SelectFramesOfInclusiveDescendantsOfContent( return NS_OK; } +void Selection::SelectFramesOfShadowIncludingDescendantsOfContent( + nsIContent* aContent, bool aSelected) const { + MOZ_ASSERT(aContent); + MOZ_ASSERT(StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()); + for (nsINode* node : ShadowIncludingTreeIterator(*aContent)) { + nsIContent* innercontent = node->IsContent() ? node->AsContent() : nullptr; + SelectFramesOf(innercontent, aSelected); + } +} + void Selection::SelectFramesInAllRanges(nsPresContext* aPresContext) { // this method is currently only called in a user-initiated context. // therefore it is safe to assume that we are not in a Highlight selection @@ -1748,16 +1784,22 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, if (mFrameSelection->IsInTableSelectionMode()) { const nsIContent* const commonAncestorContent = - nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor()); + nsIContent::FromNodeOrNull(aRange.GetClosestCommonInclusiveAncestor( + StaticPrefs::dom_select_events_textcontrols_selectstart_enabled() + ? AllowRangeCrossShadowBoundary::Yes + : AllowRangeCrossShadowBoundary::No)); nsIFrame* const frame = commonAncestorContent ? commonAncestorContent->GetPrimaryFrame() : aPresContext->PresShell()->GetRootFrame(); if (frame) { if (frame->IsTextFrame()) { - MOZ_ASSERT(commonAncestorContent == aRange.GetStartContainer()); - MOZ_ASSERT(commonAncestorContent == aRange.GetEndContainer()); + MOZ_ASSERT(commonAncestorContent == + aRange.GetMayCrossShadowBoundaryStartContainer()); + MOZ_ASSERT(commonAncestorContent == + aRange.GetMayCrossShadowBoundaryEndContainer()); static_cast<nsTextFrame*>(frame)->SelectionStateChanged( - aRange.StartOffset(), aRange.EndOffset(), aSelect, mSelectionType); + aRange.MayCrossShadowBoundaryStartOffset(), + aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType); } else { frame->SelectionStateChanged(); } @@ -1768,8 +1810,8 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // Loop through the content iterator for each content node; for each text // node, call SetSelected on it: - nsIContent* const startContent = - nsIContent::FromNodeOrNull(aRange.GetStartContainer()); + nsIContent* const startContent = nsIContent::FromNodeOrNull( + aRange.GetMayCrossShadowBoundaryStartContainer()); if (MOZ_UNLIKELY(!startContent)) { // Don't warn, bug 1055722 // XXX The range can start from a document node and such range can be @@ -1780,7 +1822,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, MOZ_DIAGNOSTIC_ASSERT(startContent->IsInComposedDoc()); // We must call first one explicitly - nsINode* const endNode = aRange.GetEndContainer(); + nsINode* const endNode = aRange.GetMayCrossShadowBoundaryEndContainer(); if (NS_WARN_IF(!endNode)) { // We null-checked start node above, therefore, end node should also be // non-null here. @@ -1792,10 +1834,10 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // The frame could be an SVG text frame, in which case we don't treat it // as a text frame. if (frame->IsTextFrame()) { - const uint32_t startOffset = aRange.StartOffset(); - const uint32_t endOffset = endNode == startContent - ? aRange.EndOffset() - : startContent->Length(); + const uint32_t startOffset = aRange.MayCrossShadowBoundaryStartOffset(); + const uint32_t endOffset = + endNode == startContent ? aRange.MayCrossShadowBoundaryEndOffset() + : startContent->Length(); static_cast<nsTextFrame*>(frame)->SelectionStateChanged( startOffset, endOffset, aSelect, mSelectionType); } else { @@ -1806,7 +1848,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // If the range is in a node and the node is a leaf node, we don't need to // walk the subtree. - if (aRange.Collapsed() || + if ((aRange.Collapsed() && !aRange.MayCrossShadowBoundary()) || (startContent == endNode && !startContent->HasChildren())) { if (!isFirstContentTextNode) { SelectFramesOf(startContent, aSelect); @@ -1815,7 +1857,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, } ContentSubtreeIterator subtreeIter; - subtreeIter.Init(&aRange); + subtreeIter.InitWithAllowCrossShadowBoundary(&aRange); if (isFirstContentTextNode && !subtreeIter.IsDone() && subtreeIter.GetCurrentNode() == startContent) { subtreeIter.Next(); // first content has already been handled. @@ -1825,8 +1867,12 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, MOZ_DIAGNOSTIC_ASSERT(subtreeIter.GetCurrentNode()); if (nsIContent* const content = nsIContent::FromNodeOrNull(subtreeIter.GetCurrentNode())) { - SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content, - aSelect); + if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) { + SelectFramesOfShadowIncludingDescendantsOfContent(content, aSelect); + } else { + SelectFramesOfInclusiveDescendantsOfContent(postOrderIter, content, + aSelect); + } } } @@ -1839,7 +1885,7 @@ nsresult Selection::SelectFrames(nsPresContext* aPresContext, // The frame could be an SVG text frame, in which case we'll ignore it. if (frame->IsTextFrame()) { static_cast<nsTextFrame*>(frame)->SelectionStateChanged( - 0, aRange.EndOffset(), aSelect, mSelectionType); + 0, aRange.MayCrossShadowBoundaryEndOffset(), aSelect, mSelectionType); } } return NS_OK; @@ -1901,10 +1947,11 @@ UniquePtr<SelectionDetails> Selection::LookUpSelection( if (range->IsStaticRange() && !range->AsStaticRange()->IsValid()) { continue; } - nsINode* startNode = range->GetStartContainer(); - nsINode* endNode = range->GetEndContainer(); - uint32_t startOffset = range->StartOffset(); - uint32_t endOffset = range->EndOffset(); + + nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer(); + nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer(); + uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset(); + uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset(); Maybe<uint32_t> start, end; if (startNode == aContent && endNode == aContent) { @@ -2184,6 +2231,67 @@ void Selection::RemoveAllRanges(ErrorResult& aRv) { RemoveAllRangesInternal(aRv); } +already_AddRefed<StaticRange> Selection::GetComposedRange( + const AbstractRange* aRange, + const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots) const { + // If aIsEndNode is true, this method does the Step 5.1 and 5.2 + // in https://www.w3.org/TR/selection-api/#dom-selection-getcomposedranges, + // otherwise it does the Step 3.1 and 3.2. + auto reScope = [&aShadowRoots](nsINode*& aNode, uint32_t& aOffset, + bool aIsEndNode) { + MOZ_ASSERT(aNode); + while (aNode) { + const ShadowRoot* shadowRootOfNode = aNode->GetContainingShadow(); + if (!shadowRootOfNode) { + return; + } + + for (const OwningNonNull<ShadowRoot>& shadowRoot : aShadowRoots) { + if (shadowRoot->IsShadowIncludingInclusiveDescendantOf( + shadowRootOfNode)) { + return; + } + } + + const nsIContent* host = aNode->GetContainingShadowHost(); + const Maybe<uint32_t> maybeIndex = host->ComputeIndexInParentContent(); + MOZ_ASSERT(maybeIndex.isSome(), "not parent or anonymous child?"); + if (MOZ_UNLIKELY(maybeIndex.isNothing())) { + // Unlikely to happen, but still set aNode to nullptr to avoid + // leaking information about the shadow tree. + aNode = nullptr; + return; + } + aOffset = maybeIndex.value(); + if (aIsEndNode) { + aOffset += 1; + } + aNode = host->GetParentNode(); + } + }; + + nsINode* startNode = aRange->GetMayCrossShadowBoundaryStartContainer(); + uint32_t startOffset = aRange->MayCrossShadowBoundaryStartOffset(); + nsINode* endNode = aRange->GetMayCrossShadowBoundaryEndContainer(); + uint32_t endOffset = aRange->MayCrossShadowBoundaryEndOffset(); + + reScope(startNode, startOffset, false /* aIsEndNode */); + reScope(endNode, endOffset, true /* aIsEndNode */); + + RefPtr<StaticRange> composedRange = StaticRange::Create( + startNode, startOffset, endNode, endOffset, IgnoreErrors()); + return composedRange.forget(); +} + +void Selection::GetComposedRanges( + const Sequence<OwningNonNull<ShadowRoot>>& aShadowRoots, + nsTArray<RefPtr<StaticRange>>& aComposedRanges) { + aComposedRanges.SetCapacity(mStyledRanges.mRanges.Length()); + for (const auto& range : mStyledRanges.mRanges) { + aComposedRanges.AppendElement(GetComposedRange(range.mRange, aShadowRoots)); + } +} + void Selection::RemoveAllRangesInternal(ErrorResult& aRv) { if (!mFrameSelection) { aRv.Throw(NS_ERROR_NOT_INITIALIZED); @@ -2501,11 +2609,15 @@ void Selection::CollapseInternal(InLimiter aInLimiter, // Hack to display the caret on the right line (bug 1237236). if (frameSelection->GetHint() == CaretAssociationHint::Before && aPoint.Container()->IsContent()) { - int32_t frameOffset; - nsTextFrame* f = do_QueryFrame(nsCaret::GetFrameAndOffset( - this, aPoint.Container(), - *aPoint.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets), - &frameOffset)); + const nsCaret::CaretPosition pos{ + aPoint.Container(), + int32_t(*aPoint.Offset(RawRangeBoundary::OffsetFilter::kValidOffsets)), + frameSelection->GetHint(), frameSelection->GetCaretBidiLevel()}; + CaretFrameData frameData = nsCaret::GetFrameAndOffset(pos); + if (frameData.mFrame) { + frameSelection->SetHint(frameData.mHint); + } + nsTextFrame* f = do_QueryFrame(frameData.mFrame); if (f && f->IsAtEndOfLine() && f->HasSignificantTerminalNewline()) { // RawRangeBounary::Offset() causes computing offset if it's not been // done yet. However, it's called only when the container is a text @@ -2672,6 +2784,19 @@ AbstractRange* Selection::GetAbstractRangeAt(uint32_t aIndex) const { return mStyledRanges.mRanges.SafeElementAt(aIndex, empty).mRange; } +void Selection::GetDirection(nsAString& aDirection) const { + if (mStyledRanges.mRanges.IsEmpty() || + (mFrameSelection && (mFrameSelection->IsDoubleClickSelection() || + mFrameSelection->IsTripleClickSelection()))) { + // Empty range and double/triple clicks result a directionless selection. + aDirection.AssignLiteral("none"); + } else if (mDirection == nsDirection::eDirPrevious) { + aDirection.AssignLiteral("backward"); + } else { + aDirection.AssignLiteral("forward"); + } +} + nsRange* Selection::GetRangeAt(uint32_t aIndex) const { // This method per IDL spec returns a dynamic range. // Therefore, it must be ensured that it is only called @@ -2836,17 +2961,17 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, #ifdef DEBUG_SELECTION nsDirection oldDirection = GetDirection(); #endif - nsINode* anchorNode = GetAnchorNode(); - nsINode* focusNode = GetFocusNode(); - const uint32_t anchorOffset = AnchorOffset(); - const uint32_t focusOffset = FocusOffset(); + nsINode* anchorNode = GetMayCrossShadowBoundaryAnchorNode(); + nsINode* focusNode = GetMayCrossShadowBoundaryFocusNode(); + const uint32_t anchorOffset = MayCrossShadowBoundaryAnchorOffset(); + const uint32_t focusOffset = MayCrossShadowBoundaryFocusOffset(); RefPtr<nsRange> range = mAnchorFocusRange->CloneRange(); - nsINode* startNode = range->GetStartContainer(); - nsINode* endNode = range->GetEndContainer(); - const uint32_t startOffset = range->StartOffset(); - const uint32_t endOffset = range->EndOffset(); + nsINode* startNode = range->GetMayCrossShadowBoundaryStartContainer(); + nsINode* endNode = range->GetMayCrossShadowBoundaryEndContainer(); + const uint32_t startOffset = range->MayCrossShadowBoundaryStartOffset(); + const uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset(); bool shouldClearRange = false; const Maybe<int32_t> anchorOldFocusOrder = nsContentUtils::ComparePoints( @@ -2882,7 +3007,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, (*anchorOldFocusOrder <= 0 && *oldFocusNewFocusOrder < 0)) { // a1,2 a,1,2 // select from 1 to 2 unless they are collapsed - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2903,7 +3029,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, *anchorNewFocusOrder > 0) { // 2, a1 // select from 2 to 1a SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2923,7 +3050,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2933,27 +3061,33 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SelectFrames(presContext, *difRange, false); // deselect now - difRange->SetEnd(range->GetEndContainer(), range->EndOffset()); + difRange->SetEnd(range->GetMayCrossShadowBoundaryEndContainer(), + range->MayCrossShadowBoundaryEndOffset(), + AllowRangeCrossShadowBoundary::Yes); SelectFrames(presContext, *difRange, true); // must reselect last node // maybe more } else if (*anchorOldFocusOrder >= 0 && *anchorNewFocusOrder <= 0) { // 1,a,2 or 1a,2 or 1,a2 or 1a2 if (GetDirection() == eDirPrevious) { - res = range->SetStart(endNode, endOffset); + res = range->SetStart(endNode, endOffset, + AllowRangeCrossShadowBoundary::Yes); if (NS_FAILED(res)) { aRv.Throw(res); return; } } SetDirection(eDirNext); - range->SetEnd(aContainer, aOffset, aRv); + range->SetEnd(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } if (focusNode != anchorNode || focusOffset != anchorOffset) { // if collapsed diff dont do anything - res = difRange->SetStart(focusNode, focusOffset); - nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset); + res = difRange->SetStart(focusNode, focusOffset, + AllowRangeCrossShadowBoundary::Yes); + nsresult tmp = difRange->SetEnd(anchorNode, anchorOffset, + AllowRangeCrossShadowBoundary::Yes); if (NS_FAILED(tmp)) { res = tmp; } @@ -2987,7 +3121,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -2998,15 +3133,19 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, return; } SelectFrames(presContext, *difRange, false); - difRange->SetStart(range->GetStartContainer(), range->StartOffset()); + difRange->SetStart(range->GetMayCrossShadowBoundaryStartContainer(), + range->MayCrossShadowBoundaryStartOffset(), + AllowRangeCrossShadowBoundary::Yes); SelectFrames(presContext, *difRange, true); // must reselect last node } else if (*anchorNewFocusOrder >= 0 && *anchorOldFocusOrder <= 0) { // 2,a,1 or 2a,1 or 2,a1 or 2a1 if (GetDirection() == eDirNext) { - range->SetEnd(startNode, startOffset); + range->SetEnd(startNode, startOffset, + AllowRangeCrossShadowBoundary::Yes); } SetDirection(eDirPrevious); - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -3036,7 +3175,8 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset, } else if (*oldFocusNewFocusOrder >= 0 && *anchorOldFocusOrder >= 0) { // 2,1,a or 21,a or 2,1a or 21a // select from 2 to 1 - range->SetStart(aContainer, aOffset, aRv); + range->SetStart(aContainer, aOffset, aRv, + AllowRangeCrossShadowBoundary::Yes); if (aRv.Failed()) { return; } @@ -3604,9 +3744,10 @@ void Selection::NotifySelectionListeners() { RefPtr<nsFrameSelection> frameSelection = mFrameSelection; - // This flag will be set to true if a selection by double click is detected. - // As soon as the selection is modified, it needs to be set to false. - frameSelection->SetIsDoubleClickSelection(false); + // This flag will be set to Double or Triple if a selection by double click or + // triple click is detected. As soon as the selection is modified, it needs to + // be reset to NotApplicable. + frameSelection->SetClickSelectionType(ClickSelectionType::NotApplicable); if (frameSelection->IsBatching()) { frameSelection->SetChangesDuringBatchingFlag(); |