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]", fprintf(out, " state=[%llx]",
static_cast<unsigned long long>(State().GetInternalValue())); static_cast<unsigned long long>(State().GetInternalValue()));
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
if (IsCommonAncestorForRangeInSelection()) { if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges(); const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
int32_t count = 0; int32_t count = 0;
if (ranges) { if (ranges) {

View File

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

View File

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

View File

@@ -366,8 +366,15 @@ class nsRange final : public mozilla::dom::AbstractRange,
typedef nsTHashtable<nsPtrHashKey<nsRange>> RangeHashTable; typedef nsTHashtable<nsPtrHashKey<nsRange>> RangeHashTable;
protected: 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 * DoSetRange() is called when `AbstractRange::SetStartAndEndInternal()` sets
@@ -392,14 +399,16 @@ class nsRange final : public mozilla::dom::AbstractRange,
nsINode* aRootNode, bool aNotInsertedYet = false); 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 * 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 * 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 * faster version of GetCommonAncestor that only works for ranges in a
* Selection. The method will assert and the behavior is undefined if called * Selection. The method will assert and the behavior is undefined if called
* on a range where IsInSelection() is false. * on a range where IsInSelection() is false.
*/ */
nsINode* GetRegisteredCommonAncestor(); nsINode* GetRegisteredClosestCommonInclusiveAncestor();
// Helper to IsNodeSelected. // Helper to IsNodeSelected.
static bool IsNodeInSortedRanges(nsINode* aNode, uint32_t aStartOffset, static bool IsNodeInSortedRanges(nsINode* aNode, uint32_t aStartOffset,
@@ -432,7 +441,7 @@ class nsRange final : public mozilla::dom::AbstractRange,
return; return;
} }
sIsNested = true; sIsNested = true;
mCommonAncestor = mRange->GetRegisteredCommonAncestor(); mCommonAncestor = mRange->GetRegisteredClosestCommonInclusiveAncestor();
} }
~AutoInvalidateSelection(); ~AutoInvalidateSelection();
nsRange* mRange; nsRange* mRange;
@@ -441,10 +450,10 @@ class nsRange final : public mozilla::dom::AbstractRange,
}; };
nsCOMPtr<nsINode> mRoot; 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, // IsInSelection(). It's kept alive via mStart/mEnd,
// because we update it any time those could become disconnected from it. // 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; mozilla::WeakPtr<mozilla::dom::Selection> mSelection;
// These raw pointers are used to remember a child that is about // 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, "Text@%p", static_cast<const void*>(this));
fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags()));
if (IsCommonAncestorForRangeInSelection()) { if (IsClosestCommonInclusiveAncestorForRangeInSelection()) {
const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges(); const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges();
int32_t count = 0; int32_t count = 0;
if (ranges) { if (ranges) {