Bug 1932150 - Allow using flat tree order for point comparing in selection r=jjaschke,smaug,dom-core

Differential Revision: https://phabricator.services.mozilla.com/D231588
This commit is contained in:
Sean Feng
2025-05-07 15:05:09 +00:00
committed by sefeng@mozilla.com
parent 793c2453f1
commit 2ba73e1639
11 changed files with 363 additions and 127 deletions

View File

@@ -26,25 +26,58 @@ template bool RangeUtils::IsValidPoints(const RawRangeBoundary& aStartBoundary,
template bool RangeUtils::IsValidPoints(const RawRangeBoundary& aStartBoundary, template bool RangeUtils::IsValidPoints(const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary); const RawRangeBoundary& aEndBoundary);
template nsresult RangeUtils::CompareNodeToRangeBoundaries( template nsresult
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, const RangeBoundary& aStartBoundary,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries<TreeKind::Flat>(
nsINode* aNode, const RangeBoundary& aStartBoundary, nsINode* aNode, const RangeBoundary& aStartBoundary,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange, const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange); bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries( template nsresult
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, const RangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries<TreeKind::Flat>(
nsINode* aNode, const RangeBoundary& aStartBoundary, nsINode* aNode, const RangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange, const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange); bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries( template nsresult
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, const RawRangeBoundary& aStartBoundary,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries<TreeKind::Flat>(
nsINode* aNode, const RawRangeBoundary& aStartBoundary, nsINode* aNode, const RawRangeBoundary& aStartBoundary,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange, const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange); bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries( template nsresult
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, const RawRangeBoundary& aStartBoundary, nsINode* aNode, const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange, const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange); bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries<TreeKind::Flat>(
nsINode* aNode, const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRange<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, AbstractRange* aAbstractRange, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRange<TreeKind::Flat>(
nsINode* aNode, AbstractRange* aAbstractRange, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template Maybe<bool>
RangeUtils::IsNodeContainedInRange<TreeKind::ShadowIncludingDOM>(
nsINode& aNode, AbstractRange* aAbstractRange);
template Maybe<bool> RangeUtils::IsNodeContainedInRange<TreeKind::Flat>(
nsINode& aNode, AbstractRange* aAbstractRange);
[[nodiscard]] static inline bool ParentNodeIsInSameSelection( [[nodiscard]] static inline bool ParentNodeIsInSameSelection(
const nsINode& aNode) { const nsINode& aNode) {
@@ -67,17 +100,6 @@ template nsresult RangeUtils::CompareNodeToRangeBoundaries(
return true; return true;
} }
// static
nsINode* RangeUtils::GetParentNodeInSameSelection(const nsINode* aNode) {
if (MOZ_UNLIKELY(!aNode)) {
return nullptr;
}
if (!ParentNodeIsInSameSelection(*aNode)) {
return nullptr;
}
return aNode->GetParentNode();
}
// static // static
nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) { nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) {
if (!aNode) { if (!aNode) {
@@ -150,13 +172,14 @@ bool RangeUtils::IsValidPoints(
} }
// static // static
template <TreeKind aKind, typename Dummy>
Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode, Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode,
AbstractRange* aAbstractRange) { AbstractRange* aAbstractRange) {
bool nodeIsBeforeRange{false}; bool nodeIsBeforeRange{false};
bool nodeIsAfterRange{false}; bool nodeIsAfterRange{false};
const nsresult rv = CompareNodeToRange(&aNode, aAbstractRange, const nsresult rv = CompareNodeToRange<aKind>(
&nodeIsBeforeRange, &nodeIsAfterRange); &aNode, aAbstractRange, &nodeIsBeforeRange, &nodeIsAfterRange);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return Nothing(); return Nothing();
} }
@@ -172,6 +195,7 @@ Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode,
// XXX - callers responsibility to ensure node in same doc as range! // XXX - callers responsibility to ensure node in same doc as range!
// static // static
template <TreeKind aKind, typename Dummy>
nsresult RangeUtils::CompareNodeToRange(nsINode* aNode, nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
AbstractRange* aAbstractRange, AbstractRange* aAbstractRange,
bool* aNodeIsBeforeRange, bool* aNodeIsBeforeRange,
@@ -180,12 +204,13 @@ nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
NS_WARN_IF(!aAbstractRange->IsPositioned())) { NS_WARN_IF(!aAbstractRange->IsPositioned())) {
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
return CompareNodeToRangeBoundaries( return CompareNodeToRangeBoundaries<aKind>(
aNode, aAbstractRange->MayCrossShadowBoundaryStartRef(), aNode, aAbstractRange->MayCrossShadowBoundaryStartRef(),
aAbstractRange->MayCrossShadowBoundaryEndRef(), aNodeIsBeforeRange, aAbstractRange->MayCrossShadowBoundaryEndRef(), aNodeIsBeforeRange,
aNodeIsAfterRange); aNodeIsAfterRange);
} }
template <typename SPT, typename SRT, typename EPT, typename ERT> template <TreeKind aKind, typename SPT, typename SRT, typename EPT,
typename ERT, typename Dummy>
nsresult RangeUtils::CompareNodeToRangeBoundaries( nsresult RangeUtils::CompareNodeToRangeBoundaries(
nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary, nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange, const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange,
@@ -208,7 +233,17 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
// gather up the dom point info // gather up the dom point info
int32_t nodeStart; int32_t nodeStart;
uint32_t nodeEnd; uint32_t nodeEnd;
const nsINode* parent = GetParentNodeInSameSelection(aNode); const nsINode* parent = nullptr;
MOZ_ASSERT_IF(aKind == TreeKind::Flat,
StaticPrefs::dom_shadowdom_selection_across_boundary_enabled());
// ShadowRoot has no parent, nor can be represented by parent/offset pair.
if (!aNode->IsShadowRoot()) {
parent = ShadowDOMSelectionHelpers::GetParentNodeInSameSelection(
*aNode, aKind == TreeKind::Flat ? AllowRangeCrossShadowBoundary::Yes
: AllowRangeCrossShadowBoundary::No);
}
if (!parent) { if (!parent) {
// can't make a parent/offset pair to represent start or // can't make a parent/offset pair to represent start or
// end of the root node, because it has no parent. // end of the root node, because it has no parent.
@@ -216,6 +251,14 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
parent = aNode; parent = aNode;
nodeStart = 0; nodeStart = 0;
nodeEnd = aNode->GetChildCount(); nodeEnd = aNode->GetChildCount();
} else if (const HTMLSlotElement* slotAsParent =
HTMLSlotElement::FromNode(parent);
slotAsParent && aKind == TreeKind::Flat) {
// aNode is a slotted content, use the index in the assigned nodes
// to represent this node.
auto index = slotAsParent->AssignedNodes().IndexOf(aNode);
nodeStart = index;
nodeEnd = nodeStart + 1;
} else { } else {
nodeStart = parent->ComputeIndexOf_Deprecated(aNode); nodeStart = parent->ComputeIndexOf_Deprecated(aNode);
NS_WARNING_ASSERTION( NS_WARNING_ASSERTION(
@@ -240,17 +283,19 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
// silence the warning. (Bug 1438996) // silence the warning. (Bug 1438996)
// is RANGE(start) <= NODE(start) ? // is RANGE(start) <= NODE(start) ?
Maybe<int32_t> order = nsContentUtils::ComparePoints_AllowNegativeOffsets( Maybe<int32_t> order =
aStartBoundary.GetContainer(), nsContentUtils::ComparePoints_AllowNegativeOffsets<aKind>(
*aStartBoundary.Offset( aStartBoundary.GetContainer(),
RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets), *aStartBoundary.Offset(
parent, nodeStart); RangeBoundaryBase<SPT,
SRT>::OffsetFilter::kValidOrInvalidOffsets),
parent, nodeStart);
if (NS_WARN_IF(!order)) { if (NS_WARN_IF(!order)) {
return NS_ERROR_DOM_WRONG_DOCUMENT_ERR; return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
} }
*aNodeIsBeforeRange = *order > 0; *aNodeIsBeforeRange = *order > 0;
// is RANGE(end) >= NODE(end) ? // is RANGE(end) >= NODE(end) ?
order = nsContentUtils::ComparePointsWithIndices( order = nsContentUtils::ComparePointsWithIndices<aKind>(
aEndBoundary.GetContainer(), aEndBoundary.GetContainer(),
*aEndBoundary.Offset( *aEndBoundary.Offset(
RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOrInvalidOffsets), RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOrInvalidOffsets),
@@ -315,10 +360,17 @@ nsINode* ShadowDOMSelectionHelpers::GetParentNodeInSameSelection(
if (!ParentNodeIsInSameSelection(aNode)) { if (!ParentNodeIsInSameSelection(aNode)) {
return nullptr; return nullptr;
} }
return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
aAllowCrossShadowBoundary) if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
? aNode.GetParentOrShadowHostNode() aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
: aNode.GetParentNode(); if (aNode.IsContent()) {
if (HTMLSlotElement* slot = aNode.AsContent()->GetAssignedSlot()) {
return slot;
}
}
return aNode.GetParentOrShadowHostNode();
}
return aNode.GetParentNode();
} }
// static // static

View File

@@ -11,6 +11,7 @@
#include "mozilla/RangeBoundary.h" #include "mozilla/RangeBoundary.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsINode.h" #include "nsINode.h"
#include "nsContentUtils.h"
namespace mozilla { namespace mozilla {
@@ -55,8 +56,6 @@ class RangeUtils final {
using AbstractRange = dom::AbstractRange; using AbstractRange = dom::AbstractRange;
public: public:
static nsINode* GetParentNodeInSameSelection(const nsINode* aNode);
/** /**
* GetRawRangeBoundaryBefore() and GetRawRangeBoundaryAfter() retrieve * GetRawRangeBoundaryBefore() and GetRawRangeBoundaryAfter() retrieve
* RawRangeBoundary which points before or after aNode. * RawRangeBoundary which points before or after aNode.
@@ -137,6 +136,9 @@ class RangeUtils final {
/** /**
* The caller needs to ensure aNode is in the same doc like aAbstractRange. * The caller needs to ensure aNode is in the same doc like aAbstractRange.
*/ */
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static Maybe<bool> IsNodeContainedInRange(nsINode& aNode, static Maybe<bool> IsNodeContainedInRange(nsINode& aNode,
AbstractRange* aAbstractRange); AbstractRange* aAbstractRange);
@@ -145,12 +147,18 @@ class RangeUtils final {
* ends after a range. If neither it is contained inside the range. * ends after a range. If neither it is contained inside the range.
* Note that callers responsibility to ensure node in same doc as range. * Note that callers responsibility to ensure node in same doc as range.
*/ */
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static nsresult CompareNodeToRange(nsINode* aNode, static nsresult CompareNodeToRange(nsINode* aNode,
AbstractRange* aAbstractRange, AbstractRange* aAbstractRange,
bool* aNodeIsBeforeRange, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange); bool* aNodeIsAfterRange);
template <typename SPT, typename SRT, typename EPT, typename ERT> template <TreeKind aKind, typename SPT, typename SRT, typename EPT,
typename ERT,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static nsresult CompareNodeToRangeBoundaries( static nsresult CompareNodeToRangeBoundaries(
nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary, nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange, const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange,

View File

@@ -928,9 +928,15 @@ static int32_t CompareToRangeStart(
return 1; return 1;
} }
// The points are in the same subtree, hence there has to be an order. nsINode* start = aRange.GetMayCrossShadowBoundaryStartContainer();
return *nsContentUtils::ComparePoints( uint32_t startOffset = aRange.MayCrossShadowBoundaryStartOffset();
aCompareBoundary, aRange.MayCrossShadowBoundaryStartRef(), aCache); if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
return *nsContentUtils::ComparePoints<TreeKind::Flat>(
aCompareBoundary, ConstRawRangeBoundary{start, startOffset}, aCache);
}
return *nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
aCompareBoundary, ConstRawRangeBoundary{start, startOffset}, aCache);
} }
template <typename PT, typename RT> template <typename PT, typename RT>
@@ -956,9 +962,14 @@ static int32_t CompareToRangeEnd(
return 1; return 1;
} }
// The points are in the same subtree, hence there has to be an order. nsINode* end = aRange.GetMayCrossShadowBoundaryEndContainer();
return *nsContentUtils::ComparePoints(aCompareBoundary, uint32_t endOffset = aRange.MayCrossShadowBoundaryEndOffset();
aRange.MayCrossShadowBoundaryEndRef()); if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
return *nsContentUtils::ComparePoints<TreeKind::Flat>(
aCompareBoundary, ConstRawRangeBoundary{end, endOffset});
}
return *nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
aCompareBoundary, ConstRawRangeBoundary{end, endOffset});
} }
// static // static
@@ -3123,17 +3134,24 @@ void Selection::Extend(nsINode& aContainer, uint32_t aOffset,
const uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset(); const uint32_t endOffset = range->MayCrossShadowBoundaryEndOffset();
bool shouldClearRange = false; bool shouldClearRange = false;
auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1,
const nsINode* aNode2, const uint32_t aOffset2) {
if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>(
aNode1, aOffset1, aNode2, aOffset2);
}
return nsContentUtils::ComparePointsWithIndices<
TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2);
};
const Maybe<int32_t> anchorOldFocusOrder = const Maybe<int32_t> anchorOldFocusOrder =
nsContentUtils::ComparePointsWithIndices(anchorNode, anchorOffset, ComparePoints(anchorNode, anchorOffset, focusNode, focusOffset);
focusNode, focusOffset);
shouldClearRange |= !anchorOldFocusOrder; shouldClearRange |= !anchorOldFocusOrder;
const Maybe<int32_t> oldFocusNewFocusOrder = const Maybe<int32_t> oldFocusNewFocusOrder =
nsContentUtils::ComparePointsWithIndices(focusNode, focusOffset, ComparePoints(focusNode, focusOffset, &aContainer, aOffset);
&aContainer, aOffset);
shouldClearRange |= !oldFocusNewFocusOrder; shouldClearRange |= !oldFocusNewFocusOrder;
const Maybe<int32_t> anchorNewFocusOrder = const Maybe<int32_t> anchorNewFocusOrder =
nsContentUtils::ComparePointsWithIndices(anchorNode, anchorOffset, ComparePoints(anchorNode, anchorOffset, &aContainer, aOffset);
&aContainer, aOffset);
shouldClearRange |= !anchorNewFocusOrder; shouldClearRange |= !anchorNewFocusOrder;
// If the points are disconnected, the range will be collapsed below, // If the points are disconnected, the range will be collapsed below,
@@ -4231,7 +4249,10 @@ void Selection::SetBaseAndExtentInternal(InLimiter aInLimiter,
// new nsRange instance? // new nsRange instance?
SelectionBatcher batch(this, __FUNCTION__); SelectionBatcher batch(this, __FUNCTION__);
const Maybe<int32_t> order = const Maybe<int32_t> order =
nsContentUtils::ComparePoints(aAnchorRef, aFocusRef); StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()
? nsContentUtils::ComparePoints<TreeKind::Flat>(aAnchorRef, aFocusRef)
: nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
aAnchorRef, aFocusRef);
if (order && (*order <= 0)) { if (order && (*order <= 0)) {
SetStartAndEndInternal(aInLimiter, aAnchorRef, aFocusRef, eDirNext, aRv); SetStartAndEndInternal(aInLimiter, aAnchorRef, aFocusRef, eDirNext, aRv);
return; return;

View File

@@ -486,31 +486,61 @@ mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
int32_t nsContentUtils::sInnerOrOuterWindowCount = 0; int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0; uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary, const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
NodeIndexCache* aIndexCache); NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
NodeIndexCache* aIndexCache);
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const RangeBoundary& aFirstBoundary, const RangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const RangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const RawRangeBoundary& aFirstBoundary, const RawRangeBoundary& aFirstBoundary,
const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const RawRangeBoundary& aFirstBoundary,
const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const RawRangeBoundary& aFirstBoundary, const RawRangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const RawRangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const RangeBoundary& aFirstBoundary, const RangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints( template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const RangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const ConstRawRangeBoundary& aFirstBoundary, const ConstRawRangeBoundary& aFirstBoundary,
const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RawRangeBoundary& aFirstBoundary, template Maybe<int32_t>
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
template Maybe<int32_t> nsContentUtils::ComparePoints(
const ConstRawRangeBoundary& aFirstBoundary, const ConstRawRangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const ConstRawRangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints<TreeKind::Flat>(
const ConstRawRangeBoundary& aFirstBoundary, const ConstRawRangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache); const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
@@ -756,9 +786,10 @@ static auto* GetFlattenedTreeParent(const nsIContent* aContent) {
return aContent->GetFlattenedTreeParent(); return aContent->GetFlattenedTreeParent();
} }
static auto* GetFlattenedTreeParentNodeForSelection( static nsIContent* GetFlattenedTreeParentNodeForSelection(
const nsIContent* aContent) { const nsIContent* aNode) {
return aContent->GetFlattenedTreeParentNodeForSelection(); nsINode* parent = aNode->GetFlattenedTreeParentNodeForSelection();
return parent && parent->IsContent() ? parent->AsContent() : nullptr;
} }
static auto* GetFlattenedTreeParentElementForStyle(const Element* aElement) { static auto* GetFlattenedTreeParentElementForStyle(const Element* aElement) {
@@ -771,6 +802,17 @@ static auto* GetParentBrowserParent(const BrowserParent* aBrowserParent) {
: nullptr; : nullptr;
} }
static bool AreNodesInSameSlot(const nsINode* aNode1, const nsINode* aNode2) {
if (auto* content1 = nsIContent::FromNodeOrNull(aNode1)) {
if (auto* slot = content1->GetAssignedSlot()) {
if (auto* content2 = nsIContent::FromNodeOrNull(aNode2)) {
return slot == content2->GetAssignedSlot();
}
}
}
return false;
}
template <typename Node1, typename Node2, typename GetParentFunc> template <typename Node1, typename Node2, typename GetParentFunc>
class MOZ_STACK_CLASS CommonAncestors final { class MOZ_STACK_CLASS CommonAncestors final {
public: public:
@@ -825,9 +867,12 @@ class MOZ_STACK_CLASS CommonAncestors final {
return GetClosestCommonAncestorChild(mInclusiveAncestors2); return GetClosestCommonAncestorChild(mInclusiveAncestors2);
} }
template <TreeKind aKind>
void WarnIfClosestCommonAncestorChildrenAreNotInChildList() const { void WarnIfClosestCommonAncestorChildrenAreNotInChildList() const {
WarnIfClosestCommonAncestorChildIsNotInChildList(mInclusiveAncestors1); WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
WarnIfClosestCommonAncestorChildIsNotInChildList(mInclusiveAncestors2); mInclusiveAncestors1);
WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
mInclusiveAncestors2);
} }
private: private:
@@ -869,7 +914,9 @@ class MOZ_STACK_CLASS CommonAncestors final {
return child; return child;
} }
template <typename Node> template <TreeKind aKind, typename Node,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
void WarnIfClosestCommonAncestorChildIsNotInChildList( void WarnIfClosestCommonAncestorChildIsNotInChildList(
const nsTArray<Node*>& aInclusiveAncestors) const { const nsTArray<Node*>& aInclusiveAncestors) const {
#ifdef DEBUG #ifdef DEBUG
@@ -878,8 +925,24 @@ class MOZ_STACK_CLASS CommonAncestors final {
if (!child) { if (!child) {
return; return;
} }
const Maybe<uint32_t> childIndex =
mClosestCommonAncestor->ComputeIndexOf(child); if (mClosestCommonAncestor->GetShadowRoot() == child) {
return;
}
Maybe<uint32_t> childIndex;
if constexpr (aKind == TreeKind::Flat) {
if (auto* slot = HTMLSlotElement::FromNode(mClosestCommonAncestor)) {
auto index = slot->AssignedNodes().IndexOf(child);
if (index != nsTArray<RefPtr<nsINode>>::NoIndex) {
childIndex = Some(index);
}
}
}
if (childIndex.isNothing()) {
childIndex = mClosestCommonAncestor->ComputeIndexOf(child);
}
if (MOZ_LIKELY(childIndex.isSome())) { if (MOZ_LIKELY(childIndex.isSome())) {
return; return;
} }
@@ -3163,6 +3226,7 @@ Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
} }
/* static */ /* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::CompareChildNodes( Maybe<int32_t> nsContentUtils::CompareChildNodes(
const nsINode* aChild1, const nsINode* aChild2, const nsINode* aChild1, const nsINode* aChild2,
NodeIndexCache* aIndexCache /* = nullptr */) { NodeIndexCache* aIndexCache /* = nullptr */) {
@@ -3185,6 +3249,23 @@ Maybe<int32_t> nsContentUtils::CompareChildNodes(
MOZ_ASSERT(aChild1->GetParentOrShadowHostNode()); MOZ_ASSERT(aChild1->GetParentOrShadowHostNode());
return Some(-1); return Some(-1);
} }
if constexpr (aKind == TreeKind::Flat) {
if (AreNodesInSameSlot(aChild1, aChild2)) {
// They differ at slot, so use their position in slot
const auto* slot = aChild1->AsContent()->GetAssignedSlot();
MOZ_ASSERT(slot);
auto child1Index = slot->AssignedNodes().IndexOf(aChild1);
auto child2Index = slot->AssignedNodes().IndexOf(aChild2);
MOZ_ASSERT(child1Index != nsTArray<RefPtr<nsINode>>::NoIndex);
MOZ_ASSERT(child2Index != nsTArray<RefPtr<nsINode>>::NoIndex);
return Some(child1Index < child2Index ? -1 : 1);
}
}
MOZ_ASSERT(aChild1->GetParentOrShadowHostNode()); MOZ_ASSERT(aChild1->GetParentOrShadowHostNode());
const nsINode& commonParentNode = *aChild1->GetParentOrShadowHostNode(); const nsINode& commonParentNode = *aChild1->GetParentOrShadowHostNode();
MOZ_ASSERT(aChild2->GetParentOrShadowHostNode() == &commonParentNode); MOZ_ASSERT(aChild2->GetParentOrShadowHostNode() == &commonParentNode);
@@ -3253,9 +3334,10 @@ Maybe<int32_t> nsContentUtils::CompareChildNodes(
} }
/* static */ /* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren( Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
const nsINode& aParent, const nsINode* aChild1, const nsINode* aChild2, const nsINode& aParent, const nsINode* aChild1, const nsINode* aChild2,
nsContentUtils::NodeIndexCache* aIndexCache = nullptr) { nsContentUtils::NodeIndexCache* aIndexCache) {
MOZ_ASSERT_IF(aChild1, GetParentOrShadowHostNode(aChild1)); MOZ_ASSERT_IF(aChild1, GetParentOrShadowHostNode(aChild1));
MOZ_ASSERT_IF(aChild2, GetParentOrShadowHostNode(aChild2)); MOZ_ASSERT_IF(aChild2, GetParentOrShadowHostNode(aChild2));
@@ -3280,7 +3362,7 @@ Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
return Some(1); return Some(1);
} }
const Maybe<int32_t> comp = const Maybe<int32_t> comp =
nsContentUtils::CompareChildNodes(aChild1, aChild2, aIndexCache); nsContentUtils::CompareChildNodes<aKind>(aChild1, aChild2, aIndexCache);
if (MOZ_UNLIKELY(comp.isNothing())) { if (MOZ_UNLIKELY(comp.isNothing())) {
NS_ASSERTION(comp.isSome(), NS_ASSERTION(comp.isSome(),
"nsContentUtils::CompareChildNodes() must return Some here. " "nsContentUtils::CompareChildNodes() must return Some here. "
@@ -3290,14 +3372,16 @@ Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
return Some(1); return Some(1);
} }
MOZ_ASSERT_IF(!*comp, aChild1 == aChild2); MOZ_ASSERT_IF(!*comp, aChild1 == aChild2);
MOZ_ASSERT_IF(*comp < 0, (aChild1 ? *aChild1->ComputeIndexInParentNode() MOZ_ASSERT_IF(*comp < 0 && !AreNodesInSameSlot(aChild1, aChild2),
: aParent.GetChildCount()) < (aChild1 ? *aChild1->ComputeIndexInParentNode()
(aChild2 ? *aChild2->ComputeIndexInParentNode() : aParent.GetChildCount()) <
: aParent.GetChildCount())); (aChild2 ? *aChild2->ComputeIndexInParentNode()
MOZ_ASSERT_IF(*comp > 0, (aChild2 ? *aChild2->ComputeIndexInParentNode() : aParent.GetChildCount()));
: aParent.GetChildCount()) < MOZ_ASSERT_IF(*comp > 0 && !AreNodesInSameSlot(aChild1, aChild2),
(aChild1 ? *aChild1->ComputeIndexInParentNode() (aChild2 ? *aChild2->ComputeIndexInParentNode()
: aParent.GetChildCount())); : aParent.GetChildCount()) <
(aChild1 ? *aChild1->ComputeIndexInParentNode()
: aParent.GetChildCount()));
return comp; return comp;
} }
@@ -3349,6 +3433,7 @@ Maybe<int32_t> nsContentUtils::CompareChildNodeAndChildOffset(
} }
/* static */ /* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::ComparePointsWithIndices( Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
uint32_t aOffset2, NodeIndexCache* aIndexCache) { uint32_t aOffset2, NodeIndexCache* aIndexCache) {
@@ -3359,8 +3444,17 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
return Some(aOffset1 < aOffset2 ? -1 : (aOffset1 > aOffset2 ? 1 : 0)); return Some(aOffset1 < aOffset2 ? -1 : (aOffset1 > aOffset2 ? 1 : 0));
} }
const CommonAncestors commonAncestors(*aParent1, *aParent2, auto GetParentFunc = [](const nsINode* aNode) -> nsINode* {
GetParentOrShadowHostNode); MOZ_ASSERT(aNode);
if constexpr (aKind == TreeKind::Flat) {
if (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()) {
return aNode->GetFlattenedTreeParentNodeForSelection();
}
}
return aNode->GetParentOrShadowHostNode();
};
const CommonAncestors commonAncestors(*aParent1, *aParent2, GetParentFunc);
if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) { if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
return Nothing(); return Nothing();
@@ -3371,16 +3465,22 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
const nsINode* closestCommonAncestorChild2 = const nsINode* closestCommonAncestorChild2 =
commonAncestors.GetClosestCommonAncestorChild2(); commonAncestors.GetClosestCommonAncestorChild2();
MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2); MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
commonAncestors.WarnIfClosestCommonAncestorChildrenAreNotInChildList(); commonAncestors
.template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
if (closestCommonAncestorChild1 && closestCommonAncestorChild2) { if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
return CompareClosestCommonAncestorChildren( return CompareClosestCommonAncestorChildren<aKind>(
*commonAncestors.GetClosestCommonAncestor(), *commonAncestors.GetClosestCommonAncestor(),
closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache); closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
} }
if (closestCommonAncestorChild2) { if (closestCommonAncestorChild2) {
MOZ_ASSERT(closestCommonAncestorChild2->GetParentOrShadowHostNode() == MOZ_ASSERT(GetParentFunc(closestCommonAncestorChild2) == aParent1);
aParent1); if (aParent1->GetShadowRoot() == closestCommonAncestorChild2) {
// Comparing a shadow host with its shadow root.
// We consider: [host, 0] < anything in shadow root < [host, 1]
return aOffset1 > 0 ? Some(-1) : Some(1);
}
// FIXME: bug 1946001, bug 1946003 and bug 1946008. // FIXME: bug 1946001, bug 1946003 and bug 1946008.
if (MOZ_UNLIKELY( if (MOZ_UNLIKELY(
closestCommonAncestorChild2->IsRootOfNativeAnonymousSubtree() || closestCommonAncestorChild2->IsRootOfNativeAnonymousSubtree() ||
@@ -3407,9 +3507,14 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
return comp; return comp;
} }
if (aParent2->GetShadowRoot() == closestCommonAncestorChild1) {
// Comparing a shadow host with its shadow root.
// We consider: [host, 0] < anything in shadow root < [host, 1]
return aOffset2 > 0 ? Some(-1) : Some(1);
}
MOZ_ASSERT(closestCommonAncestorChild1); MOZ_ASSERT(closestCommonAncestorChild1);
MOZ_ASSERT(closestCommonAncestorChild1->GetParentOrShadowHostNode() == MOZ_ASSERT(GetParentFunc(closestCommonAncestorChild1) == aParent2);
aParent2);
// FIXME: bug 1946001, bug 1946003 and bug 1946008. // FIXME: bug 1946001, bug 1946003 and bug 1946008.
if (MOZ_UNLIKELY( if (MOZ_UNLIKELY(
closestCommonAncestorChild1->IsRootOfNativeAnonymousSubtree() || closestCommonAncestorChild1->IsRootOfNativeAnonymousSubtree() ||
@@ -3499,7 +3604,8 @@ Element* nsContentUtils::GetTargetElement(Document* aDocument,
} }
/* static */ /* static */
template <typename PT1, typename RT1, typename PT2, typename RT2> template <TreeKind aKind, typename PT1, typename RT1, typename PT2,
typename RT2, typename Dummy>
Maybe<int32_t> nsContentUtils::ComparePoints( Maybe<int32_t> nsContentUtils::ComparePoints(
const RangeBoundaryBase<PT1, RT1>& aBoundary1, const RangeBoundaryBase<PT1, RT1>& aBoundary1,
const RangeBoundaryBase<PT2, RT2>& aBoundary2, const RangeBoundaryBase<PT2, RT2>& aBoundary2,
@@ -3516,7 +3622,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// offset in the container. If both instances have computed offset, we can // offset in the container. If both instances have computed offset, we can
// use ComparePointsWithIndices() which works with offsets. // use ComparePointsWithIndices() which works with offsets.
if (aBoundary1.HasOffset() && aBoundary2.HasOffset()) { if (aBoundary1.HasOffset() && aBoundary2.HasOffset()) {
return ComparePointsWithIndices( return ComparePointsWithIndices<aKind>(
aBoundary1.GetContainer(), *aBoundary1.Offset(kValidOrInvalidOffsets1), aBoundary1.GetContainer(), *aBoundary1.Offset(kValidOrInvalidOffsets1),
aBoundary2.GetContainer(), *aBoundary2.Offset(kValidOrInvalidOffsets2), aBoundary2.GetContainer(), *aBoundary2.Offset(kValidOrInvalidOffsets2),
aIndexCache); aIndexCache);
@@ -3534,18 +3640,27 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
if (aBoundary1.GetContainer() == aBoundary2.GetContainer()) { if (aBoundary1.GetContainer() == aBoundary2.GetContainer()) {
const nsIContent* const child1 = aBoundary1.GetChildAtOffset(); const nsIContent* const child1 = aBoundary1.GetChildAtOffset();
const nsIContent* const child2 = aBoundary2.GetChildAtOffset(); const nsIContent* const child2 = aBoundary2.GetChildAtOffset();
return CompareClosestCommonAncestorChildren(*aBoundary1.GetContainer(), return CompareClosestCommonAncestorChildren<aKind>(
child1, child2, aIndexCache); *aBoundary1.GetContainer(), child1, child2, aIndexCache);
} }
auto GetParentFunc = [](const nsINode* aNode) -> nsINode* {
MOZ_ASSERT(aNode);
if constexpr (aKind == TreeKind::Flat) {
if (aNode->IsContent() && aNode->AsContent()->GetAssignedSlot()) {
return aNode->GetFlattenedTreeParentNodeForSelection();
}
}
return aNode->GetParentOrShadowHostNode();
};
// Otherwise, we need to compare the common ancestor children which is the // Otherwise, we need to compare the common ancestor children which is the
// most distant different inclusive ancestors of the containers. So, the // most distant different inclusive ancestors of the containers. So, the
// following implementation is similar to ComparePointsWithIndices(), but we // following implementation is similar to ComparePointsWithIndices(), but we
// don't have offset, so, we cannot use offset when we compare the boundaries // don't have offset, so, we cannot use offset when we compare the boundaries
// whose one is a descendant of the other. // whose one is a descendant of the other.
const CommonAncestors commonAncestors(*aBoundary1.GetContainer(), const CommonAncestors commonAncestors(
*aBoundary2.GetContainer(), *aBoundary1.GetContainer(), *aBoundary2.GetContainer(), GetParentFunc);
GetParentOrShadowHostNode);
if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) { if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
return Nothing(); return Nothing();
@@ -3556,10 +3671,11 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
commonAncestors.GetClosestCommonAncestorChild1(); commonAncestors.GetClosestCommonAncestorChild1();
const nsINode* closestCommonAncestorChild2 = const nsINode* closestCommonAncestorChild2 =
commonAncestors.GetClosestCommonAncestorChild2(); commonAncestors.GetClosestCommonAncestorChild2();
commonAncestors.WarnIfClosestCommonAncestorChildrenAreNotInChildList(); commonAncestors
.template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2); MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
if (closestCommonAncestorChild1 && closestCommonAncestorChild2) { if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
return CompareClosestCommonAncestorChildren( return CompareClosestCommonAncestorChildren<aKind>(
*commonAncestors.GetClosestCommonAncestor(), *commonAncestors.GetClosestCommonAncestor(),
closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache); closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
} }
@@ -3574,7 +3690,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// XXX Keep the odd traditional behavior for now. // XXX Keep the odd traditional behavior for now.
return Some(1); return Some(1);
} }
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes( const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2, aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2,
aIndexCache); aIndexCache);
if (NS_WARN_IF(comp.isNothing())) { if (NS_WARN_IF(comp.isNothing())) {
@@ -3612,7 +3728,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// XXX Keep the odd traditional behavior for now. // XXX Keep the odd traditional behavior for now.
return Some(-1); return Some(-1);
} }
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes( const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
closestCommonAncestorChild1, aBoundary2.GetChildAtOffset(), aIndexCache); closestCommonAncestorChild1, aBoundary2.GetChildAtOffset(), aIndexCache);
if (NS_WARN_IF(comp.isNothing())) { if (NS_WARN_IF(comp.isNothing())) {
NS_ASSERTION(comp.isSome(), NS_ASSERTION(comp.isSome(),

View File

@@ -714,6 +714,9 @@ class nsContentUtils {
* 0 if point1 == point2. * 0 if point1 == point2.
* `Nothing` if the two nodes aren't in the same connected subtree. * `Nothing` if the two nodes aren't in the same connected subtree.
*/ */
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> ComparePointsWithIndices( static mozilla::Maybe<int32_t> ComparePointsWithIndices(
const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2, const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
uint32_t aOffset2, NodeIndexCache* aIndexCache = nullptr); uint32_t aOffset2, NodeIndexCache* aIndexCache = nullptr);
@@ -728,7 +731,10 @@ class nsContentUtils {
* 0 if point1 == point2. * 0 if point1 == point2.
* `Nothing` if the two nodes aren't in the same connected subtree. * `Nothing` if the two nodes aren't in the same connected subtree.
*/ */
template <typename PT1, typename RT1, typename PT2, typename RT2> template <TreeKind aKind = TreeKind::ShadowIncludingDOM, typename PT1,
typename RT1, typename PT2, typename RT2,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> ComparePoints( static mozilla::Maybe<int32_t> ComparePoints(
const mozilla::RangeBoundaryBase<PT1, RT1>& aBoundary1, const mozilla::RangeBoundaryBase<PT1, RT1>& aBoundary1,
const mozilla::RangeBoundaryBase<PT2, RT2>& aBoundary2, const mozilla::RangeBoundaryBase<PT2, RT2>& aBoundary2,
@@ -744,6 +750,9 @@ class nsContentUtils {
* traditional behavior. If you want to use this in new code, it means that * traditional behavior. If you want to use this in new code, it means that
* you **should** check the offset values and call `ComparePoints` instead. * you **should** check the offset values and call `ComparePoints` instead.
*/ */
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> ComparePoints_AllowNegativeOffsets( static mozilla::Maybe<int32_t> ComparePoints_AllowNegativeOffsets(
const nsINode* aParent1, int64_t aOffset1, const nsINode* aParent2, const nsINode* aParent1, int64_t aOffset1, const nsINode* aParent2,
int64_t aOffset2) { int64_t aOffset2) {
@@ -766,7 +775,7 @@ class nsContentUtils {
} }
// Otherwise, aOffset1 nor aOffset2 is referred so that any value is fine // Otherwise, aOffset1 nor aOffset2 is referred so that any value is fine
// if negative. // if negative.
return ComparePointsWithIndices( return ComparePointsWithIndices<aKind>(
aParent1, aParent1,
// Avoid warnings. // Avoid warnings.
aOffset1 < 0 ? aParent1->GetChildCount() aOffset1 < 0 ? aParent1->GetChildCount()
@@ -778,7 +787,8 @@ class nsContentUtils {
: std::min(static_cast<uint32_t>(aOffset2), : std::min(static_cast<uint32_t>(aOffset2),
aParent2->GetChildCount())); aParent2->GetChildCount()));
} }
return ComparePointsWithIndices(aParent1, aOffset1, aParent2, aOffset2); return ComparePointsWithIndices<aKind>(aParent1, aOffset1, aParent2,
aOffset2);
} }
/** /**
@@ -3634,6 +3644,9 @@ class nsContentUtils {
* node. * node.
* Return Nothing if aChild1 is a root of the native anonymous subtree. * Return Nothing if aChild1 is a root of the native anonymous subtree.
*/ */
template <TreeKind aKind,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> CompareChildNodes( static mozilla::Maybe<int32_t> CompareChildNodes(
const nsINode* aChild1, const nsINode* aChild2, const nsINode* aChild1, const nsINode* aChild2,
NodeIndexCache* aIndexCache = nullptr); NodeIndexCache* aIndexCache = nullptr);
@@ -3663,8 +3676,12 @@ class nsContentUtils {
* includes odd traditional behavior. Therefore, do not use this method as a * includes odd traditional behavior. Therefore, do not use this method as a
* utility method. * utility method.
*/ */
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> CompareClosestCommonAncestorChildren( static mozilla::Maybe<int32_t> CompareClosestCommonAncestorChildren(
const nsINode&, const nsINode*, const nsINode*, NodeIndexCache*); const nsINode&, const nsINode*, const nsINode*,
NodeIndexCache* = nullptr);
static nsIXPConnect* sXPConnect; static nsIXPConnect* sXPConnect;

View File

@@ -144,9 +144,8 @@ inline nsINode* nsINode::GetFlattenedTreeParentNodeForStyle() const {
return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this); return ::GetFlattenedTreeParentNode<nsINode::eForStyle>(this);
} }
inline nsIContent* nsINode::GetFlattenedTreeParentNodeForSelection() const { inline nsINode* nsINode::GetFlattenedTreeParentNodeForSelection() const {
nsINode* parent = ::GetFlattenedTreeParentNode<nsINode::eForSelection>(this); return ::GetFlattenedTreeParentNode<nsINode::eForSelection>(this);
return (parent && parent->IsContent()) ? parent->AsContent() : nullptr;
} }
inline bool nsINode::NodeOrAncestorHasDirAuto() const { inline bool nsINode::NodeOrAncestorHasDirAuto() const {

View File

@@ -343,16 +343,28 @@ class IsItemInRangeComparator {
} }
int operator()(const AbstractRange* const aRange) const { int operator()(const AbstractRange* const aRange) const {
Maybe<int32_t> cmp = nsContentUtils::ComparePoints( auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1,
ConstRawRangeBoundary(&mNode, mEndOffset, const nsINode* aNode2, const uint32_t aOffset2,
RangeBoundaryIsMutationObserved::No), nsContentUtils::NodeIndexCache* aCache) {
aRange->MayCrossShadowBoundaryStartRef(), mCache); if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
if (cmp.valueOr(1) == 1) { return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>(
cmp = nsContentUtils::ComparePoints( aNode1, aOffset1, aNode2, aOffset2, aCache);
ConstRawRangeBoundary(&mNode, mStartOffset, }
RangeBoundaryIsMutationObserved::No), return nsContentUtils::ComparePointsWithIndices<
aRange->MayCrossShadowBoundaryEndRef(), mCache); TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2,
if (cmp.valueOr(1) == -1) { aCache);
};
Maybe<int32_t> cmp = ComparePoints(
&mNode, mEndOffset, aRange->GetMayCrossShadowBoundaryStartContainer(),
aRange->MayCrossShadowBoundaryStartOffset(), mCache);
MOZ_ASSERT(cmp.isSome()); // Should always be connected at this point.
if (cmp.value() == 1) {
cmp = ComparePoints(&mNode, mStartOffset,
aRange->GetMayCrossShadowBoundaryEndContainer(),
aRange->MayCrossShadowBoundaryEndOffset(), mCache);
MOZ_ASSERT(cmp.isSome());
if (cmp.value() == -1) {
return 0; return 0;
} }
return 1; return 1;

View File

@@ -1190,7 +1190,7 @@ class nsINode : public mozilla::dom::EventTarget {
* 2. For contents that are slotted into a UA shadow tree, use its * 2. For contents that are slotted into a UA shadow tree, use its
* parent rather than the slot element. * parent rather than the slot element.
*/ */
inline nsIContent* GetFlattenedTreeParentNodeForSelection() const; inline nsINode* GetFlattenedTreeParentNodeForSelection() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElement() const; inline mozilla::dom::Element* GetFlattenedTreeParentElement() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElementForStyle() const; inline mozilla::dom::Element* GetFlattenedTreeParentElementForStyle() const;

View File

@@ -285,12 +285,21 @@ static RangeBehaviour GetRangeBehaviour(
const RangeBoundary& otherSideExistingBoundary = const RangeBoundary& otherSideExistingBoundary =
aIsSetStart ? aRange->EndRef() : aRange->StartRef(); aIsSetStart ? aRange->EndRef() : aRange->StartRef();
auto ComparePoints = [aAllowCrossShadowBoundary](
const RawRangeBoundary& aBoundary1,
const RawRangeBoundary& aBoundary2) {
if (aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
return nsContentUtils::ComparePoints<TreeKind::Flat>(aBoundary1,
aBoundary2);
}
return nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
aBoundary1, aBoundary2);
};
// Both bondaries are in the same root, now check for their position // Both bondaries are in the same root, now check for their position
const Maybe<int32_t> order = const Maybe<int32_t> order =
aIsSetStart ? nsContentUtils::ComparePoints(aNewBoundary, aIsSetStart
otherSideExistingBoundary) ? ComparePoints(aNewBoundary, otherSideExistingBoundary.AsRaw())
: nsContentUtils::ComparePoints(otherSideExistingBoundary, : ComparePoints(otherSideExistingBoundary.AsRaw(), aNewBoundary);
aNewBoundary);
if (order) { if (order) {
if (*order != 1) { if (*order != 1) {
@@ -324,11 +333,12 @@ static RangeBehaviour GetRangeBehaviour(
// otherSideExistingBoundary. However, it's possible that aNewBoundary // otherSideExistingBoundary. However, it's possible that aNewBoundary
// is valid with the otherSideExistingCrossShadowBoundaryBoundary. // is valid with the otherSideExistingCrossShadowBoundaryBoundary.
const Maybe<int32_t> withCrossShadowBoundaryOrder = const Maybe<int32_t> withCrossShadowBoundaryOrder =
aIsSetStart aIsSetStart ? ComparePoints(
? nsContentUtils::ComparePoints( aNewBoundary,
aNewBoundary, otherSideExistingCrossShadowBoundaryBoundary) otherSideExistingCrossShadowBoundaryBoundary.AsRaw())
: nsContentUtils::ComparePoints( : ComparePoints(
otherSideExistingCrossShadowBoundaryBoundary, aNewBoundary); otherSideExistingCrossShadowBoundaryBoundary.AsRaw(),
aNewBoundary);
// Valid to the cross boundary boundary. // Valid to the cross boundary boundary.
if (withCrossShadowBoundaryOrder && *withCrossShadowBoundaryOrder != 1) { if (withCrossShadowBoundaryOrder && *withCrossShadowBoundaryOrder != 1) {
@@ -3246,8 +3256,7 @@ void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) {
nsINode* node = preOrderIter.GetCurrentNode(); nsINode* node = preOrderIter.GetCurrentNode();
preOrderIter.Next(); preOrderIter.Next();
bool selectable = true; bool selectable = true;
nsIContent* content = nsIContent* content = nsIContent::FromNodeOrNull(node);
node && node->IsContent() ? node->AsContent() : nullptr;
if (content) { if (content) {
if (firstNonSelectableContent && if (firstNonSelectableContent &&
ExcludeIfNextToNonSelectable(content)) { ExcludeIfNextToNonSelectable(content)) {

View File

@@ -2829,9 +2829,10 @@ HTMLEditUtils::ComputePointToPutCaretInElementIfOutside(
// Use range boundaries and RangeUtils::CompareNodeToRange() to compare // Use range boundaries and RangeUtils::CompareNodeToRange() to compare
// selection start to new block. // selection start to new block.
bool nodeBefore, nodeAfter; bool nodeBefore, nodeAfter;
nsresult rv = RangeUtils::CompareNodeToRangeBoundaries( nsresult rv =
const_cast<Element*>(&aElement), aCurrentPoint.ToRawRangeBoundary(), RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
aCurrentPoint.ToRawRangeBoundary(), &nodeBefore, &nodeAfter); const_cast<Element*>(&aElement), aCurrentPoint.ToRawRangeBoundary(),
aCurrentPoint.ToRawRangeBoundary(), &nodeBefore, &nodeAfter);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("RangeUtils::CompareNodeToRange() failed"); NS_WARNING("RangeUtils::CompareNodeToRange() failed");
return Err(rv); return Err(rv);

View File

@@ -5063,7 +5063,8 @@ nsRect PresShell::ClipListToRange(nsDisplayListBuilder* aBuilder,
// if the node is within the range, append it to the temporary list // if the node is within the range, append it to the temporary list
bool before, after; bool before, after;
nsresult rv = nsresult rv =
RangeUtils::CompareNodeToRange(content, aRange, &before, &after); RangeUtils::CompareNodeToRange<TreeKind::ShadowIncludingDOM>(
content, aRange, &before, &after);
if (NS_SUCCEEDED(rv) && !before && !after) { if (NS_SUCCEEDED(rv) && !before && !after) {
itemToInsert = i; itemToInsert = i;
bool snap; bool snap;