forked from mirrors/gecko-dev
Bug 1867058 - Part 8: Update Selection to support across shadow dom selection r=smaug
Differential Revision: https://phabricator.services.mozilla.com/D195310
This commit is contained in:
parent
b411788f91
commit
2026f2e0ce
9 changed files with 192 additions and 76 deletions
|
|
@ -378,7 +378,10 @@ void AbstractRange::RegisterSelection(Selection& aSelection) {
|
|||
bool isFirstSelection = mSelections.IsEmpty();
|
||||
mSelections.AppendElement(&aSelection);
|
||||
if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
|
||||
nsINode* commonAncestor = GetClosestCommonInclusiveAncestor();
|
||||
nsINode* commonAncestor = GetClosestCommonInclusiveAncestor(
|
||||
StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
|
||||
? AllowRangeCrossShadowBoundary::Yes
|
||||
: AllowRangeCrossShadowBoundary::No);
|
||||
MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
|
||||
RegisterClosestCommonInclusiveAncestor(commonAncestor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,9 +147,10 @@ nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
|
|||
NS_WARN_IF(!aAbstractRange->IsPositioned())) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
return CompareNodeToRangeBoundaries(aNode, aAbstractRange->StartRef(),
|
||||
aAbstractRange->EndRef(),
|
||||
aNodeIsBeforeRange, aNodeIsAfterRange);
|
||||
return CompareNodeToRangeBoundaries(
|
||||
aNode, aAbstractRange->MayCrossShadowBoundaryStartRef(),
|
||||
aAbstractRange->MayCrossShadowBoundaryEndRef(), aNodeIsBeforeRange,
|
||||
aNodeIsAfterRange);
|
||||
}
|
||||
template <typename SPT, typename SRT, typename EPT, typename ERT>
|
||||
nsresult RangeUtils::CompareNodeToRangeBoundaries(
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#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"
|
||||
|
|
@ -778,30 +779,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 +828,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 +840,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 +855,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 +866,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
|
||||
|
|
@ -1616,7 +1628,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 +1640,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++;
|
||||
}
|
||||
|
|
@ -1748,16 +1762,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 +1788,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 +1800,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 +1812,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 +1826,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 +1835,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.
|
||||
|
|
@ -1839,7 +1859,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 +1921,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) {
|
||||
|
|
@ -2914,17 +2935,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(
|
||||
|
|
@ -2960,7 +2981,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;
|
||||
}
|
||||
|
|
@ -2981,7 +3003,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;
|
||||
}
|
||||
|
|
@ -3001,7 +3024,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;
|
||||
}
|
||||
|
|
@ -3011,27 +3035,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;
|
||||
}
|
||||
|
|
@ -3065,7 +3095,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;
|
||||
}
|
||||
|
|
@ -3076,15 +3107,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;
|
||||
}
|
||||
|
|
@ -3114,7 +3149,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ namespace dom {
|
|||
class Selection final : public nsSupportsWeakReference,
|
||||
public nsWrapperCache,
|
||||
public SupportsWeakPtr {
|
||||
using AllowRangeCrossShadowBoundary =
|
||||
mozilla::dom::AllowRangeCrossShadowBoundary;
|
||||
|
||||
protected:
|
||||
virtual ~Selection();
|
||||
|
||||
|
|
@ -327,6 +330,30 @@ class Selection final : public nsSupportsWeakReference,
|
|||
return offset ? *offset : 0;
|
||||
}
|
||||
|
||||
nsINode* GetMayCrossShadowBoundaryAnchorNode() const {
|
||||
const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
|
||||
return anchor.IsSet() ? anchor.Container() : nullptr;
|
||||
}
|
||||
|
||||
uint32_t MayCrossShadowBoundaryAnchorOffset() const {
|
||||
const RangeBoundary& anchor = AnchorRef(AllowRangeCrossShadowBoundary::Yes);
|
||||
const Maybe<uint32_t> offset =
|
||||
anchor.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
return offset ? *offset : 0;
|
||||
}
|
||||
|
||||
nsINode* GetMayCrossShadowBoundaryFocusNode() const {
|
||||
const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
|
||||
return focus.IsSet() ? focus.Container() : nullptr;
|
||||
}
|
||||
|
||||
uint32_t MayCrossShadowBoundaryFocusOffset() const {
|
||||
const RangeBoundary& focus = FocusRef(AllowRangeCrossShadowBoundary::Yes);
|
||||
const Maybe<uint32_t> offset =
|
||||
focus.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
|
||||
return offset ? *offset : 0;
|
||||
}
|
||||
|
||||
nsIContent* GetChildAtAnchorOffset() {
|
||||
const RangeBoundary& anchor = AnchorRef();
|
||||
return anchor.IsSet() ? anchor.GetChildAtOffset() : nullptr;
|
||||
|
|
@ -336,8 +363,12 @@ class Selection final : public nsSupportsWeakReference,
|
|||
return focus.IsSet() ? focus.GetChildAtOffset() : nullptr;
|
||||
}
|
||||
|
||||
const RangeBoundary& AnchorRef() const;
|
||||
const RangeBoundary& FocusRef() const;
|
||||
const RangeBoundary& AnchorRef(
|
||||
AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
|
||||
AllowRangeCrossShadowBoundary::No) const;
|
||||
const RangeBoundary& FocusRef(
|
||||
AllowRangeCrossShadowBoundary aAllowCrossShadowBoundary =
|
||||
AllowRangeCrossShadowBoundary::No) const;
|
||||
|
||||
/*
|
||||
* IsCollapsed -- is the whole selection just one point, or unset?
|
||||
|
|
|
|||
|
|
@ -287,11 +287,17 @@ static const nsINode* GetClosestCommonInclusiveAncestorForRangeInSelection(
|
|||
const nsINode* aNode) {
|
||||
while (aNode &&
|
||||
!aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
|
||||
const bool isNodeInShadowTree =
|
||||
StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
|
||||
aNode->IsInShadowTree();
|
||||
if (!aNode
|
||||
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
|
||||
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() &&
|
||||
!isNodeInShadowTree) {
|
||||
return nullptr;
|
||||
}
|
||||
aNode = aNode->GetParentNode();
|
||||
aNode = StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
|
||||
? aNode->GetParentOrShadowHostNode()
|
||||
: aNode->GetParentNode();
|
||||
}
|
||||
return aNode;
|
||||
}
|
||||
|
|
@ -315,12 +321,12 @@ class IsItemInRangeComparator {
|
|||
|
||||
int operator()(const AbstractRange* const aRange) const {
|
||||
int32_t cmp = nsContentUtils::ComparePoints_Deprecated(
|
||||
&mNode, mEndOffset, aRange->GetStartContainer(), aRange->StartOffset(),
|
||||
nullptr, mCache);
|
||||
&mNode, mEndOffset, aRange->GetMayCrossShadowBoundaryStartContainer(),
|
||||
aRange->MayCrossShadowBoundaryStartOffset(), nullptr, mCache);
|
||||
if (cmp == 1) {
|
||||
cmp = nsContentUtils::ComparePoints_Deprecated(
|
||||
&mNode, mStartOffset, aRange->GetEndContainer(), aRange->EndOffset(),
|
||||
nullptr, mCache);
|
||||
&mNode, mStartOffset, aRange->GetMayCrossShadowBoundaryEndContainer(),
|
||||
aRange->MayCrossShadowBoundaryEndOffset(), nullptr, mCache);
|
||||
if (cmp == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -386,6 +392,18 @@ bool nsINode::IsSelected(const uint32_t aStartOffset,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (range->MayCrossShadowBoundary()) {
|
||||
MOZ_ASSERT(range->IsDynamicRange(),
|
||||
"range->MayCrossShadowBoundary() can only return true for "
|
||||
"dynamic range");
|
||||
StaticRange* crossBoundaryRange =
|
||||
range->AsDynamicRange()->GetCrossShadowBoundaryRange();
|
||||
MOZ_ASSERT(crossBoundaryRange);
|
||||
if (!crossBoundaryRange->Collapsed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const AbstractRange* middlePlus1;
|
||||
const AbstractRange* middleMinus1;
|
||||
// if node end > start of middle+1, result = 1
|
||||
|
|
@ -552,7 +570,8 @@ static nsIContent* GetRootForContentSubtree(nsIContent* aContent) {
|
|||
return nsIContent::FromNode(aContent->SubtreeRoot());
|
||||
}
|
||||
|
||||
nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell) {
|
||||
nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell,
|
||||
bool aAllowCrossShadowBoundary) {
|
||||
NS_ENSURE_TRUE(aPresShell, nullptr);
|
||||
|
||||
if (IsDocument()) return AsDocument()->GetRootElement();
|
||||
|
|
@ -596,7 +615,7 @@ nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell) {
|
|||
}
|
||||
|
||||
RefPtr<nsFrameSelection> fs = aPresShell->FrameSelection();
|
||||
nsIContent* content = fs->GetLimiter();
|
||||
nsCOMPtr<nsIContent> content = fs->GetLimiter();
|
||||
if (!content) {
|
||||
content = fs->GetAncestorLimiter();
|
||||
if (!content) {
|
||||
|
|
@ -616,6 +635,10 @@ nsIContent* nsINode::GetSelectionRootContent(PresShell* aPresShell) {
|
|||
// Use the host as the root.
|
||||
if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(content)) {
|
||||
content = shadowRoot->GetHost();
|
||||
if (content && aAllowCrossShadowBoundary) {
|
||||
content = content->GetSelectionRootContent(aPresShell,
|
||||
aAllowCrossShadowBoundary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1629,7 +1629,7 @@ class nsINode : public mozilla::dom::EventTarget {
|
|||
* not in same subtree, this returns the root content of the closeset subtree.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsIContent* GetSelectionRootContent(
|
||||
mozilla::PresShell* aPresShell);
|
||||
mozilla::PresShell* aPresShell, bool aAllowCrossShadowBoundary = false);
|
||||
|
||||
nsINodeList* ChildNodes();
|
||||
|
||||
|
|
|
|||
|
|
@ -3188,7 +3188,7 @@ void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) {
|
|||
// This is the initial range and all its nodes until now are
|
||||
// non-selectable so just trim them from the start.
|
||||
IgnoredErrorResult err;
|
||||
range->SetStartBefore(*node, err);
|
||||
range->SetStartBefore(*node, err, AllowRangeCrossShadowBoundary::Yes);
|
||||
if (err.Failed()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -3202,7 +3202,8 @@ void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) {
|
|||
|
||||
// Truncate the current range before the first non-selectable node.
|
||||
IgnoredErrorResult err;
|
||||
range->SetEndBefore(*firstNonSelectableContent, err);
|
||||
range->SetEndBefore(*firstNonSelectableContent, err,
|
||||
AllowRangeCrossShadowBoundary::Yes);
|
||||
|
||||
// Store it in the result (strong ref) - do this before creating
|
||||
// a new range in |newRange| below so we don't drop the last ref
|
||||
|
|
|
|||
|
|
@ -539,7 +539,11 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(
|
|||
|
||||
NS_ENSURE_STATE(mPresShell);
|
||||
RefPtr<PresShell> presShell = mPresShell;
|
||||
nsIContent* anchorRoot = anchorContent->GetSelectionRootContent(presShell);
|
||||
nsIContent* anchorRoot =
|
||||
anchorContent
|
||||
->GetSelectionRootContent(
|
||||
presShell,
|
||||
StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
|
||||
NS_ENSURE_TRUE(anchorRoot, NS_ERROR_UNEXPECTED);
|
||||
|
||||
//
|
||||
|
|
@ -549,7 +553,10 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(
|
|||
nsCOMPtr<nsIContent> content = aFrame->GetContent();
|
||||
|
||||
if (content) {
|
||||
nsIContent* contentRoot = content->GetSelectionRootContent(presShell);
|
||||
nsIContent* contentRoot =
|
||||
content->GetSelectionRootContent(
|
||||
presShell, StaticPrefs::
|
||||
dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
|
||||
NS_ENSURE_TRUE(contentRoot, NS_ERROR_UNEXPECTED);
|
||||
|
||||
if (anchorRoot == contentRoot) {
|
||||
|
|
@ -573,8 +580,9 @@ nsresult nsFrameSelection::ConstrainFrameAndPointToAnchorSubtree(
|
|||
if (cursorFrame && cursorFrame->PresShell() == presShell) {
|
||||
nsCOMPtr<nsIContent> cursorContent = cursorFrame->GetContent();
|
||||
NS_ENSURE_TRUE(cursorContent, NS_ERROR_FAILURE);
|
||||
nsIContent* cursorContentRoot =
|
||||
cursorContent->GetSelectionRootContent(presShell);
|
||||
nsIContent* cursorContentRoot = cursorContent->GetSelectionRootContent(
|
||||
presShell, StaticPrefs::
|
||||
dom_shadowdom_selection_across_boundary_enabled() /* aAllowCrossShadowBoundary */);
|
||||
NS_ENSURE_TRUE(cursorContentRoot, NS_ERROR_UNEXPECTED);
|
||||
if (cursorContentRoot == anchorRoot) {
|
||||
*aRetFrame = cursorFrame;
|
||||
|
|
|
|||
|
|
@ -8568,6 +8568,12 @@ const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
|
|||
bool nsIFrame::IsFrameSelected() const {
|
||||
NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
|
||||
"use the public IsSelected() instead");
|
||||
if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
|
||||
if (const ShadowRoot* shadowRoot =
|
||||
GetContent()->GetShadowRootForSelection()) {
|
||||
return shadowRoot->IsSelected(0, shadowRoot->GetChildCount());
|
||||
}
|
||||
}
|
||||
return GetContent()->IsSelected(0, GetContent()->GetChildCount());
|
||||
}
|
||||
|
||||
|
|
@ -8991,6 +8997,13 @@ nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) {
|
|||
|
||||
if (reachedLimit) { // no "stop frame" found
|
||||
aPos->mResultContent = frame->GetContent();
|
||||
if (ShadowRoot* shadowRoot =
|
||||
aPos->mResultContent->GetShadowRootForSelection()) {
|
||||
// Even if there's no children for this node,
|
||||
// the elements inside the shadow root is still
|
||||
// selectable
|
||||
aPos->mResultContent = shadowRoot;
|
||||
}
|
||||
if (aPos->mDirection == eDirPrevious) {
|
||||
aPos->mContentOffset = 0;
|
||||
} else if (aPos->mResultContent) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue