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-09 17:16:36 +00:00 committed by sefeng@mozilla.com
parent 4381c6ea7b
commit a8fbcd54a1
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,
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,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
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,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
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,
const RangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
bool* aNodeIsAfterRange);
template nsresult RangeUtils::CompareNodeToRangeBoundaries(
template nsresult
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
nsINode* aNode, const RawRangeBoundary& aStartBoundary,
const RawRangeBoundary& aEndBoundary, bool* aNodeIsBeforeRange,
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(
const nsINode& aNode) {
@ -67,17 +100,6 @@ template nsresult RangeUtils::CompareNodeToRangeBoundaries(
return true;
}
// static
nsINode* RangeUtils::GetParentNodeInSameSelection(const nsINode* aNode) {
if (MOZ_UNLIKELY(!aNode)) {
return nullptr;
}
if (!ParentNodeIsInSameSelection(*aNode)) {
return nullptr;
}
return aNode->GetParentNode();
}
// static
nsINode* RangeUtils::ComputeRootNode(nsINode* aNode) {
if (!aNode) {
@ -150,13 +172,14 @@ bool RangeUtils::IsValidPoints(
}
// static
template <TreeKind aKind, typename Dummy>
Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode,
AbstractRange* aAbstractRange) {
bool nodeIsBeforeRange{false};
bool nodeIsAfterRange{false};
const nsresult rv = CompareNodeToRange(&aNode, aAbstractRange,
&nodeIsBeforeRange, &nodeIsAfterRange);
const nsresult rv = CompareNodeToRange<aKind>(
&aNode, aAbstractRange, &nodeIsBeforeRange, &nodeIsAfterRange);
if (NS_FAILED(rv)) {
return Nothing();
}
@ -172,6 +195,7 @@ Maybe<bool> RangeUtils::IsNodeContainedInRange(nsINode& aNode,
// XXX - callers responsibility to ensure node in same doc as range!
// static
template <TreeKind aKind, typename Dummy>
nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
AbstractRange* aAbstractRange,
bool* aNodeIsBeforeRange,
@ -180,12 +204,13 @@ nsresult RangeUtils::CompareNodeToRange(nsINode* aNode,
NS_WARN_IF(!aAbstractRange->IsPositioned())) {
return NS_ERROR_INVALID_ARG;
}
return CompareNodeToRangeBoundaries(
return CompareNodeToRangeBoundaries<aKind>(
aNode, aAbstractRange->MayCrossShadowBoundaryStartRef(),
aAbstractRange->MayCrossShadowBoundaryEndRef(), aNodeIsBeforeRange,
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(
nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange,
@ -208,7 +233,17 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
// gather up the dom point info
int32_t nodeStart;
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) {
// can't make a parent/offset pair to represent start or
// end of the root node, because it has no parent.
@ -216,6 +251,14 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
parent = aNode;
nodeStart = 0;
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 {
nodeStart = parent->ComputeIndexOf_Deprecated(aNode);
NS_WARNING_ASSERTION(
@ -240,17 +283,19 @@ nsresult RangeUtils::CompareNodeToRangeBoundaries(
// silence the warning. (Bug 1438996)
// is RANGE(start) <= NODE(start) ?
Maybe<int32_t> order = nsContentUtils::ComparePoints_AllowNegativeOffsets(
aStartBoundary.GetContainer(),
*aStartBoundary.Offset(
RangeBoundaryBase<SPT, SRT>::OffsetFilter::kValidOrInvalidOffsets),
parent, nodeStart);
Maybe<int32_t> order =
nsContentUtils::ComparePoints_AllowNegativeOffsets<aKind>(
aStartBoundary.GetContainer(),
*aStartBoundary.Offset(
RangeBoundaryBase<SPT,
SRT>::OffsetFilter::kValidOrInvalidOffsets),
parent, nodeStart);
if (NS_WARN_IF(!order)) {
return NS_ERROR_DOM_WRONG_DOCUMENT_ERR;
}
*aNodeIsBeforeRange = *order > 0;
// is RANGE(end) >= NODE(end) ?
order = nsContentUtils::ComparePointsWithIndices(
order = nsContentUtils::ComparePointsWithIndices<aKind>(
aEndBoundary.GetContainer(),
*aEndBoundary.Offset(
RangeBoundaryBase<EPT, ERT>::OffsetFilter::kValidOrInvalidOffsets),
@ -315,10 +360,17 @@ nsINode* ShadowDOMSelectionHelpers::GetParentNodeInSameSelection(
if (!ParentNodeIsInSameSelection(aNode)) {
return nullptr;
}
return (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
aAllowCrossShadowBoundary)
? aNode.GetParentOrShadowHostNode()
: aNode.GetParentNode();
if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled() &&
aAllowCrossShadowBoundary == AllowRangeCrossShadowBoundary::Yes) {
if (aNode.IsContent()) {
if (HTMLSlotElement* slot = aNode.AsContent()->GetAssignedSlot()) {
return slot;
}
}
return aNode.GetParentOrShadowHostNode();
}
return aNode.GetParentNode();
}
// static

View file

@ -11,6 +11,7 @@
#include "mozilla/RangeBoundary.h"
#include "nsIContent.h"
#include "nsINode.h"
#include "nsContentUtils.h"
namespace mozilla {
@ -55,8 +56,6 @@ class RangeUtils final {
using AbstractRange = dom::AbstractRange;
public:
static nsINode* GetParentNodeInSameSelection(const nsINode* aNode);
/**
* GetRawRangeBoundaryBefore() and GetRawRangeBoundaryAfter() retrieve
* 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.
*/
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static Maybe<bool> IsNodeContainedInRange(nsINode& aNode,
AbstractRange* aAbstractRange);
@ -145,12 +147,18 @@ class RangeUtils final {
* ends after a range. If neither it is contained inside the 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,
AbstractRange* aAbstractRange,
bool* aNodeIsBeforeRange,
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(
nsINode* aNode, const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary, bool* aNodeIsBeforeRange,

View file

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

View file

@ -487,31 +487,61 @@ mozilla::LazyLogModule nsContentUtils::sDOMDumpLog("Dump");
int32_t nsContentUtils::sInnerOrOuterWindowCount = 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,
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 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 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& 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 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 RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RawRangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
template Maybe<int32_t>
nsContentUtils::ComparePoints<TreeKind::ShadowIncludingDOM>(
const ConstRawRangeBoundary& aFirstBoundary,
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& aSecondBoundary, NodeIndexCache* aIndexCache);
@ -757,9 +787,10 @@ static auto* GetFlattenedTreeParent(const nsIContent* aContent) {
return aContent->GetFlattenedTreeParent();
}
static auto* GetFlattenedTreeParentNodeForSelection(
const nsIContent* aContent) {
return aContent->GetFlattenedTreeParentNodeForSelection();
static nsIContent* GetFlattenedTreeParentNodeForSelection(
const nsIContent* aNode) {
nsINode* parent = aNode->GetFlattenedTreeParentNodeForSelection();
return parent && parent->IsContent() ? parent->AsContent() : nullptr;
}
static auto* GetFlattenedTreeParentElementForStyle(const Element* aElement) {
@ -772,6 +803,17 @@ static auto* GetParentBrowserParent(const BrowserParent* aBrowserParent) {
: 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>
class MOZ_STACK_CLASS CommonAncestors final {
public:
@ -826,9 +868,12 @@ class MOZ_STACK_CLASS CommonAncestors final {
return GetClosestCommonAncestorChild(mInclusiveAncestors2);
}
template <TreeKind aKind>
void WarnIfClosestCommonAncestorChildrenAreNotInChildList() const {
WarnIfClosestCommonAncestorChildIsNotInChildList(mInclusiveAncestors1);
WarnIfClosestCommonAncestorChildIsNotInChildList(mInclusiveAncestors2);
WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
mInclusiveAncestors1);
WarnIfClosestCommonAncestorChildIsNotInChildList<aKind>(
mInclusiveAncestors2);
}
private:
@ -870,7 +915,9 @@ class MOZ_STACK_CLASS CommonAncestors final {
return child;
}
template <typename Node>
template <TreeKind aKind, typename Node,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
void WarnIfClosestCommonAncestorChildIsNotInChildList(
const nsTArray<Node*>& aInclusiveAncestors) const {
#ifdef DEBUG
@ -879,8 +926,24 @@ class MOZ_STACK_CLASS CommonAncestors final {
if (!child) {
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())) {
return;
}
@ -3164,6 +3227,7 @@ Element* nsContentUtils::GetCommonFlattenedTreeAncestorForStyle(
}
/* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::CompareChildNodes(
const nsINode* aChild1, const nsINode* aChild2,
NodeIndexCache* aIndexCache /* = nullptr */) {
@ -3186,6 +3250,23 @@ Maybe<int32_t> nsContentUtils::CompareChildNodes(
MOZ_ASSERT(aChild1->GetParentOrShadowHostNode());
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());
const nsINode& commonParentNode = *aChild1->GetParentOrShadowHostNode();
MOZ_ASSERT(aChild2->GetParentOrShadowHostNode() == &commonParentNode);
@ -3254,9 +3335,10 @@ Maybe<int32_t> nsContentUtils::CompareChildNodes(
}
/* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
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(aChild2, GetParentOrShadowHostNode(aChild2));
@ -3281,7 +3363,7 @@ Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
return Some(1);
}
const Maybe<int32_t> comp =
nsContentUtils::CompareChildNodes(aChild1, aChild2, aIndexCache);
nsContentUtils::CompareChildNodes<aKind>(aChild1, aChild2, aIndexCache);
if (MOZ_UNLIKELY(comp.isNothing())) {
NS_ASSERTION(comp.isSome(),
"nsContentUtils::CompareChildNodes() must return Some here. "
@ -3291,14 +3373,16 @@ Maybe<int32_t> nsContentUtils::CompareClosestCommonAncestorChildren(
return Some(1);
}
MOZ_ASSERT_IF(!*comp, aChild1 == aChild2);
MOZ_ASSERT_IF(*comp < 0, (aChild1 ? *aChild1->ComputeIndexInParentNode()
: aParent.GetChildCount()) <
(aChild2 ? *aChild2->ComputeIndexInParentNode()
: aParent.GetChildCount()));
MOZ_ASSERT_IF(*comp > 0, (aChild2 ? *aChild2->ComputeIndexInParentNode()
: aParent.GetChildCount()) <
(aChild1 ? *aChild1->ComputeIndexInParentNode()
: aParent.GetChildCount()));
MOZ_ASSERT_IF(*comp < 0 && !AreNodesInSameSlot(aChild1, aChild2),
(aChild1 ? *aChild1->ComputeIndexInParentNode()
: aParent.GetChildCount()) <
(aChild2 ? *aChild2->ComputeIndexInParentNode()
: aParent.GetChildCount()));
MOZ_ASSERT_IF(*comp > 0 && !AreNodesInSameSlot(aChild1, aChild2),
(aChild2 ? *aChild2->ComputeIndexInParentNode()
: aParent.GetChildCount()) <
(aChild1 ? *aChild1->ComputeIndexInParentNode()
: aParent.GetChildCount()));
return comp;
}
@ -3350,6 +3434,7 @@ Maybe<int32_t> nsContentUtils::CompareChildNodeAndChildOffset(
}
/* static */
template <TreeKind aKind, typename Dummy>
Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
uint32_t aOffset2, NodeIndexCache* aIndexCache) {
@ -3360,8 +3445,17 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
return Some(aOffset1 < aOffset2 ? -1 : (aOffset1 > aOffset2 ? 1 : 0));
}
const CommonAncestors commonAncestors(*aParent1, *aParent2,
GetParentOrShadowHostNode);
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();
};
const CommonAncestors commonAncestors(*aParent1, *aParent2, GetParentFunc);
if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
return Nothing();
@ -3372,16 +3466,22 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
const nsINode* closestCommonAncestorChild2 =
commonAncestors.GetClosestCommonAncestorChild2();
MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
commonAncestors.WarnIfClosestCommonAncestorChildrenAreNotInChildList();
commonAncestors
.template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
return CompareClosestCommonAncestorChildren(
return CompareClosestCommonAncestorChildren<aKind>(
*commonAncestors.GetClosestCommonAncestor(),
closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
}
if (closestCommonAncestorChild2) {
MOZ_ASSERT(closestCommonAncestorChild2->GetParentOrShadowHostNode() ==
aParent1);
MOZ_ASSERT(GetParentFunc(closestCommonAncestorChild2) == 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.
if (MOZ_UNLIKELY(
closestCommonAncestorChild2->IsRootOfNativeAnonymousSubtree() ||
@ -3408,9 +3508,14 @@ Maybe<int32_t> nsContentUtils::ComparePointsWithIndices(
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->GetParentOrShadowHostNode() ==
aParent2);
MOZ_ASSERT(GetParentFunc(closestCommonAncestorChild1) == aParent2);
// FIXME: bug 1946001, bug 1946003 and bug 1946008.
if (MOZ_UNLIKELY(
closestCommonAncestorChild1->IsRootOfNativeAnonymousSubtree() ||
@ -3500,7 +3605,8 @@ Element* nsContentUtils::GetTargetElement(Document* aDocument,
}
/* 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(
const RangeBoundaryBase<PT1, RT1>& aBoundary1,
const RangeBoundaryBase<PT2, RT2>& aBoundary2,
@ -3517,7 +3623,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// offset in the container. If both instances have computed offset, we can
// use ComparePointsWithIndices() which works with offsets.
if (aBoundary1.HasOffset() && aBoundary2.HasOffset()) {
return ComparePointsWithIndices(
return ComparePointsWithIndices<aKind>(
aBoundary1.GetContainer(), *aBoundary1.Offset(kValidOrInvalidOffsets1),
aBoundary2.GetContainer(), *aBoundary2.Offset(kValidOrInvalidOffsets2),
aIndexCache);
@ -3535,18 +3641,27 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
if (aBoundary1.GetContainer() == aBoundary2.GetContainer()) {
const nsIContent* const child1 = aBoundary1.GetChildAtOffset();
const nsIContent* const child2 = aBoundary2.GetChildAtOffset();
return CompareClosestCommonAncestorChildren(*aBoundary1.GetContainer(),
child1, child2, aIndexCache);
return CompareClosestCommonAncestorChildren<aKind>(
*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
// most distant different inclusive ancestors of the containers. So, the
// following implementation is similar to ComparePointsWithIndices(), but we
// don't have offset, so, we cannot use offset when we compare the boundaries
// whose one is a descendant of the other.
const CommonAncestors commonAncestors(*aBoundary1.GetContainer(),
*aBoundary2.GetContainer(),
GetParentOrShadowHostNode);
const CommonAncestors commonAncestors(
*aBoundary1.GetContainer(), *aBoundary2.GetContainer(), GetParentFunc);
if (MOZ_UNLIKELY(!commonAncestors.GetClosestCommonAncestor())) {
return Nothing();
@ -3557,10 +3672,11 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
commonAncestors.GetClosestCommonAncestorChild1();
const nsINode* closestCommonAncestorChild2 =
commonAncestors.GetClosestCommonAncestorChild2();
commonAncestors.WarnIfClosestCommonAncestorChildrenAreNotInChildList();
commonAncestors
.template WarnIfClosestCommonAncestorChildrenAreNotInChildList<aKind>();
MOZ_ASSERT(closestCommonAncestorChild1 != closestCommonAncestorChild2);
if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
return CompareClosestCommonAncestorChildren(
return CompareClosestCommonAncestorChildren<aKind>(
*commonAncestors.GetClosestCommonAncestor(),
closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
}
@ -3575,7 +3691,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// XXX Keep the odd traditional behavior for now.
return Some(1);
}
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes(
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2,
aIndexCache);
if (NS_WARN_IF(comp.isNothing())) {
@ -3613,7 +3729,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
// XXX Keep the odd traditional behavior for now.
return Some(-1);
}
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes(
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes<aKind>(
closestCommonAncestorChild1, aBoundary2.GetChildAtOffset(), aIndexCache);
if (NS_WARN_IF(comp.isNothing())) {
NS_ASSERTION(comp.isSome(),

View file

@ -716,6 +716,9 @@ class nsContentUtils {
* 0 if point1 == point2.
* `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(
const nsINode* aParent1, uint32_t aOffset1, const nsINode* aParent2,
uint32_t aOffset2, NodeIndexCache* aIndexCache = nullptr);
@ -730,7 +733,10 @@ class nsContentUtils {
* 0 if point1 == point2.
* `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(
const mozilla::RangeBoundaryBase<PT1, RT1>& aBoundary1,
const mozilla::RangeBoundaryBase<PT2, RT2>& aBoundary2,
@ -746,6 +752,9 @@ class nsContentUtils {
* traditional behavior. If you want to use this in new code, it means that
* 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(
const nsINode* aParent1, int64_t aOffset1, const nsINode* aParent2,
int64_t aOffset2) {
@ -768,7 +777,7 @@ class nsContentUtils {
}
// Otherwise, aOffset1 nor aOffset2 is referred so that any value is fine
// if negative.
return ComparePointsWithIndices(
return ComparePointsWithIndices<aKind>(
aParent1,
// Avoid warnings.
aOffset1 < 0 ? aParent1->GetChildCount()
@ -780,7 +789,8 @@ class nsContentUtils {
: std::min(static_cast<uint32_t>(aOffset2),
aParent2->GetChildCount()));
}
return ComparePointsWithIndices(aParent1, aOffset1, aParent2, aOffset2);
return ComparePointsWithIndices<aKind>(aParent1, aOffset1, aParent2,
aOffset2);
}
/**
@ -3642,6 +3652,9 @@ class nsContentUtils {
* node.
* 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(
const nsINode* aChild1, const nsINode* aChild2,
NodeIndexCache* aIndexCache = nullptr);
@ -3671,8 +3684,12 @@ class nsContentUtils {
* includes odd traditional behavior. Therefore, do not use this method as a
* utility method.
*/
template <TreeKind aKind = TreeKind::ShadowIncludingDOM,
typename = std::enable_if_t<aKind == TreeKind::ShadowIncludingDOM ||
aKind == TreeKind::Flat>>
static mozilla::Maybe<int32_t> CompareClosestCommonAncestorChildren(
const nsINode&, const nsINode*, const nsINode*, NodeIndexCache*);
const nsINode&, const nsINode*, const nsINode*,
NodeIndexCache* = nullptr);
static nsIXPConnect* sXPConnect;

View file

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

View file

@ -343,16 +343,28 @@ class IsItemInRangeComparator {
}
int operator()(const AbstractRange* const aRange) const {
Maybe<int32_t> cmp = nsContentUtils::ComparePoints(
ConstRawRangeBoundary(&mNode, mEndOffset,
RangeBoundaryIsMutationObserved::No),
aRange->MayCrossShadowBoundaryStartRef(), mCache);
if (cmp.valueOr(1) == 1) {
cmp = nsContentUtils::ComparePoints(
ConstRawRangeBoundary(&mNode, mStartOffset,
RangeBoundaryIsMutationObserved::No),
aRange->MayCrossShadowBoundaryEndRef(), mCache);
if (cmp.valueOr(1) == -1) {
auto ComparePoints = [](const nsINode* aNode1, const uint32_t aOffset1,
const nsINode* aNode2, const uint32_t aOffset2,
nsContentUtils::NodeIndexCache* aCache) {
if (StaticPrefs::dom_shadowdom_selection_across_boundary_enabled()) {
return nsContentUtils::ComparePointsWithIndices<TreeKind::Flat>(
aNode1, aOffset1, aNode2, aOffset2, aCache);
}
return nsContentUtils::ComparePointsWithIndices<
TreeKind::ShadowIncludingDOM>(aNode1, aOffset1, aNode2, aOffset2,
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 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
* parent rather than the slot element.
*/
inline nsIContent* GetFlattenedTreeParentNodeForSelection() const;
inline nsINode* GetFlattenedTreeParentNodeForSelection() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElement() const;
inline mozilla::dom::Element* GetFlattenedTreeParentElementForStyle() const;

View file

@ -285,12 +285,21 @@ static RangeBehaviour GetRangeBehaviour(
const RangeBoundary& otherSideExistingBoundary =
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
const Maybe<int32_t> order =
aIsSetStart ? nsContentUtils::ComparePoints(aNewBoundary,
otherSideExistingBoundary)
: nsContentUtils::ComparePoints(otherSideExistingBoundary,
aNewBoundary);
aIsSetStart
? ComparePoints(aNewBoundary, otherSideExistingBoundary.AsRaw())
: ComparePoints(otherSideExistingBoundary.AsRaw(), aNewBoundary);
if (order) {
if (*order != 1) {
@ -324,11 +333,12 @@ static RangeBehaviour GetRangeBehaviour(
// otherSideExistingBoundary. However, it's possible that aNewBoundary
// is valid with the otherSideExistingCrossShadowBoundaryBoundary.
const Maybe<int32_t> withCrossShadowBoundaryOrder =
aIsSetStart
? nsContentUtils::ComparePoints(
aNewBoundary, otherSideExistingCrossShadowBoundaryBoundary)
: nsContentUtils::ComparePoints(
otherSideExistingCrossShadowBoundaryBoundary, aNewBoundary);
aIsSetStart ? ComparePoints(
aNewBoundary,
otherSideExistingCrossShadowBoundaryBoundary.AsRaw())
: ComparePoints(
otherSideExistingCrossShadowBoundaryBoundary.AsRaw(),
aNewBoundary);
// Valid to the cross boundary boundary.
if (withCrossShadowBoundaryOrder && *withCrossShadowBoundaryOrder != 1) {
@ -3246,8 +3256,7 @@ void nsRange::ExcludeNonSelectableNodes(nsTArray<RefPtr<nsRange>>* aOutRanges) {
nsINode* node = preOrderIter.GetCurrentNode();
preOrderIter.Next();
bool selectable = true;
nsIContent* content =
node && node->IsContent() ? node->AsContent() : nullptr;
nsIContent* content = nsIContent::FromNodeOrNull(node);
if (content) {
if (firstNonSelectableContent &&
ExcludeIfNextToNonSelectable(content)) {

View file

@ -2829,9 +2829,10 @@ HTMLEditUtils::ComputePointToPutCaretInElementIfOutside(
// Use range boundaries and RangeUtils::CompareNodeToRange() to compare
// selection start to new block.
bool nodeBefore, nodeAfter;
nsresult rv = RangeUtils::CompareNodeToRangeBoundaries(
const_cast<Element*>(&aElement), aCurrentPoint.ToRawRangeBoundary(),
aCurrentPoint.ToRawRangeBoundary(), &nodeBefore, &nodeAfter);
nsresult rv =
RangeUtils::CompareNodeToRangeBoundaries<TreeKind::ShadowIncludingDOM>(
const_cast<Element*>(&aElement), aCurrentPoint.ToRawRangeBoundary(),
aCurrentPoint.ToRawRangeBoundary(), &nodeBefore, &nodeAfter);
if (NS_FAILED(rv)) {
NS_WARNING("RangeUtils::CompareNodeToRange() failed");
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
bool before, after;
nsresult rv =
RangeUtils::CompareNodeToRange(content, aRange, &before, &after);
RangeUtils::CompareNodeToRange<TreeKind::ShadowIncludingDOM>(
content, aRange, &before, &after);
if (NS_SUCCEEDED(rv) && !before && !after) {
itemToInsert = i;
bool snap;