mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Bug 1828469, part 2: Moved some logic from nsRange
to AbstractRange
to support StaticRange
s 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:
parent
1bbcb68c33
commit
bfe0baa6d6
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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...
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()));
|
||||
|
@ -1,2 +0,0 @@
|
||||
[custom-highlight-painting-staticrange-002.html]
|
||||
expected: FAIL
|
@ -1,2 +0,0 @@
|
||||
[custom-highlight-painting-staticrange-003.html]
|
||||
expected: FAIL
|
Loading…
Reference in New Issue
Block a user