Bug 1608071: part 1) Rename common ancestor to closest common inclusive ancestor around nsRange. r=smaug

It's more precise and enables one to reason more clearly about related
code.

Differential Revision: https://phabricator.services.mozilla.com/D59318
This commit is contained in:
Mirko Brodesser
2020-01-13 10:29:31 +00:00
parent 868cfa55ef
commit ae70537c80
5 changed files with 149 additions and 97 deletions

View File

@@ -2695,7 +2695,7 @@ void Element::List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const {
fprintf(out, " state=[%llx]",
static_cast<unsigned long long>(State().GetInternalValue()));
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
if (IsCommonAncestorForRangeInSelection()) {
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
int32_t count = 0;
if (ranges) {

View File

@@ -1350,14 +1350,15 @@ class nsINode : public mozilla::dom::EventTarget {
inline bool IsRootOfChromeAccessOnlySubtree() const;
/**
* Returns true if |this| node is the common ancestor of the start/end
* nodes of a Range in a Selection or a descendant of such a common ancestor.
* This node is definitely not selected when |false| is returned, but it may
* or may not be selected when |true| is returned.
* Returns true if |this| node is the closest common inclusive ancestor
* (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor) of the
* start/end nodes of a Range in a Selection or a descendant of such a common
* ancestor. This node is definitely not selected when |false| is returned,
* but it may or may not be selected when |true| is returned.
*/
bool IsSelectionDescendant() const {
return IsDescendantOfCommonAncestorForRangeInSelection() ||
IsCommonAncestorForRangeInSelection();
return IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() ||
IsClosestCommonInclusiveAncestorForRangeInSelection();
}
/**
@@ -1595,11 +1596,11 @@ class nsINode : public mozilla::dom::EventTarget {
ElementHasPart,
// Set if the element might have a contenteditable attribute set.
ElementMayHaveContentEditableAttr,
// Set if the node is the common ancestor of the start/end nodes of a Range
// that is in a Selection.
NodeIsCommonAncestorForRangeInSelection,
// Set if the node is the closest common inclusive ancestor of the start/end
// nodes of a Range that is in a Selection.
NodeIsClosestCommonInclusiveAncestorForRangeInSelection,
// Set if the node is a descendant of a node with the above bit set.
NodeIsDescendantOfCommonAncestorForRangeInSelection,
NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection,
// Set if CanSkipInCC check has been done for this subtree root.
NodeIsCCMarkedRoot,
// Maybe set if this node is in black subtree.
@@ -1693,23 +1694,44 @@ class nsINode : public mozilla::dom::EventTarget {
bool MayHaveContentEditableAttr() const {
return GetBoolFlag(ElementMayHaveContentEditableAttr);
}
bool IsCommonAncestorForRangeInSelection() const {
return GetBoolFlag(NodeIsCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
bool IsClosestCommonInclusiveAncestorForRangeInSelection() const {
return GetBoolFlag(NodeIsClosestCommonInclusiveAncestorForRangeInSelection);
}
void SetCommonAncestorForRangeInSelection() {
SetBoolFlag(NodeIsCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void SetClosestCommonInclusiveAncestorForRangeInSelection() {
SetBoolFlag(NodeIsClosestCommonInclusiveAncestorForRangeInSelection);
}
void ClearCommonAncestorForRangeInSelection() {
ClearBoolFlag(NodeIsCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void ClearClosestCommonInclusiveAncestorForRangeInSelection() {
ClearBoolFlag(NodeIsClosestCommonInclusiveAncestorForRangeInSelection);
}
bool IsDescendantOfCommonAncestorForRangeInSelection() const {
return GetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
bool IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() const {
return GetBoolFlag(
NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection);
}
void SetDescendantOfCommonAncestorForRangeInSelection() {
SetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() {
SetBoolFlag(
NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection);
}
void ClearDescendantOfCommonAncestorForRangeInSelection() {
ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection);
/**
* https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor
*/
void ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection() {
ClearBoolFlag(
NodeIsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection);
}
void SetCCMarkedRoot(bool aValue) { SetBoolFlag(NodeIsCCMarkedRoot, aValue); }

View File

@@ -119,8 +119,10 @@ static void InvalidateAllFrames(nsINode* aNode) {
}
static nsINode* GetNextRangeCommonAncestor(nsINode* aNode) {
while (aNode && !aNode->IsCommonAncestorForRangeInSelection()) {
if (!aNode->IsDescendantOfCommonAncestorForRangeInSelection()) {
while (aNode &&
!aNode->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
if (!aNode
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
return nullptr;
}
aNode = aNode->GetParentNode();
@@ -270,7 +272,7 @@ nsRange::~nsRange() {
nsRange::nsRange(nsINode* aNode)
: AbstractRange(aNode),
mRegisteredCommonAncestor(nullptr),
mRegisteredClosestCommonInclusiveAncestor(nullptr),
mNextStartRef(nullptr),
mNextEndRef(nullptr) {
// printf("Size of nsRange: %zu\n", sizeof(nsRange));
@@ -309,11 +311,12 @@ NS_INTERFACE_MAP_END_INHERITING(AbstractRange)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsRange, AbstractRange)
// We _could_ just rely on Reset() to UnregisterCommonAncestor(),
// but it wouldn't know we're calling it from Unlink and so would do
// more work than it really needs to.
if (tmp->mRegisteredCommonAncestor) {
tmp->UnregisterCommonAncestor(tmp->mRegisteredCommonAncestor, true);
// 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);
}
tmp->Reset();
@@ -330,16 +333,16 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsRange, AbstractRange)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
static void MarkDescendants(nsINode* aNode) {
// Set NodeIsDescendantOfCommonAncestorForRangeInSelection 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.
// 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->IsSelectionDescendant()) {
// don't set the Descendant bit on |aNode| itself
nsINode* node = aNode->GetNextNode(aNode);
while (node) {
node->SetDescendantOfCommonAncestorForRangeInSelection();
if (!node->IsCommonAncestorForRangeInSelection()) {
node->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(aNode);
} else {
// optimize: skip this sub-tree since it's marked already.
@@ -350,16 +353,17 @@ static void MarkDescendants(nsINode* aNode) {
}
static void UnmarkDescendants(nsINode* aNode) {
// Unset NodeIsDescendantOfCommonAncestorForRangeInSelection 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->IsDescendantOfCommonAncestorForRangeInSelection()) {
// 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->ClearDescendantOfCommonAncestorForRangeInSelection();
if (!node->IsCommonAncestorForRangeInSelection()) {
node->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
if (!node->IsClosestCommonInclusiveAncestorForRangeInSelection()) {
node = node->GetNextNode(aNode);
} else {
// We found an ancestor of an overlapping range, skip its descendants.
@@ -369,12 +373,12 @@ static void UnmarkDescendants(nsINode* aNode) {
}
}
void nsRange::RegisterCommonAncestor(nsINode* aNode) {
void nsRange::RegisterClosestCommonInclusiveAncestor(nsINode* aNode) {
MOZ_ASSERT(aNode, "bad arg");
MOZ_DIAGNOSTIC_ASSERT(IsInSelection(), "registering range not in selection");
mRegisteredCommonAncestor = aNode;
mRegisteredClosestCommonInclusiveAncestor = aNode;
MarkDescendants(aNode);
@@ -385,17 +389,20 @@ void nsRange::RegisterCommonAncestor(nsINode* aNode) {
MOZ_DIAGNOSTIC_ASSERT(!isInList());
ranges->insertBack(this);
aNode->SetCommonAncestorForRangeInSelection();
aNode->SetClosestCommonInclusiveAncestorForRangeInSelection();
}
void nsRange::UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking) {
void nsRange::UnregisterClosestCommonInclusiveAncestor(nsINode* aNode,
bool aIsUnlinking) {
MOZ_ASSERT(aNode, "bad arg");
NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node");
MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredCommonAncestor, "wrong node");
NS_ASSERTION(aNode->IsClosestCommonInclusiveAncestorForRangeInSelection(),
"wrong node");
MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredClosestCommonInclusiveAncestor,
"wrong node");
LinkedList<nsRange>* ranges = aNode->GetExistingCommonAncestorRanges();
MOZ_ASSERT(ranges);
mRegisteredCommonAncestor = nullptr;
mRegisteredClosestCommonInclusiveAncestor = nullptr;
#ifdef DEBUG
bool found = false;
@@ -414,7 +421,7 @@ void nsRange::UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking) {
// We don't want to waste time unmarking flags on nodes that are
// being unlinked anyway.
if (!aIsUnlinking && ranges->isEmpty()) {
aNode->ClearCommonAncestorForRangeInSelection();
aNode->ClearClosestCommonInclusiveAncestorForRangeInSelection();
UnmarkDescendants(aNode);
}
}
@@ -554,13 +561,13 @@ void nsRange::CharacterDataChanged(nsIContent* aContent,
bool isCommonAncestor =
IsInSelection() && mStart.Container() == mEnd.Container();
if (isCommonAncestor) {
UnregisterCommonAncestor(mStart.Container(), false);
RegisterCommonAncestor(newStart.Container());
UnregisterClosestCommonInclusiveAncestor(mStart.Container(), false);
RegisterClosestCommonInclusiveAncestor(newStart.Container());
}
if (mStart.Container()
->IsDescendantOfCommonAncestorForRangeInSelection()) {
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
newStart.Container()
->SetDescendantOfCommonAncestorForRangeInSelection();
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
} else {
// If boundary is inside changed text, position it before change
@@ -607,12 +614,16 @@ void nsRange::CharacterDataChanged(nsIContent* aContent,
IsInSelection() && mStart.Container() == mEnd.Container();
if (isCommonAncestor && !newStart.Container()) {
// The split occurs inside the range.
UnregisterCommonAncestor(mStart.Container(), false);
RegisterCommonAncestor(mStart.Container()->GetParentNode());
newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
} else if (mEnd.Container()
->IsDescendantOfCommonAncestorForRangeInSelection()) {
newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection();
UnregisterClosestCommonInclusiveAncestor(mStart.Container(), false);
RegisterClosestCommonInclusiveAncestor(
mStart.Container()->GetParentNode());
newEnd.Container()
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
} else if (
mEnd.Container()
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
newEnd.Container()
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
} else {
CheckedUint32 newEndOffset{0};
@@ -671,9 +682,11 @@ void nsRange::ContentAppended(nsIContent* aFirstNewContent) {
if (container->IsSelectionDescendant() && IsInSelection()) {
nsINode* child = aFirstNewContent;
while (child) {
if (!child->IsDescendantOfCommonAncestorForRangeInSelection()) {
if (!child
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
MarkDescendants(child);
child->SetDescendantOfCommonAncestorForRangeInSelection();
child
->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
child = child->GetNextSibling();
}
@@ -724,9 +737,10 @@ void nsRange::ContentInserted(nsIContent* aChild) {
}
if (container->IsSelectionDescendant() &&
!aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
!aChild
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
MarkDescendants(aChild);
aChild->SetDescendantOfCommonAncestorForRangeInSelection();
aChild->SetDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
}
if (mNextStartRef || mNextEndRef) {
@@ -810,8 +824,10 @@ void nsRange::ContentRemoved(nsIContent* aChild, nsIContent* aPreviousSibling) {
MOZ_ASSERT(mEnd.Ref() != aChild);
if (container->IsSelectionDescendant() &&
aChild->IsDescendantOfCommonAncestorForRangeInSelection()) {
aChild->ClearDescendantOfCommonAncestorForRangeInSelection();
aChild
->IsDescendantOfClosestCommonInclusiveAncestorForRangeInSelection()) {
aChild
->ClearDescendantOfClosestCommonInclusiveAncestorForRangeInSelection();
UnmarkDescendants(aChild);
}
}
@@ -1012,24 +1028,24 @@ void nsRange::DoSetRange(const RangeBoundaryBase<SPT, SRT>& aStartBoundary,
mEnd = aEndBoundary;
if (checkCommonAncestor) {
nsINode* oldCommonAncestor = mRegisteredCommonAncestor;
nsINode* oldCommonAncestor = mRegisteredClosestCommonInclusiveAncestor;
nsINode* newCommonAncestor = GetCommonAncestor();
if (newCommonAncestor != oldCommonAncestor) {
if (oldCommonAncestor) {
UnregisterCommonAncestor(oldCommonAncestor, false);
UnregisterClosestCommonInclusiveAncestor(oldCommonAncestor, false);
}
if (newCommonAncestor) {
RegisterCommonAncestor(newCommonAncestor);
RegisterClosestCommonInclusiveAncestor(newCommonAncestor);
} else {
NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
mSelection = nullptr;
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredCommonAncestor,
!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 "
"mRegisteredCommonAncestor");
"mRegisteredClosestCommonInclusiveAncestor");
}
}
}
@@ -1077,16 +1093,18 @@ void nsRange::SetSelection(mozilla::dom::Selection* aSelection) {
if (mSelection) {
nsINode* commonAncestor = GetCommonAncestor();
NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
RegisterCommonAncestor(commonAncestor);
} else if (mRegisteredCommonAncestor) {
UnregisterCommonAncestor(mRegisteredCommonAncestor, false);
RegisterClosestCommonInclusiveAncestor(commonAncestor);
} else if (mRegisteredClosestCommonInclusiveAncestor) {
UnregisterClosestCommonInclusiveAncestor(
mRegisteredClosestCommonInclusiveAncestor, false);
MOZ_DIAGNOSTIC_ASSERT(
!mRegisteredCommonAncestor,
!mRegisteredClosestCommonInclusiveAncestor,
"How can we have a registered common ancestor when we "
"just unregistered?");
MOZ_DIAGNOSTIC_ASSERT(!isInList(),
MOZ_DIAGNOSTIC_ASSERT(
!isInList(),
"Shouldn't be registered if we have no "
"mRegisteredCommonAncestor after unregistering");
"mRegisteredClosestCommonInclusiveAncestor after unregistering");
}
}
@@ -2965,11 +2983,12 @@ nsresult nsRange::GetUsedFontFaces(nsLayoutUtils::UsedFontFaceList& aResult,
return NS_OK;
}
nsINode* nsRange::GetRegisteredCommonAncestor() {
nsINode* nsRange::GetRegisteredClosestCommonInclusiveAncestor() {
MOZ_ASSERT(IsInSelection(),
"GetRegisteredCommonAncestor only valid for range in selection");
MOZ_ASSERT(mRegisteredCommonAncestor);
return mRegisteredCommonAncestor;
"GetRegisteredClosestCommonInclusiveAncestor only valid for range "
"in selection");
MOZ_ASSERT(mRegisteredClosestCommonInclusiveAncestor);
return mRegisteredClosestCommonInclusiveAncestor;
}
/* static */
@@ -2988,10 +3007,12 @@ nsRange::AutoInvalidateSelection::~AutoInvalidateSelection() {
// we have a different common ancestor now, and if so invalidate its subtree
// so it paints the selection it's in now.
if (mRange->IsInSelection()) {
nsINode* commonAncestor = mRange->GetRegisteredCommonAncestor();
nsINode* commonAncestor =
mRange->GetRegisteredClosestCommonInclusiveAncestor();
// XXXbz can commonAncestor really be null here? I wouldn't think so! If
// it _were_, then in a debug build GetRegisteredCommonAncestor() would have
// fatally asserted.
// it _were_, then in a debug build
// GetRegisteredClosestCommonInclusiveAncestor() would have fatally
// asserted.
if (commonAncestor && commonAncestor != mCommonAncestor) {
::InvalidateAllFrames(commonAncestor);
}

View File

@@ -366,8 +366,15 @@ class nsRange final : public mozilla::dom::AbstractRange,
typedef nsTHashtable<nsPtrHashKey<nsRange>> RangeHashTable;
protected:
void RegisterCommonAncestor(nsINode* aNode);
void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking);
/**
* 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
@@ -392,14 +399,16 @@ class nsRange final : public mozilla::dom::AbstractRange,
nsINode* aRootNode, bool aNotInsertedYet = false);
/**
* For a range for which IsInSelection() is true, return the common ancestor
* For a range for which IsInSelection() is true, return the closest common
* inclusive ancestor
* (https://dom.spec.whatwg.org/#concept-tree-inclusive-ancestor)
* for the range, which we had to compute when the common ancestor changed or
* IsInSelection became true, so we could register with it. That is, it's a
* faster version of GetCommonAncestor that only works for ranges in a
* Selection. The method will assert and the behavior is undefined if called
* on a range where IsInSelection() is false.
*/
nsINode* GetRegisteredCommonAncestor();
nsINode* GetRegisteredClosestCommonInclusiveAncestor();
// Helper to IsNodeSelected.
static bool IsNodeInSortedRanges(nsINode* aNode, uint32_t aStartOffset,
@@ -432,7 +441,7 @@ class nsRange final : public mozilla::dom::AbstractRange,
return;
}
sIsNested = true;
mCommonAncestor = mRange->GetRegisteredCommonAncestor();
mCommonAncestor = mRange->GetRegisteredClosestCommonInclusiveAncestor();
}
~AutoInvalidateSelection();
nsRange* mRange;
@@ -441,10 +450,10 @@ class nsRange final : public mozilla::dom::AbstractRange,
};
nsCOMPtr<nsINode> mRoot;
// mRegisteredCommonAncestor is only non-null when the range
// mRegisteredClosestCommonInclusiveAncestor is only non-null when the range
// IsInSelection(). It's kept alive via mStart/mEnd,
// because we update it any time those could become disconnected from it.
nsINode* MOZ_NON_OWNING_REF mRegisteredCommonAncestor;
nsINode* MOZ_NON_OWNING_REF mRegisteredClosestCommonInclusiveAncestor;
mozilla::WeakPtr<mozilla::dom::Selection> mSelection;
// These raw pointers are used to remember a child that is about

View File

@@ -133,7 +133,7 @@ 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 (IsCommonAncestorForRangeInSelection()) {
if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
int32_t count = 0;
if (ranges) {