Bug 1945711 - part 3: Make some callers of nsContentUtils::ComparePoints use the RangeBoundaryBase version r=jjaschke,dom-core

There are some callers of parent/offset version which computes offset before.
However, the offset may not be used.  Therefore, the callers should use
`RangeBoundaryBase` version.

Additionally, if only one of the pairs is computed from `RangeBoundaryBase`,
such callers should use the `RangeBoundaryBase` version too because the offset
computation may be skipped.  In this case, temporary `RangeBoundaryBase` should
be a `RawRangeBoundary` and whose `aRangeIsMutationObserver` should be set to
`RangeBoundaryIsMutationObserved::No` to avoid immediately to compute the child
node from `aOffset`.  I think that this should be default to
`RangeBoundaryIsMutationObserved::No` in the future, but for now, we should just
make the users explicitly set it to `RangeBoundaryIsMutationObserved::No` for
avoiding regressions.

Differential Revision: https://phabricator.services.mozilla.com/D236793
This commit is contained in:
Masayuki Nakano
2025-02-10 01:29:39 +00:00
parent cdc2c516ae
commit e595f1e9aa
11 changed files with 249 additions and 195 deletions

View File

@@ -816,39 +816,38 @@ bool HyperTextAccessible::SelectionBoundsAt(int32_t aSelectionNum,
nsRange* range = ranges[aSelectionNum];
// Get start and end points.
nsINode* startNode = range->GetStartContainer();
nsINode* endNode = range->GetEndContainer();
uint32_t startOffset = range->StartOffset();
uint32_t endOffset = range->EndOffset();
// Make sure start is before end, by swapping DOM points. This occurs when
// the user selects backwards in the text.
const Maybe<int32_t> order =
nsContentUtils::ComparePoints(endNode, endOffset, startNode, startOffset);
nsContentUtils::ComparePoints(range->EndRef(), range->StartRef());
if (!order) {
MOZ_ASSERT_UNREACHABLE();
return false;
}
if (*order < 0) {
std::swap(startNode, endNode);
std::swap(startOffset, endOffset);
}
const RangeBoundary& precedingBoundary =
*order < 0 ? range->EndRef() : range->StartRef();
const RangeBoundary& followingBoundary =
*order < 0 ? range->StartRef() : range->EndRef();
if (!startNode->IsInclusiveDescendantOf(mContent)) {
if (!precedingBoundary.Container()->IsInclusiveDescendantOf(mContent)) {
*aStartOffset = 0;
} else {
*aStartOffset =
DOMPointToOffset(startNode, AssertedCast<int32_t>(startOffset));
*aStartOffset = DOMPointToOffset(
precedingBoundary.Container(),
AssertedCast<int32_t>(*precedingBoundary.Offset(
RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)));
}
if (!endNode->IsInclusiveDescendantOf(mContent)) {
if (!followingBoundary.Container()->IsInclusiveDescendantOf(mContent)) {
*aEndOffset = CharacterCount();
} else {
*aEndOffset =
DOMPointToOffset(endNode, AssertedCast<int32_t>(endOffset), true);
*aEndOffset = DOMPointToOffset(
followingBoundary.Container(),
AssertedCast<int32_t>(*followingBoundary.Offset(
RangeBoundary::OffsetFilter::kValidOrInvalidOffsets)),
true);
}
return true;
}

View File

@@ -41,9 +41,11 @@ class EditorDOMPointBase;
template <typename ParentType, typename RefType>
class RangeBoundaryBase;
typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>
RangeBoundary;
typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
using RangeBoundary =
RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>>;
using RawRangeBoundary = RangeBoundaryBase<nsINode*, nsIContent*>;
using ConstRawRangeBoundary =
RangeBoundaryBase<const nsINode*, const nsIContent*>;
/**
* There are two ways of ensuring that `mRef` points to the correct node.
@@ -69,9 +71,9 @@ typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary;
*/
enum class RangeBoundaryIsMutationObserved { No = 0, Yes = 1 };
// This class has two specializations, one using reference counting
// pointers and one using raw pointers. This helps us avoid unnecessary
// AddRef/Release calls.
// This class has two types of specializations, one using reference counting
// pointers and one using raw pointers (both non-const and const versions). The
// latter help us avoid unnecessary AddRef/Release calls.
template <typename ParentType, typename RefType>
class RangeBoundaryBase {
template <typename T, typename U>
@@ -90,8 +92,26 @@ class RangeBoundaryBase {
static const uint32_t kFallbackOffset = 0;
template <typename T, typename Enable = void>
struct GetNodeType;
template <typename T>
struct GetNodeType<T, std::enable_if_t<std::is_pointer_v<T>>> {
using type = std::remove_pointer_t<T>;
};
template <typename T>
struct GetNodeType<T, std::enable_if_t<!std::is_pointer_v<T>>> {
using type = typename T::element_type;
};
public:
RangeBoundaryBase(nsINode* aContainer, nsIContent* aRef)
using RawParentType = typename GetNodeType<ParentType>::type;
static_assert(std::is_same_v<RawParentType, nsINode> ||
std::is_same_v<RawParentType, const nsINode>);
using RawRefType = typename GetNodeType<RefType>::type;
static_assert(std::is_same_v<RawRefType, nsIContent> ||
std::is_same_v<RawRefType, const nsIContent>);
RangeBoundaryBase(RawParentType* aContainer, RawRefType* aRef)
: mParent(aContainer), mRef(aRef), mIsMutationObserved(true) {
if (mRef) {
NS_WARNING_ASSERTION(mRef->GetParentNode() == mParent,
@@ -101,7 +121,7 @@ class RangeBoundaryBase {
}
}
RangeBoundaryBase(nsINode* aContainer, uint32_t aOffset,
RangeBoundaryBase(RawParentType* aContainer, uint32_t aOffset,
RangeBoundaryIsMutationObserved aRangeIsMutationObserver =
RangeBoundaryIsMutationObserved::Yes)
: mParent(aContainer),
@@ -122,11 +142,30 @@ class RangeBoundaryBase {
"Constructing RangeBoundary with invalid value");
}
/**
* Special constructor to create RangeBoundaryBase which stores both mRef and
* mOffset. This can make the instance provide both mRef and mOffset without
* computation, but the creator needs to guarantee that this is valid at least
* at construction.
*/
RangeBoundaryBase(RawParentType* aContainer, RawRefType* aRef,
uint32_t aOffset,
RangeBoundaryIsMutationObserved aRangeIsMutationObserver =
RangeBoundaryIsMutationObserved::Yes)
: mParent(const_cast<nsINode*>(aContainer)),
mRef(const_cast<nsIContent*>(aRef)),
mOffset(mozilla::Some(aOffset)),
mIsMutationObserved(bool(aRangeIsMutationObserver)) {
MOZ_ASSERT(IsSetAndValid());
}
RangeBoundaryBase()
: mParent(nullptr), mRef(nullptr), mIsMutationObserved(true) {}
// Needed for initializing RawRangeBoundary from an existing RangeBoundary.
template <typename PT, typename RT>
// Convert from RawRangeBoundary or RangeBoundary.
template <typename PT, typename RT,
typename = std::enable_if_t<!std::is_const_v<RawParentType> ||
std::is_const_v<PT>>>
RangeBoundaryBase(const RangeBoundaryBase<PT, RT>& aOther,
RangeBoundaryIsMutationObserved aIsMutationObserved)
: mParent(aOther.mParent),
@@ -145,7 +184,7 @@ class RangeBoundaryBase {
* Code inside of this class should call this method exactly one time and
* afterwards refer to `mRef` directly.
*/
nsIContent* Ref() const {
RawRefType* Ref() const {
if (mIsMutationObserved) {
return mRef;
}
@@ -177,17 +216,21 @@ class RangeBoundaryBase {
return mRef;
}
nsINode* Container() const { return mParent; }
RawParentType* Container() const { return mParent; }
dom::Document* GetComposedDoc() const {
return mParent ? mParent->GetComposedDoc() : nullptr;
}
/**
* This method may return `nullptr` if `mIsMutationObserved` is false and
* `mOffset` is out of bounds.
*/
nsIContent* GetChildAtOffset() const {
RawRefType* GetChildAtOffset() const {
if (!mParent || !mParent->IsContainerNode()) {
return nullptr;
}
nsIContent* const ref = Ref();
RawRefType* const ref = Ref();
if (!ref) {
if (!mIsMutationObserved && *mOffset != 0) {
// This means that this boundary is invalid.
@@ -209,11 +252,11 @@ class RangeBoundaryBase {
* If this refers after the last child or the container cannot have children,
* this returns nullptr with warning.
*/
nsIContent* GetNextSiblingOfChildAtOffset() const {
RawRefType* GetNextSiblingOfChildAtOffset() const {
if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
return nullptr;
}
nsIContent* const ref = Ref();
RawRefType* const ref = Ref();
if (!ref) {
if (!mIsMutationObserved && *mOffset != 0) {
// This means that this boundary is invalid.
@@ -241,11 +284,11 @@ class RangeBoundaryBase {
* at offset. If this refers the first child or the container cannot have
* children, this returns nullptr with warning.
*/
nsIContent* GetPreviousSiblingOfChildAtOffset() const {
RawRefType* GetPreviousSiblingOfChildAtOffset() const {
if (NS_WARN_IF(!mParent) || NS_WARN_IF(!mParent->IsContainerNode())) {
return nullptr;
}
nsIContent* const ref = Ref();
RawRefType* const ref = Ref();
if (NS_WARN_IF(!ref)) {
// Already referring the start of the container.
return nullptr;
@@ -392,6 +435,10 @@ class RangeBoundaryBase {
bool IsSet() const { return mParent && (mRef || mOffset.isSome()); }
[[nodiscard]] bool IsSetAndInComposedDoc() const {
return IsSet() && mParent->IsInComposedDoc();
}
bool IsSetAndValid() const {
if (!IsSet()) {
return false;
@@ -428,17 +475,25 @@ class RangeBoundaryBase {
// Convenience methods for switching between the two types
// of RangeBoundary.
RangeBoundaryBase<nsINode*, nsIContent*> AsRaw() const {
return RangeBoundaryBase<nsINode*, nsIContent*>(
template <typename PT = RawParentType,
typename = std::enable_if_t<!std::is_const_v<PT>>>
RawRangeBoundary AsRaw() const {
return RawRangeBoundary(
*this, RangeBoundaryIsMutationObserved(mIsMutationObserved));
}
ConstRawRangeBoundary AsConstRaw() const {
return ConstRawRangeBoundary(
*this, RangeBoundaryIsMutationObserved(mIsMutationObserved));
}
template <typename A, typename B>
RangeBoundaryBase& operator=(const RangeBoundaryBase<A, B>& aOther) = delete;
template <typename A, typename B>
template <
typename PT, typename RT, typename RPT = RawParentType,
typename = std::enable_if_t<!std::is_const_v<PT> || std::is_const_v<RPT>>>
RangeBoundaryBase& CopyFrom(
const RangeBoundaryBase<A, B>& aOther,
const RangeBoundaryBase<PT, RT>& aOther,
RangeBoundaryIsMutationObserved aIsMutationObserved) {
// mParent and mRef can be strong pointers, so better to try to avoid any
// extra AddRef/Release calls.
@@ -455,7 +510,7 @@ class RangeBoundaryBase {
// XXX What should we do if aOther is not updated for mutations and
// mOffset has already been invalid?
mOffset = aOther.Offset(
RangeBoundaryBase<A, B>::OffsetFilter::kValidOrInvalidOffsets);
RangeBoundaryBase<PT, RT>::OffsetFilter::kValidOrInvalidOffsets);
MOZ_DIAGNOSTIC_ASSERT(mOffset.isSome());
} else {
mOffset = aOther.mOffset;
@@ -463,7 +518,7 @@ class RangeBoundaryBase {
return *this;
}
bool Equals(const nsINode* aNode, uint32_t aOffset) const {
bool Equals(const RawParentType* aNode, uint32_t aOffset) const {
if (mParent != aNode) {
return false;
}

View File

@@ -890,16 +890,17 @@ void Selection::SetAnchorFocusRange(size_t aIndex) {
mAnchorFocusRange = anchorFocusRange->AsDynamicRange();
}
static int32_t CompareToRangeStart(const nsINode& aCompareNode,
uint32_t aCompareOffset,
const AbstractRange& aRange,
nsContentUtils::NodeIndexCache* aCache) {
template <typename PT, typename RT>
static int32_t CompareToRangeStart(
const RangeBoundaryBase<PT, RT>& aCompareBoundary,
const AbstractRange& aRange, nsContentUtils::NodeIndexCache* aCache) {
MOZ_ASSERT(aCompareBoundary.IsSet());
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() ||
!start->GetComposedDoc()) {
if (aCompareBoundary.GetComposedDoc() !=
aRange.MayCrossShadowBoundaryStartRef().GetComposedDoc() ||
!aRange.MayCrossShadowBoundaryStartRef().IsSetAndInComposedDoc()) {
NS_WARNING(
"`CompareToRangeStart` couldn't compare nodes, pretending some order.");
return 1;
@@ -907,41 +908,44 @@ 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.MayCrossShadowBoundaryStartOffset(), aCache);
aCompareBoundary, aRange.MayCrossShadowBoundaryStartRef(), aCache);
}
static int32_t CompareToRangeStart(const nsINode& aCompareNode,
uint32_t aCompareOffset,
const AbstractRange& aRange) {
return CompareToRangeStart(aCompareNode, aCompareOffset, aRange, nullptr);
template <typename PT, typename RT>
static int32_t CompareToRangeStart(
const RangeBoundaryBase<PT, RT>& aCompareBoundary,
const AbstractRange& aRange) {
return CompareToRangeStart(aCompareBoundary, aRange, nullptr);
}
static int32_t CompareToRangeEnd(const nsINode& aCompareNode,
uint32_t aCompareOffset,
const AbstractRange& aRange) {
template <typename PT, typename RT>
static int32_t CompareToRangeEnd(
const RangeBoundaryBase<PT, RT>& aCompareBoundary,
const AbstractRange& aRange) {
MOZ_ASSERT(aCompareBoundary.IsSet());
MOZ_ASSERT(aRange.IsPositioned());
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() ||
!end->GetComposedDoc()) {
if (aCompareBoundary.GetComposedDoc() !=
aRange.MayCrossShadowBoundaryEndRef().GetComposedDoc() ||
!aRange.MayCrossShadowBoundaryEndRef().IsSetAndInComposedDoc()) {
NS_WARNING(
"`CompareToRangeEnd` couldn't compare nodes, pretending some order.");
return 1;
}
// The points are in the same subtree, hence there has to be an order.
return *nsContentUtils::ComparePoints(
&aCompareNode, aCompareOffset, end,
aRange.MayCrossShadowBoundaryEndOffset());
return *nsContentUtils::ComparePoints(aCompareBoundary,
aRange.MayCrossShadowBoundaryEndRef());
}
// static
template <typename PT, typename RT>
size_t Selection::StyledRanges::FindInsertionPoint(
const nsTArray<StyledRange>* aElementArray, const nsINode& aPointNode,
uint32_t aPointOffset,
int32_t (*aComparator)(const nsINode&, uint32_t, const AbstractRange&)) {
const nsTArray<StyledRange>* aElementArray,
const RangeBoundaryBase<PT, RT>& aBoundary,
int32_t (*aComparator)(const RangeBoundaryBase<PT, RT>&,
const AbstractRange&)) {
int32_t beginSearch = 0;
int32_t endSearch = aElementArray->Length(); // one beyond what to check
@@ -950,7 +954,7 @@ size_t Selection::StyledRanges::FindInsertionPoint(
do {
const AbstractRange* range = (*aElementArray)[center].mRange;
int32_t cmp{aComparator(aPointNode, aPointOffset, *range)};
int32_t cmp{aComparator(aBoundary, *range)};
if (cmp < 0) { // point < cur
endSearch = center;
@@ -991,12 +995,10 @@ nsresult Selection::StyledRanges::SubtractRange(
}
// First we want to compare to the range start
int32_t cmp{CompareToRangeStart(*range->GetStartContainer(),
range->StartOffset(), aSubtract)};
int32_t cmp{CompareToRangeStart(range->StartRef(), aSubtract)};
// Also, make a comparison to the range end
int32_t cmp2{CompareToRangeEnd(*range->GetEndContainer(), range->EndOffset(),
aSubtract)};
int32_t cmp2{CompareToRangeEnd(range->EndRef(), aSubtract)};
// If the existing range left overlaps the new range (aSubtract) then
// cmp < 0, and cmp2 < 0
@@ -1365,9 +1367,8 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
// Insert the new element into our "leftovers" array
// `aRange` is positioned, so it has to have a start container.
size_t insertionPoint{FindInsertionPoint(&temp, *aRange->GetStartContainer(),
aRange->StartOffset(),
CompareToRangeStart)};
size_t insertionPoint{
FindInsertionPoint(&temp, aRange->StartRef(), CompareToRangeStart)};
temp.InsertElementAt(insertionPoint, StyledRange(aRange));
@@ -1591,22 +1592,17 @@ void Selection::StyledRanges::ReorderRangesIfNecessary() {
// the cache, which is reused by the sort call).
nsContentUtils::NodeIndexCache cache;
bool rangeOrderHasChanged = false;
const nsINode* prevStartContainer = nullptr;
uint32_t prevStartOffset = 0;
RawRangeBoundary previousStartRef;
for (const StyledRange& range : mRanges) {
const nsINode* startContainer = range.mRange->GetStartContainer();
uint32_t startOffset = range.mRange->StartOffset();
if (!prevStartContainer) {
prevStartContainer = startContainer;
prevStartOffset = startOffset;
if (!previousStartRef.IsSet()) {
previousStartRef = range.mRange->StartRef().AsRaw();
continue;
}
// Calling ComparePoints here saves one call of
// AbstractRange::StartOffset() per iteration (which is surprisingly
// expensive).
const Maybe<int32_t> compareResult = nsContentUtils::ComparePoints(
startContainer, startOffset, prevStartContainer, prevStartOffset,
&cache);
range.mRange->StartRef(), previousStartRef, &cache);
// If the nodes are in different subtrees, the Maybe is empty.
// Since CompareToRangeStart pretends ranges to be ordered, this aligns
// to that behavior.
@@ -1614,13 +1610,11 @@ void Selection::StyledRanges::ReorderRangesIfNecessary() {
rangeOrderHasChanged = true;
break;
}
prevStartContainer = startContainer;
prevStartOffset = startOffset;
previousStartRef = range.mRange->StartRef().AsRaw();
}
if (rangeOrderHasChanged) {
mRanges.Sort([&cache](const StyledRange& a, const StyledRange& b) -> int {
return CompareToRangeStart(*a.mRange->GetStartContainer(),
a.mRange->StartOffset(), *b.mRange, &cache);
return CompareToRangeStart(a.mRange->StartRef(), *b.mRange, &cache);
});
}
mDocumentGeneration = currentDocumentGeneration;
@@ -1654,8 +1648,11 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
// Ranges that end before the given interval and begin after the given
// interval can be discarded
size_t endsBeforeIndex{FindInsertionPoint(&mRanges, *aEndNode, aEndOffset,
&CompareToRangeStart)};
size_t endsBeforeIndex{FindInsertionPoint(
&mRanges,
ConstRawRangeBoundary(aEndNode, aEndOffset,
RangeBoundaryIsMutationObserved::No),
&CompareToRangeStart)};
if (endsBeforeIndex == 0) {
const AbstractRange* endRange = mRanges[endsBeforeIndex].mRange;
@@ -1676,8 +1673,11 @@ nsresult Selection::StyledRanges::GetIndicesForInterval(
}
aEndIndex.emplace(endsBeforeIndex);
size_t beginsAfterIndex{FindInsertionPoint(&mRanges, *aBeginNode,
aBeginOffset, &CompareToRangeEnd)};
size_t beginsAfterIndex{FindInsertionPoint(
&mRanges,
ConstRawRangeBoundary(aBeginNode, aBeginOffset,
RangeBoundaryIsMutationObserved::No),
&CompareToRangeEnd)};
if (beginsAfterIndex == mRanges.Length()) {
return NS_OK; // optimization: all ranges are strictly before us

View File

@@ -981,19 +981,21 @@ class Selection final : public nsSupportsWeakReference,
/**
* Binary searches the given sorted array of ranges for the insertion point
* for the given node/offset. The given comparator is used, and the index
* for the given aBoundary. The given comparator is used, and the index
* where the point should appear in the array is returned.
* If there is an item in the array equal to the input point (aPointNode,
* aPointOffset), we will return the index of this item.
* If there is an item in the array equal to aBoundary, we will return the
index of this item.
*
* @return the index where the point should appear in the array. In
* [0, `aElementArray->Length()`].
*/
template <typename PT, typename RT>
static size_t FindInsertionPoint(
const nsTArray<StyledRange>* aElementArray, const nsINode& aPointNode,
uint32_t aPointOffset,
int32_t (*aComparator)(const nsINode&, uint32_t, const AbstractRange&));
const nsTArray<StyledRange>* aElementArray,
const RangeBoundaryBase<PT, RT>& aBoundary,
int32_t (*aComparator)(const RangeBoundaryBase<PT, RT>&,
const AbstractRange&));
/**
* Works on the same principle as GetRangesForIntervalArray, however

View File

@@ -486,16 +486,32 @@ int32_t nsContentUtils::sInnerOrOuterWindowCount = 0;
uint32_t nsContentUtils::sInnerOrOuterWindowSerialCounter = 0;
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary);
const RangeBoundary& aFirstBoundary, const RangeBoundary& aSecondBoundary,
NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary);
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RawRangeBoundary& aFirstBoundary,
const RangeBoundary& aSecondBoundary);
const RangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RawRangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary);
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const RangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
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(
const ConstRawRangeBoundary& aFirstBoundary,
const RawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
template Maybe<int32_t> nsContentUtils::ComparePoints(
const ConstRawRangeBoundary& aFirstBoundary,
const ConstRawRangeBoundary& aSecondBoundary, NodeIndexCache* aIndexCache);
// Subset of
// http://www.whatwg.org/specs/web-apps/current-work/#autofill-field-name
@@ -750,10 +766,10 @@ static auto* GetParentBrowserParent(const BrowserParent* aBrowserParent) {
: nullptr;
}
template <typename Node, typename GetParentFunc>
template <typename Node1, typename Node2, typename GetParentFunc>
class MOZ_STACK_CLASS CommonAncestors final {
public:
CommonAncestors(Node& aNode1, Node& aNode2, GetParentFunc aGetParentFunc)
CommonAncestors(Node1& aNode1, Node2& aNode2, GetParentFunc aGetParentFunc)
: GetParent(aGetParentFunc) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
mAssertNoGC.emplace();
@@ -766,11 +782,11 @@ class MOZ_STACK_CLASS CommonAncestors final {
size_t depth1 = mInclusiveAncestors1.Length();
size_t depth2 = mInclusiveAncestors2.Length();
const size_t shorterLength = std::min(depth1, depth2);
Node** const inclusiveAncestors1 = mInclusiveAncestors1.Elements();
Node** const inclusiveAncestors2 = mInclusiveAncestors2.Elements();
Node1** const inclusiveAncestors1 = mInclusiveAncestors1.Elements();
Node2** const inclusiveAncestors2 = mInclusiveAncestors2.Elements();
for ([[maybe_unused]] const size_t unused : IntegerRange(shorterLength)) {
Node* const inclusiveAncestor1 = inclusiveAncestors1[--depth1];
Node* const inclusiveAncestor2 = inclusiveAncestors2[--depth2];
Node1* const inclusiveAncestor1 = inclusiveAncestors1[--depth1];
Node2* const inclusiveAncestor2 = inclusiveAncestors2[--depth2];
if (inclusiveAncestor1 != inclusiveAncestor2) {
MOZ_ASSERT_IF(mClosestCommonAncestor,
inclusiveAncestor1 == GetClosestCommonAncestorChild1());
@@ -794,13 +810,13 @@ class MOZ_STACK_CLASS CommonAncestors final {
~CommonAncestors() { MOZ_DIAGNOSTIC_ASSERT(!mMutationGuard.Mutated(0)); }
#endif // #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
[[nodiscard]] Node* GetClosestCommonAncestor() const {
[[nodiscard]] Node1* GetClosestCommonAncestor() const {
return mClosestCommonAncestor;
}
[[nodiscard]] Node* GetClosestCommonAncestorChild1() const {
[[nodiscard]] Node1* GetClosestCommonAncestorChild1() const {
return GetClosestCommonAncestorChild(mInclusiveAncestors1);
}
[[nodiscard]] Node* GetClosestCommonAncestorChild2() const {
[[nodiscard]] Node2* GetClosestCommonAncestorChild2() const {
return GetClosestCommonAncestorChild(mInclusiveAncestors2);
}
@@ -810,6 +826,7 @@ class MOZ_STACK_CLASS CommonAncestors final {
}
private:
template <typename Node>
static void AppendInclusiveAncestors(Node* aNode,
GetParentFunc aGetParentFunc,
nsTArray<Node*>& aArrayOfParents) {
@@ -820,6 +837,7 @@ class MOZ_STACK_CLASS CommonAncestors final {
}
}
template <typename Node>
Maybe<size_t> GetClosestCommonAncestorChildIndex(
const nsTArray<Node*>& aInclusiveAncestors) const {
if (!mClosestCommonAncestor ||
@@ -830,6 +848,7 @@ class MOZ_STACK_CLASS CommonAncestors final {
- mNumberOfCommonAncestors); // before closest common ancestor
}
template <typename Node>
[[nodiscard]] Node* GetClosestCommonAncestorChild(
const nsTArray<Node*>& aInclusiveAncestors) const {
const Maybe<size_t> index =
@@ -845,6 +864,7 @@ class MOZ_STACK_CLASS CommonAncestors final {
return child;
}
template <typename Node>
void WarnIfClosestCommonAncestorChildIsNotInChildList(
const nsTArray<Node*>& aInclusiveAncestors) const {
#ifdef DEBUG
@@ -877,8 +897,9 @@ class MOZ_STACK_CLASS CommonAncestors final {
#endif
}
AutoTArray<Node*, 30> mInclusiveAncestors1, mInclusiveAncestors2;
Node* mClosestCommonAncestor = nullptr;
AutoTArray<Node1*, 30> mInclusiveAncestors1;
AutoTArray<Node2*, 30> mInclusiveAncestors2;
Node1* mClosestCommonAncestor = nullptr;
const GetParentFunc GetParent;
uint32_t mNumberOfCommonAncestors = 0;
@@ -3522,7 +3543,8 @@ Element* nsContentUtils::GetTargetElement(Document* aDocument,
template <typename PT1, typename RT1, typename PT2, typename RT2>
Maybe<int32_t> nsContentUtils::ComparePoints(
const RangeBoundaryBase<PT1, RT1>& aBoundary1,
const RangeBoundaryBase<PT2, RT2>& aBoundary2) {
const RangeBoundaryBase<PT2, RT2>& aBoundary2,
NodeIndexCache* aIndexCache /* = nullptr */) {
if (!aBoundary1.IsSet() || !aBoundary2.IsSet()) {
return Nothing{};
}
@@ -3537,7 +3559,8 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
if (aBoundary1.HasOffset() && aBoundary2.HasOffset()) {
return ComparePoints(
aBoundary1.Container(), *aBoundary1.Offset(kValidOrInvalidOffsets1),
aBoundary2.Container(), *aBoundary2.Offset(kValidOrInvalidOffsets2));
aBoundary2.Container(), *aBoundary2.Offset(kValidOrInvalidOffsets2),
aIndexCache);
}
// Otherwise, i.e., at least one RangeBoundaryBase stores the child node.
@@ -3553,7 +3576,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
const nsIContent* const child1 = aBoundary1.GetChildAtOffset();
const nsIContent* const child2 = aBoundary2.GetChildAtOffset();
return CompareClosestCommonAncestorChildren(*aBoundary1.Container(), child1,
child2);
child2, aIndexCache);
}
// Otherwise, we need to compare the common ancestor children which is the
@@ -3579,7 +3602,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
if (closestCommonAncestorChild1 && closestCommonAncestorChild2) {
return CompareClosestCommonAncestorChildren(
*commonAncestors.GetClosestCommonAncestor(),
closestCommonAncestorChild1, closestCommonAncestorChild2);
closestCommonAncestorChild1, closestCommonAncestorChild2, aIndexCache);
}
if (closestCommonAncestorChild2) {
@@ -3593,7 +3616,8 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
return Some(1);
}
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes(
aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2);
aBoundary1.GetChildAtOffset(), closestCommonAncestorChild2,
aIndexCache);
if (NS_WARN_IF(comp.isNothing())) {
NS_ASSERTION(
comp.isSome(),
@@ -3630,7 +3654,7 @@ Maybe<int32_t> nsContentUtils::ComparePoints(
return Some(-1);
}
const Maybe<int32_t> comp = nsContentUtils::CompareChildNodes(
closestCommonAncestorChild1, aBoundary2.GetChildAtOffset());
closestCommonAncestorChild1, aBoundary2.GetChildAtOffset(), aIndexCache);
if (NS_WARN_IF(comp.isNothing())) {
NS_ASSERTION(comp.isSome(),
"nsContentUtils::CompareChildOffsetAndChildNode() must return "

View File

@@ -702,7 +702,8 @@ class nsContentUtils {
template <typename PT1, typename RT1, typename PT2, typename RT2>
static mozilla::Maybe<int32_t> ComparePoints(
const mozilla::RangeBoundaryBase<PT1, RT1>& aBoundary1,
const mozilla::RangeBoundaryBase<PT2, RT2>& aBoundary2);
const mozilla::RangeBoundaryBase<PT2, RT2>& aBoundary2,
NodeIndexCache* aIndexCache = nullptr);
/**
* DO NOT USE this method for comparing the points in new code. this method

View File

@@ -342,12 +342,14 @@ class IsItemInRangeComparator {
int operator()(const AbstractRange* const aRange) const {
Maybe<int32_t> cmp = nsContentUtils::ComparePoints(
&mNode, mEndOffset, aRange->GetMayCrossShadowBoundaryStartContainer(),
aRange->MayCrossShadowBoundaryStartOffset(), mCache);
ConstRawRangeBoundary(&mNode, mEndOffset,
RangeBoundaryIsMutationObserved::No),
aRange->MayCrossShadowBoundaryStartRef(), mCache);
if (cmp.valueOr(1) == 1) {
cmp = nsContentUtils::ComparePoints(
&mNode, mStartOffset, aRange->GetMayCrossShadowBoundaryEndContainer(),
aRange->MayCrossShadowBoundaryEndOffset(), mCache);
ConstRawRangeBoundary(&mNode, mStartOffset,
RangeBoundaryIsMutationObserved::No),
aRange->MayCrossShadowBoundaryEndRef(), mCache);
if (cmp.valueOr(1) == -1) {
return 0;
}
@@ -433,17 +435,20 @@ bool nsINode::IsSelected(const uint32_t aStartOffset, const uint32_t aEndOffset,
// if node end > start of middle+1, result = 1
if (middle + 1 < high &&
(middlePlus1 = selection->GetAbstractRangeAt(middle + 1)) &&
nsContentUtils::ComparePoints(this, aEndOffset,
middlePlus1->GetStartContainer(),
middlePlus1->StartOffset(), &cache)
nsContentUtils::ComparePoints(
ConstRawRangeBoundary(this, aEndOffset,
RangeBoundaryIsMutationObserved::No),
middlePlus1->StartRef(), &cache)
.valueOr(1) > 0) {
result = 1;
// if node start < end of middle - 1, result = -1
} else if (middle >= 1 &&
(middleMinus1 = selection->GetAbstractRangeAt(middle - 1)) &&
nsContentUtils::ComparePoints(
this, aStartOffset, middleMinus1->GetEndContainer(),
middleMinus1->EndOffset(), &cache)
ConstRawRangeBoundary(
this, aStartOffset,
RangeBoundaryIsMutationObserved::No),
middleMinus1->EndRef(), &cache)
.valueOr(1) < 0) {
result = -1;
} else {

View File

@@ -904,13 +904,10 @@ bool nsRange::IntersectsNode(nsINode& aNode, ErrorResult& aRv) {
}
const Maybe<int32_t> startOrder = nsContentUtils::ComparePoints(
mStart.Container(),
*mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets), parent,
*nodeIndex + 1u);
mStart, RawRangeBoundary(parent, aNode.AsContent(), *nodeIndex + 1u));
if (startOrder && (*startOrder < 0)) {
const Maybe<int32_t> endOrder = nsContentUtils::ComparePoints(
parent, *nodeIndex, mEnd.Container(),
*mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets));
RawRangeBoundary(parent, aNode.GetPreviousSibling(), *nodeIndex), mEnd);
return endOrder && (*endOrder < 0);
}
@@ -2144,33 +2141,23 @@ int16_t nsRange::CompareBoundaryPoints(uint16_t aHow,
return 0;
}
nsINode *ourNode, *otherNode;
uint32_t ourOffset, otherOffset;
RawRangeBoundary ourBoundary, otherBoundary;
switch (aHow) {
case Range_Binding::START_TO_START:
ourNode = mStart.Container();
ourOffset = *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
otherNode = aOtherRange.GetStartContainer();
otherOffset = aOtherRange.StartOffset();
ourBoundary = mStart.AsRaw();
otherBoundary = aOtherRange.StartRef().AsRaw();
break;
case Range_Binding::START_TO_END:
ourNode = mEnd.Container();
ourOffset = *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
otherNode = aOtherRange.GetStartContainer();
otherOffset = aOtherRange.StartOffset();
ourBoundary = mEnd.AsRaw();
otherBoundary = aOtherRange.StartRef().AsRaw();
break;
case Range_Binding::END_TO_START:
ourNode = mStart.Container();
ourOffset = *mStart.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
otherNode = aOtherRange.GetEndContainer();
otherOffset = aOtherRange.EndOffset();
ourBoundary = mStart.AsRaw();
otherBoundary = aOtherRange.EndRef().AsRaw();
break;
case Range_Binding::END_TO_END:
ourNode = mEnd.Container();
ourOffset = *mEnd.Offset(RangeBoundary::OffsetFilter::kValidOffsets);
otherNode = aOtherRange.GetEndContainer();
otherOffset = aOtherRange.EndOffset();
ourBoundary = mEnd.AsRaw();
otherBoundary = aOtherRange.EndRef().AsRaw();
break;
default:
// We were passed an illegal value
@@ -2184,11 +2171,11 @@ int16_t nsRange::CompareBoundaryPoints(uint16_t aHow,
}
const Maybe<int32_t> order =
nsContentUtils::ComparePoints(ourNode, ourOffset, otherNode, otherOffset);
nsContentUtils::ComparePoints(ourBoundary, otherBoundary);
// `this` and `aOtherRange` share the same root and (ourNode, ourOffset),
// (otherNode, otherOffset) correspond to some of their boundaries. Hence,
// (ourNode, ourOffset) and (otherNode, otherOffset) have to be comparable.
// `this` and `aOtherRange` share the same root and ourBoundary, otherBoundary
// correspond to some of their boundaries. Hence, ourBoundary and
// otherBoundary have to be comparable.
return *order;
}

View File

@@ -136,14 +136,7 @@ ContentEventHandler::SimpleRangeBase<nsINode*,
template <typename NodeType, typename RangeBoundaryType>
void ContentEventHandler::SimpleRangeBase<
NodeType, RangeBoundaryType>::AssertStartIsBeforeOrEqualToEnd() {
MOZ_ASSERT(
*nsContentUtils::ComparePoints(
mStart.Container(),
*mStart.Offset(
RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets),
mEnd.Container(),
*mEnd.Offset(
RangeBoundaryType::OffsetFilter::kValidOrInvalidOffsets)) <= 0);
MOZ_ASSERT(*nsContentUtils::ComparePoints(mStart, mEnd) <= 0);
}
template <typename NodeType, typename RangeBoundaryType>

View File

@@ -189,37 +189,27 @@ void FilteredContentIterator::Last() {
// the traversal of the range in the specified mode.
//
static bool ContentIsInTraversalRange(nsIContent* aContent, bool aIsPreMode,
nsINode* aStartContainer,
int32_t aStartOffset,
nsINode* aEndContainer,
int32_t aEndOffset) {
NS_ENSURE_TRUE(aStartContainer && aEndContainer && aContent, false);
const RangeBoundary& aStartBoundary,
const RangeBoundary& aEndBoundary) {
NS_ENSURE_TRUE(aStartBoundary.IsSet() && aEndBoundary.IsSet() && aContent,
false);
nsIContent* parentContent = aContent->GetParent();
if (MOZ_UNLIKELY(NS_WARN_IF(!parentContent))) {
return false;
}
Maybe<uint32_t> offsetInParent = parentContent->ComputeIndexOf(aContent);
NS_WARNING_ASSERTION(
offsetInParent.isSome(),
"Content is not in the parent, is this called during a DOM mutation?");
if (MOZ_UNLIKELY(NS_WARN_IF(offsetInParent.isNothing()))) {
if (NS_WARN_IF(!parentContent) ||
NS_WARN_IF(parentContent->IsRootOfNativeAnonymousSubtree())) {
return false;
}
const RawRangeBoundary compPoint(
parentContent, aIsPreMode ? aContent->GetPreviousSibling() : aContent);
if (!aIsPreMode) {
MOZ_ASSERT(*offsetInParent != UINT32_MAX);
++(*offsetInParent);
}
const Maybe<int32_t> startRes = nsContentUtils::ComparePoints(
aStartContainer, aStartOffset, parentContent, *offsetInParent);
if (MOZ_UNLIKELY(NS_WARN_IF(!startRes))) {
const Maybe<int32_t> startRes =
nsContentUtils::ComparePoints(aStartBoundary, compPoint);
if (NS_WARN_IF(!startRes)) {
return false;
}
const Maybe<int32_t> endRes = nsContentUtils::ComparePoints(
aEndContainer, aEndOffset, parentContent, *offsetInParent);
if (MOZ_UNLIKELY(NS_WARN_IF(!endRes))) {
const Maybe<int32_t> endRes =
nsContentUtils::ComparePoints(aEndBoundary, compPoint);
if (NS_WARN_IF(!endRes)) {
return false;
}
return *startRes <= 0 && *endRes >= 0;
@@ -231,10 +221,8 @@ static bool ContentIsInTraversalRange(nsRange* aRange, nsIContent* aNextContent,
// aNextContent!
NS_ENSURE_TRUE(aNextContent && aRange, false);
return ContentIsInTraversalRange(
aNextContent, aIsPreMode, aRange->GetStartContainer(),
static_cast<int32_t>(aRange->StartOffset()), aRange->GetEndContainer(),
static_cast<int32_t>(aRange->EndOffset()));
return ContentIsInTraversalRange(aNextContent, aIsPreMode, aRange->StartRef(),
aRange->EndRef());
}
// Helper function to advance to the next or previous node

View File

@@ -1161,10 +1161,10 @@ void nsFrameSelection::MaintainedRange::AdjustContentOffsets(
nsIFrame::ContentOffsets& aOffsets, StopAtScroller aStopAtScroller) const {
// Adjust offsets according to maintained amount
if (mRange && mAmount != eSelectNoAmount) {
nsINode* rangenode = mRange->GetStartContainer();
int32_t rangeOffset = mRange->StartOffset();
const Maybe<int32_t> relativePosition = nsContentUtils::ComparePoints(
rangenode, rangeOffset, aOffsets.content, aOffsets.offset);
mRange->StartRef(),
RawRangeBoundary(aOffsets.content, aOffsets.offset,
RangeBoundaryIsMutationObserved::No));
if (NS_WARN_IF(!relativePosition)) {
// Potentially handle this properly when Selection across Shadow DOM
// boundary is implemented