Bug 1828469, part 2: Moved some logic from nsRange to AbstractRange to support StaticRanges in custom highlights. r=masayuki

With this change, Selections are also registered into StaticRanges,
ultimately making them visible to `nsINode::IsSelected()`,
which is necessary to paint them.

Differential Revision: https://phabricator.services.mozilla.com/D175784
This commit is contained in:
Jan-Niklas Jaeschke 2023-05-31 08:23:45 +00:00
parent 1bbcb68c33
commit bfe0baa6d6
13 changed files with 258 additions and 250 deletions

View File

@ -12,6 +12,7 @@
#include "mozilla/RangeUtils.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/StaticRange.h"
#include "mozilla/dom/Selection.h"
#include "nsContentUtils.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGkAtoms.h"
@ -65,18 +66,73 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AbstractRange)
// mStart and mEnd may depend on or be depended on some other members in
// concrete classes so that they should be unlinked in sub classes.
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
tmp->mSelections.Clear();
// Unregistering of the common inclusive ancestors would by design
// also happen when the actual implementations unlink `mStart`/`mEnd`.
// This may introduce additional overhead which is not needed when unlinking,
// therefore this is done here beforehand.
if (tmp->mRegisteredClosestCommonInclusiveAncestor) {
tmp->UnregisterClosestCommonInclusiveAncestor(
tmp->mRegisteredClosestCommonInclusiveAncestor, true);
}
MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
"Shouldn't be registered now that we're unlinking");
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(AbstractRange)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStart)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEnd)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRegisteredClosestCommonInclusiveAncestor)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
void AbstractRange::MarkDescendants(const nsINode& aNode) {
// Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on
// aNode's descendants unless aNode is already marked as a range common
// ancestor or a descendant of one, in which case all of our descendants have
// the bit set already.
if (!aNode.IsMaybeSelected()) {
// don't set the Descendant bit on |aNode| itself
nsINode* node = aNode.GetNextNode(&aNode);
while (node) {
node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(&aNode);
} else {
// optimize: skip this sub-tree since it's marked already.
node = node->GetNextNonChildNode(&aNode);
}
}
}
}
void AbstractRange::UnmarkDescendants(const nsINode& aNode) {
// Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection
// on aNode's descendants unless aNode is a descendant of another range common
// ancestor. Also, exclude descendants of range common ancestors (but not the
// common ancestor itself).
if (!aNode
.IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
// we know |aNode| doesn't have any bit set
nsINode* node = aNode.GetNextNode(&aNode);
while (node) {
node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(&aNode);
} else {
// We found an ancestor of an overlapping range, skip its descendants.
node = node->GetNextNonChildNode(&aNode);
}
}
}
}
// NOTE: If you need to change default value of members of AbstractRange,
// update nsRange::Create(nsINode* aNode) and ClearForReuse() too.
AbstractRange::AbstractRange(nsINode* aNode, bool aIsDynamicRange)
: mIsPositioned(false),
: mRegisteredClosestCommonInclusiveAncestor(nullptr),
mIsPositioned(false),
mIsGenerated(false),
mCalledByJS(false),
mIsDynamicRange(aIsDynamicRange) {
@ -206,6 +262,122 @@ nsresult AbstractRange::SetStartAndEndInternal(
return NS_OK;
}
bool AbstractRange::IsInSelection(const Selection& aSelection) const {
return mSelections.Contains(&aSelection);
}
void AbstractRange::RegisterSelection(Selection& aSelection) {
if (IsInSelection(aSelection)) {
return;
}
bool isFirstSelection = mSelections.IsEmpty();
mSelections.AppendElement(&aSelection);
if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
nsINode* commonAncestor = GetClosestCommonInclusiveAncestor();
MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
RegisterClosestCommonInclusiveAncestor(commonAncestor);
}
}
const nsTArray<WeakPtr<Selection>>& AbstractRange::GetSelections() const {
return mSelections;
}
void AbstractRange::UnregisterSelection(const Selection& aSelection) {
mSelections.RemoveElement(&aSelection);
if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) {
UnregisterClosestCommonInclusiveAncestor(
mRegisteredClosestCommonInclusiveAncestor, false);
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredClosestCommonInclusiveAncestor,
"How can we have a registered common ancestor when we "
"just unregistered?");
MOZ_DIAGNOSTIC_ASSERT(
!isInList(),
"Shouldn't be registered if we have no "
"mRegisteredClosestCommonInclusiveAncestor after unregistering");
}
}
void AbstractRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
MOZ_ASSERT(aNode, "bad arg");
MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(),
"registering range not in selection");
mRegisteredClosestCommonInclusiveAncestor = aNode;
MarkDescendants(*aNode);
UniquePtr<LinkedList<AbstractRange>>& ranges =
aNode->GetClosestCommonInclusiveAncestorRangesPtr();
if (!ranges) {
ranges = MakeUnique<LinkedList<AbstractRange>>();
}
MOZ_DIAGNOSTIC_ASSERT(!isInList());
ranges->insertBack(this);
aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
}
void AbstractRange::UnregisterClosestCommonInclusiveAncestor(
nsINode* aNode, bool aIsUnlinking) {
MOZ_ASSERT(aNode, "bad arg");
NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(),
"wrong node");
MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor,
"wrong node");
LinkedList<AbstractRange>* ranges =
aNode->GetExistingClosestCommonInclusiveAncestorRanges();
MOZ_ASSERT(ranges);
mRegisteredClosestCommonInclusiveAncestor = nullptr;
#ifdef DEBUG
bool found = false;
for (AbstractRange* range : *ranges) {
if (range == this) {
found = true;
break;
}
}
MOZ_ASSERT(found,
"We should be in the list on our registered common ancestor");
#endif // DEBUG
remove();
// We don't want to waste time unmarking flags on nodes that are
// being unlinked anyway.
if (!aIsUnlinking && ranges->isEmpty()) {
aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection();
UnmarkDescendants(*aNode);
}
}
void AbstractRange::UpdateCommonAncestorIfNecessary() {
nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor();
if (newCommonAncestor != oldCommonAncestor) {
if (oldCommonAncestor) {
UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false);
}
if (newCommonAncestor) {
RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
} else {
MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes");
mSelections.Clear();
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredClosestCommonInclusiveAncestor,
"How can we have a registered common ancestor when we "
"didn't register ourselves?");
MOZ_DIAGNOSTIC_ASSERT(!isInList(),
"Shouldn't be registered if we have no "
"mRegisteredClosestCommonInclusiveAncestor");
}
}
}
nsINode* AbstractRange::GetParentObject() const { return mOwner; }
JSObject* AbstractRange::WrapObject(JSContext* aCx,

View File

@ -15,6 +15,7 @@
#include "mozilla/Maybe.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsISupports.h"
#include "nsWrapperCache.h"
@ -26,10 +27,14 @@ class nsRange;
struct JSContext;
namespace mozilla::dom {
class StaticRange;
class Document;
class Selection;
class StaticRange;
class AbstractRange : public nsISupports, public nsWrapperCache {
class AbstractRange : public nsISupports,
public nsWrapperCache,
// For linking together selection-associated ranges.
public mozilla::LinkedListElement<AbstractRange> {
protected:
explicit AbstractRange(nsINode* aNode, bool aIsDynamicRange);
virtual ~AbstractRange();
@ -106,6 +111,27 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
inline StaticRange* AsStaticRange();
inline const StaticRange* AsStaticRange() const;
/**
* Return true if this range is part of a Selection object
* and isn't detached.
*/
bool IsInAnySelection() const { return !mSelections.IsEmpty(); }
MOZ_CAN_RUN_SCRIPT void RegisterSelection(
mozilla::dom::Selection& aSelection);
void UnregisterSelection(const mozilla::dom::Selection& aSelection);
/**
* Returns a list of all Selections the range is associated with.
*/
const nsTArray<WeakPtr<Selection>>& GetSelections() const;
/**
* Return true if this range is in |aSelection|.
*/
bool IsInSelection(const mozilla::dom::Selection& aSelection) const;
protected:
template <typename SPT, typename SRT, typename EPT, typename ERT,
typename RangeType>
@ -133,6 +159,21 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
<< (aRange.mIsDynamicRange ? "true" : "false") << " }";
}
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void RegisterClosestCommonInclusiveAncestor(nsINode* aNode);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void UnregisterClosestCommonInclusiveAncestor(nsINode* aNode,
bool aIsUnlinking);
void UpdateCommonAncestorIfNecessary();
static void MarkDescendants(const nsINode& aNode);
static void UnmarkDescendants(const nsINode& aNode);
private:
void ClearForReuse();
@ -140,6 +181,13 @@ class AbstractRange : public nsISupports, public nsWrapperCache {
RefPtr<Document> mOwner;
RangeBoundary mStart;
RangeBoundary mEnd;
// A Range can be part of multiple |Selection|s. This is a very rare use case.
AutoTArray<WeakPtr<Selection>, 1> mSelections;
// mRegisteredClosestCommonInclusiveAncestor is only non-null when the range
// IsInAnySelection().
nsCOMPtr<nsINode> mRegisteredClosestCommonInclusiveAncestor;
// `true` if `mStart` and `mEnd` are set for StaticRange or set and valid
// for nsRange.
bool mIsPositioned;

View File

@ -3003,12 +3003,12 @@ void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const {
static_cast<unsigned long long>(State().GetInternalValue()));
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges =
const LinkedList<AbstractRange>* ranges =
GetExistingClosestCommonInclusiveAncestorRanges();
int32_t count = 0;
if (ranges) {
// Can't use range-based iteration on a const LinkedList, unfortunately.
for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
for (const AbstractRange* r = ranges->getFirst(); r; r = r->getNext()) {
++count;
}
}

View File

@ -1234,9 +1234,7 @@ nsresult Selection::StyledRanges::MaybeAddRangeAndTruncateOverlaps(
// Remove all the overlapping ranges
for (size_t i = startIndex; i < endIndex; ++i) {
if (mRanges[i].mRange->IsDynamicRange()) {
mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection);
}
mRanges[i].mRange->UnregisterSelection(mSelection);
}
mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
@ -1287,9 +1285,8 @@ nsresult Selection::StyledRanges::RemoveRangeAndUnregisterSelection(
if (idx < 0) return NS_ERROR_DOM_NOT_FOUND_ERR;
mRanges.RemoveElementAt(idx);
if (aRange.IsDynamicRange()) {
aRange.AsDynamicRange()->UnregisterSelection(mSelection);
}
aRange.UnregisterSelection(mSelection);
return NS_OK;
}
nsresult Selection::RemoveCollapsedRanges() {
@ -1987,9 +1984,7 @@ void Selection::SetAncestorLimiter(nsIContent* aLimiter) {
void Selection::StyledRanges::UnregisterSelection() {
uint32_t count = mRanges.Length();
for (uint32_t i = 0; i < count; ++i) {
if (mRanges[i].mRange->IsDynamicRange()) {
mRanges[i].mRange->AsDynamicRange()->UnregisterSelection(mSelection);
}
mRanges[i].mRange->UnregisterSelection(mSelection);
}
}
@ -2237,10 +2232,8 @@ void Selection::AddHighlightRangeAndSelectFramesAndNotifyListeners(
MOZ_ASSERT(mSelectionType == SelectionType::eHighlight);
mStyledRanges.mRanges.AppendElement(StyledRange{&aRange});
if (aRange.IsDynamicRange()) {
RefPtr<nsRange> range = aRange.AsDynamicRange();
range->RegisterSelection(*this);
}
aRange.RegisterSelection(*this);
if (!mFrameSelection) {
return; // nothing to do
}

View File

@ -90,14 +90,25 @@ already_AddRefed<StaticRange> StaticRange::Create(
return staticRange.forget();
}
StaticRange::~StaticRange() {
DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
}
template <typename SPT, typename SRT, typename EPT, typename ERT>
void StaticRange::DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
const RangeBoundaryBase<EPT, ERT>& aEndBoundary,
nsINode* aRootNode) {
bool checkCommonAncestor =
IsInAnySelection() && (mStart.Container() != aStartBoundary.Container() ||
mEnd.Container() != aEndBoundary.Container());
mStart.CopyFrom(aStartBoundary, RangeBoundaryIsMutationObserved::No);
mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::No);
MOZ_ASSERT(mStart.IsSet() == mEnd.IsSet());
mIsPositioned = mStart.IsSet() && mEnd.IsSet();
if (checkCommonAncestor) {
UpdateCommonAncestorIfNecessary();
}
}
/* static */

View File

@ -75,7 +75,7 @@ class StaticRange final : public AbstractRange {
protected:
explicit StaticRange(nsINode* aNode)
: AbstractRange(aNode, /* aIsDynamicRange = */ false) {}
virtual ~StaticRange() = default;
virtual ~StaticRange();
public:
NS_DECL_ISUPPORTS_INHERITED

View File

@ -345,12 +345,12 @@ bool nsINode::IsSelected(const uint32_t aStartOffset,
nsTHashSet<Selection*> ancestorSelections;
for (; n; n = GetClosestCommonInclusiveAncestorForRangeInSelection(
n->GetParentNode())) {
const LinkedList<nsRange>* ranges =
const LinkedList<AbstractRange>* ranges =
n->GetExistingClosestCommonInclusiveAncestorRanges();
if (!ranges) {
continue;
}
for (const nsRange* range : *ranges) {
for (const AbstractRange* range : *ranges) {
MOZ_ASSERT(range->IsInAnySelection(),
"Why is this range registered with a node?");
// Looks like that IsInSelection() assert fails sometimes...

View File

@ -76,6 +76,7 @@ inline bool IsSpaceCharacter(char aChar) {
return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' ||
aChar == '\f';
}
class AbstractRange;
class AccessibleNode;
template <typename T>
class AncestorsOfTypeIterator;
@ -1350,7 +1351,7 @@ class nsINode : public mozilla::dom::EventTarget {
* pushing DOMSlots up to the next allocation bucket size, at the cost of
* some complexity.
*/
mozilla::UniquePtr<mozilla::LinkedList<nsRange>>
mozilla::UniquePtr<mozilla::LinkedList<mozilla::dom::AbstractRange>>
mClosestCommonInclusiveAncestorRanges;
};
@ -2211,7 +2212,7 @@ class nsINode : public mozilla::dom::EventTarget {
/**
* See nsSlots::mClosestCommonInclusiveAncestorRanges.
*/
const mozilla::LinkedList<nsRange>*
const mozilla::LinkedList<mozilla::dom::AbstractRange>*
GetExistingClosestCommonInclusiveAncestorRanges() const {
if (!HasSlots()) {
return nullptr;
@ -2222,7 +2223,7 @@ class nsINode : public mozilla::dom::EventTarget {
/**
* See nsSlots::mClosestCommonInclusiveAncestorRanges.
*/
mozilla::LinkedList<nsRange>*
mozilla::LinkedList<mozilla::dom::AbstractRange>*
GetExistingClosestCommonInclusiveAncestorRanges() {
if (!HasSlots()) {
return nullptr;
@ -2233,7 +2234,7 @@ class nsINode : public mozilla::dom::EventTarget {
/**
* See nsSlots::mClosestCommonInclusiveAncestorRanges.
*/
mozilla::UniquePtr<mozilla::LinkedList<nsRange>>&
mozilla::UniquePtr<mozilla::LinkedList<mozilla::dom::AbstractRange>>&
GetClosestCommonInclusiveAncestorRangesPtr() {
return Slots()->mClosestCommonInclusiveAncestorRanges;
}

View File

@ -166,7 +166,6 @@ nsRange::~nsRange() {
nsRange::nsRange(nsINode* aNode)
: AbstractRange(aNode, /* aIsDynamicRange = */ true),
mRegisteredClosestCommonInclusiveAncestor(nullptr),
mNextStartRef(nullptr),
mNextEndRef(nullptr) {
// printf("Size of nsRange: %zu\n", sizeof(nsRange));
@ -218,19 +217,8 @@ NS_INTERFACE_MAP_END_INHERITING(AbstractRange)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange)
tmp->mSelections.Clear();
// We _could_ just rely on Reset() to
// UnregisterClosestCommonInclusiveAncestor(), but it wouldn't know we're
// calling it from Unlink and so would do more work than it really needs to.
if (tmp->mRegisteredClosestCommonInclusiveAncestor) {
tmp->UnregisterClosestCommonInclusiveAncestor(
tmp->mRegisteredClosestCommonInclusiveAncestor, true);
}
// `Reset()` unlinks `mStart`, `mEnd` and `mRoot`.
tmp->Reset();
MOZ_DIAGNOSTIC_ASSERT(!tmp->isInList(),
"Shouldn't be registered now that we're unlinking");
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsRange, AbstractRange)
@ -246,103 +234,6 @@ bool nsRange::MaybeInterruptLastRelease() {
return interrupt;
}
static void MarkDescendants(nsINode* aNode) {
// Set NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection on
// aNode's descendants unless aNode is already marked as a range common
// ancestor or a descendant of one, in which case all of our descendants have
// the bit set already.
if (!aNode->IsMaybeSelected()) {
// don't set the Descendant bit on |aNode| itself
nsINode* node = aNode->GetNextNode(aNode);
while (node) {
node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(aNode);
} else {
// optimize: skip this sub-tree since it's marked already.
node = node->GetNextNonChildNode(aNode);
}
}
}
}
static void UnmarkDescendants(nsINode* aNode) {
// Unset NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection
// on aNode's descendants unless aNode is a descendant of another range common
// ancestor. Also, exclude descendants of range common ancestors (but not the
// common ancestor itself).
if (!aNode
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
// we know |aNode| doesn't have any bit set
nsINode* node = aNode->GetNextNode(aNode);
while (node) {
node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(aNode);
} else {
// We found an ancestor of an overlapping range, skip its descendants.
node = node->GetNextNonChildNode(aNode);
}
}
}
}
void nsRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
MOZ_ASSERT(aNode, "bad arg");
MOZ_DIAGNOSTIC_ASSERT(IsInAnySelection(),
"registering range not in selection");
mRegisteredClosestCommonInclusiveAncestor = aNode;
MarkDescendants(aNode);
UniquePtr<LinkedList<nsRange>>& ranges =
aNode->GetClosestCommonInclusiveAncestorRangesPtr();
if (!ranges) {
ranges = MakeUnique<LinkedList<nsRange>>();
}
MOZ_DIAGNOSTIC_ASSERT(!isInList());
ranges->insertBack(this);
aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
}
void nsRange::UnregisterClosestCommonInclusiveAncestor(nsINode* aNode,
bool aIsUnlinking) {
MOZ_ASSERT(aNode, "bad arg");
NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(),
"wrong node");
MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor,
"wrong node");
LinkedList<nsRange>* ranges =
aNode->GetExistingClosestCommonInclusiveAncestorRanges();
MOZ_ASSERT(ranges);
mRegisteredClosestCommonInclusiveAncestor = nullptr;
#ifdef DEBUG
bool found = false;
for (nsRange* range : *ranges) {
if (range == this) {
found = true;
break;
}
}
MOZ_ASSERT(found,
"We should be in the list on our registered common ancestor");
#endif // DEBUG
remove();
// We don't want to waste time unmarking flags on nodes that are
// being unlinked anyway.
if (!aIsUnlinking && ranges->isEmpty()) {
aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection();
UnmarkDescendants(aNode);
}
}
void nsRange::AdjustNextRefsOnCharacterDataSplit(
const nsIContent& aContent, const CharacterDataChangeInfo& aInfo) {
// If the splitted text node is immediately before a range boundary point
@ -594,7 +485,7 @@ void nsRange::ContentAppended(nsIContent* aFirstNewContent) {
while (child) {
if (!child
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
MarkDescendants(child);
MarkDescendants(*child);
child
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
@ -646,7 +537,7 @@ void nsRange::ContentInserted(nsIContent* aChild) {
if (container->IsMaybeSelected() &&
!aChild
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
MarkDescendants(aChild);
MarkDescendants(*aChild);
aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
@ -739,7 +630,7 @@ void nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling) {
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
aChild
->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
UnmarkDescendants(aChild);
UnmarkDescendants(*aChild);
}
}
@ -985,26 +876,7 @@ void nsRange::DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
mEnd.CopyFrom(aEndBoundary, RangeBoundaryIsMutationObserved::Yes);
if (checkCommonAncestor) {
nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
nsINode* newCommonAncestor = GetClosestCommonInclusiveAncestor();
if (newCommonAncestor != oldCommonAncestor) {
if (oldCommonAncestor) {
UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false);
}
if (newCommonAncestor) {
RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
} else {
MOZ_DIAGNOSTIC_ASSERT(!mIsPositioned, "unexpected disconnected nodes");
mSelections.Clear();
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredClosestCommonInclusiveAncestor,
"How can we have a registered common ancestor when we "
"didn't register ourselves?");
MOZ_DIAGNOSTIC_ASSERT(!isInList(),
"Shouldn't be registered if we have no "
"mRegisteredClosestCommonInclusiveAncestor");
}
}
UpdateCommonAncestorIfNecessary();
}
// This needs to be the last thing this function does, other than notifying
@ -1034,43 +906,6 @@ void nsRange::DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
}
}
bool nsRange::IsInSelection(const Selection& aSelection) const {
return mSelections.Contains(&aSelection);
}
void nsRange::RegisterSelection(Selection& aSelection) {
if (IsInSelection(aSelection)) {
return;
}
bool isFirstSelection = mSelections.IsEmpty();
mSelections.AppendElement(&aSelection);
if (isFirstSelection && !mRegisteredClosestCommonInclusiveAncestor) {
nsINode* commonAncestor = GetClosestCommonInclusiveAncestor();
MOZ_ASSERT(commonAncestor, "unexpected disconnected nodes");
RegisterClosestCommonInclusiveAncestor(commonAncestor);
}
}
const nsTArray<WeakPtr<Selection>>& nsRange::GetSelections() const {
return mSelections;
}
void nsRange::UnregisterSelection(Selection& aSelection) {
mSelections.RemoveElement(&aSelection);
if (mSelections.IsEmpty() && mRegisteredClosestCommonInclusiveAncestor) {
UnregisterClosestCommonInclusiveAncestor(
mRegisteredClosestCommonInclusiveAncestor, false);
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredClosestCommonInclusiveAncestor,
"How can we have a registered common ancestor when we "
"just unregistered?");
MOZ_DIAGNOSTIC_ASSERT(
!isInList(),
"Shouldn't be registered if we have no "
"mRegisteredClosestCommonInclusiveAncestor after unregistering");
}
}
void nsRange::Reset() {
DoSetRange(RawRangeBoundary(), RawRangeBoundary(), nullptr);
}

View File

@ -20,7 +20,6 @@
#include "mozilla/ErrorResult.h"
#include "mozilla/RangeBoundary.h"
#include "mozilla/RefPtr.h"
#include "mozilla/WeakPtr.h"
namespace mozilla {
class RectCallback;
@ -36,9 +35,7 @@ class Selection;
} // namespace mozilla
class nsRange final : public mozilla::dom::AbstractRange,
public nsStubMutationObserver,
// For linking together selection-associated ranges.
public mozilla::LinkedListElement<nsRange> {
public nsStubMutationObserver {
using ErrorResult = mozilla::ErrorResult;
using AbstractRange = mozilla::dom::AbstractRange;
using DocGroup = mozilla::dom::DocGroup;
@ -91,28 +88,6 @@ class nsRange final : public mozilla::dom::AbstractRange,
nsINode* GetRoot() const { return mRoot; }
/**
* Return true if this range is part of a Selection object
* and isn't detached.
*/
bool IsInAnySelection() const { return !mSelections.IsEmpty(); }
MOZ_CAN_RUN_SCRIPT void RegisterSelection(
mozilla::dom::Selection& aSelection);
void UnregisterSelection(mozilla::dom::Selection& aSelection);
/**
* Returns a list of all Selections the range is associated with.
*/
const nsTArray<mozilla::WeakPtr<mozilla::dom::Selection>>& GetSelections()
const;
/**
* Return true if this range is in |aSelection|.
*/
bool IsInSelection(const mozilla::dom::Selection& aSelection) const;
/**
* Return true if this range was generated.
* @see SetIsGenerated
@ -377,16 +352,6 @@ class nsRange final : public mozilla::dom::AbstractRange,
nsINode* GetRegisteredClosestCommonInclusiveAncestor();
protected:
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void RegisterClosestCommonInclusiveAncestor(nsINode* aNode);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void UnregisterClosestCommonInclusiveAncestor(nsINode* aNode,
bool aIsUnlinking);
/**
* DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets
* mStart and mEnd, or some other internal methods modify `mStart` and/or
@ -448,13 +413,6 @@ class nsRange final : public mozilla::dom::AbstractRange,
#endif // #ifdef DEBUG
nsCOMPtr<nsINode> mRoot;
// mRegisteredClosestCommonInclusiveAncestor is only non-null when the range
// IsInAnySelection(). It's kept alive via mStart/mEnd,
// because we update it any time those could become disconnected from it.
nsINode* MOZ_NON_OWNING_REF mRegisteredClosestCommonInclusiveAncestor;
// A Range can be part of multiple |Selection|s. This is a very rare use case.
AutoTArray<mozilla::WeakPtr<mozilla::dom::Selection>, 1> mSelections;
// These raw pointers are used to remember a child that is about
// to be inserted between a CharacterData call and a subsequent

View File

@ -133,15 +133,9 @@ void nsTextNode::List(FILE* out, int32_t aIndent) const {
fprintf(out, "Text@%p", static_cast<const void*>(this));
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges =
const LinkedList<AbstractRange>* ranges =
GetExistingClosestCommonInclusiveAncestorRanges();
int32_t count = 0;
if (ranges) {
// Can't use range-based iteration on a const LinkedList, unfortunately.
for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) {
++count;
}
}
uint32_t count = ranges ? ranges->length() : 0;
fprintf(out, " ranges:%d", count);
}
fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame()));

View File

@ -1,2 +0,0 @@
[custom-highlight-painting-staticrange-002.html]
expected: FAIL

View File

@ -1,2 +0,0 @@
[custom-highlight-painting-staticrange-003.html]
expected: FAIL