Bug 1399626 - Part 4: Allow initializing nsIContentIterator with RangeBoundaries, r=masayuki

This patch adds an overload to nsIContentIterator::Init which accepts
RangeBoundary objects, and modifies the codepath to avoid using the offset of
the start or end nodes to construct the nsIContentIterator.

MozReview-Commit-ID: 5ZqKeiUunoN
This commit is contained in:
Nika Layzell 2017-10-02 10:58:31 -04:00
Родитель 7a623e996f
Коммит cbc8fa8a3b
5 изменённых файлов: 155 добавлений и 153 удалений

Просмотреть файл

@ -18,55 +18,37 @@
#include "nsElementTable.h"
using mozilla::DebugOnly;
using mozilla::RawRangeBoundary;
// couple of utility static functs
///////////////////////////////////////////////////////////////////////////
// NodeToParentOffset: returns the node's parent and offset.
//
static nsINode*
NodeToParentOffset(nsINode* aNode, int32_t* aOffset)
{
*aOffset = 0;
nsINode* parent = aNode->GetParentNode();
if (parent) {
*aOffset = parent->IndexOf(aNode);
NS_WARNING_ASSERTION(*aOffset >= 0, "bad offset");
}
return parent;
}
///////////////////////////////////////////////////////////////////////////
// NodeIsInTraversalRange: returns true if content is visited during
// the traversal of the range in the specified mode.
//
static bool
NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
nsINode* aStartContainer, int32_t aStartOffset,
nsINode* aEndContainer, int32_t aEndOffset)
const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
if (NS_WARN_IF(!aStartContainer) || NS_WARN_IF(!aEndContainer) ||
if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet()) ||
NS_WARN_IF(!aNode)) {
return false;
}
// If a leaf node contains an end point of the traversal range, it is
// always in the traversal range.
if (aNode == aStartContainer || aNode == aEndContainer) {
if (aNode == aStart.Container() || aNode == aEnd.Container()) {
if (aNode->IsNodeOfType(nsINode::eDATA_NODE)) {
return true; // text node or something
}
if (!aNode->HasChildren()) {
MOZ_ASSERT(aNode != aStartContainer || !aStartOffset,
"aStartContainer doesn't have children and not a data node, "
"aStartOffset should be 0");
MOZ_ASSERT(aNode != aEndContainer || !aEndOffset,
"aEndContainer doesn't have children and not a data node, "
"aEndOffset should be 0");
MOZ_ASSERT(aNode != aStart.Container() || aStart.IsStartOfContainer(),
"aStart.Container() doesn't have children and not a data node, "
"aStart should be at the beginning of its container");
MOZ_ASSERT(aNode != aEnd.Container() || aEnd.IsStartOfContainer(),
"aEnd.Container() doesn't have children and not a data node, "
"aEnd should be at the beginning of its container");
return true;
}
}
@ -76,22 +58,22 @@ NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode,
return false;
}
int32_t indx = parent->IndexOf(aNode);
NS_WARNING_ASSERTION(indx != -1, "bad indx");
if (!aIsPreMode) {
// aNode should always be content, as we have a parent, but let's just be
// extra careful and check.
nsIContent* content = NS_WARN_IF(!aNode->IsContent())
? nullptr
: aNode->AsContent();
// Post mode: start < node <= end.
return nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
parent, indx + 1) < 0 &&
nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
parent, indx + 1) >= 0;
RawRangeBoundary afterNode(parent, content);
return nsContentUtils::ComparePoints(aStart, afterNode) < 0 &&
nsContentUtils::ComparePoints(aEnd, afterNode) >= 0;
}
// Pre mode: start <= node < end.
return nsContentUtils::ComparePoints(aStartContainer, aStartOffset,
parent, indx) <= 0 &&
nsContentUtils::ComparePoints(aEndContainer, aEndOffset,
parent, indx) > 0;
RawRangeBoundary beforeNode(parent, aNode->GetPreviousSibling());
return nsContentUtils::ComparePoints(aStart, beforeNode) <= 0 &&
nsContentUtils::ComparePoints(aEnd, beforeNode) > 0;
}
@ -116,6 +98,9 @@ public:
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset) override;
virtual nsresult Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd) override;
virtual void First() override;
virtual void Last() override;
@ -139,8 +124,8 @@ protected:
* - aStartOffset and aEndOffset are valid for its container.
* - The start point and the end point are in document order.
*/
nsresult InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset);
nsresult InitInternal(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd);
// Recursively get the deepest first/last child of aRoot. This will return
// aRoot itself if it has no children.
@ -287,8 +272,7 @@ nsContentIterator::Init(nsIDOMRange* aDOMRange)
return NS_ERROR_INVALID_ARG;
}
return InitInternal(range->GetStartContainer(), range->StartOffset(),
range->GetEndContainer(), range->EndOffset());
return InitInternal(range->StartRef().AsRaw(), range->EndRef().AsRaw());
}
nsresult
@ -302,57 +286,67 @@ nsContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
return NS_ERROR_INVALID_ARG;
}
return InitInternal(aStartContainer, aStartOffset,
aEndContainer, aEndOffset);
return InitInternal(RawRangeBoundary(aStartContainer, aStartOffset),
RawRangeBoundary(aEndContainer, aEndOffset));
}
// XXX Argument names will be replaced in the following patch.
nsresult
nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset)
nsContentIterator::Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
mIsDone = false;
if (NS_WARN_IF(!nsRange::IsValidPoints(aStart.Container(), aStart.Offset(),
aEnd.Container(), aEnd.Offset()))) {
return NS_ERROR_INVALID_ARG;
}
return InitInternal(aStart, aEnd);
}
nsresult
nsContentIterator::InitInternal(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
// get common content parent
mCommonParent =
nsContentUtils::GetCommonAncestor(aStartContainer, aEndContainer);
nsContentUtils::GetCommonAncestor(aStart.Container(), aEnd.Container());
if (NS_WARN_IF(!mCommonParent)) {
return NS_ERROR_FAILURE;
}
bool startIsData = aStartContainer->IsNodeOfType(nsINode::eDATA_NODE);
bool startIsData = aStart.Container()->IsNodeOfType(nsINode::eDATA_NODE);
// short circuit when start node == end node
if (aStartContainer == aEndContainer) {
// Check to see if we have a collapsed range, if so, there is nothing to
// iterate over.
//
// XXX: CharacterDataNodes (text nodes) are currently an exception, since
// we always want to be able to iterate text nodes at the end points
// of a range.
// Check to see if we have a collapsed range, if so, there is nothing to
// iterate over.
//
// XXX: CharacterDataNodes (text nodes) are currently an exception, since
// we always want to be able to iterate text nodes at the end points
// of a range.
if (!startIsData && aStartOffset == aEndOffset) {
MakeEmpty();
return NS_OK;
}
if (!startIsData && aStart == aEnd) {
MakeEmpty();
return NS_OK;
}
if (startIsData) {
// It's a character data node.
mFirst = aStartContainer->AsContent();
mLast = mFirst;
mCurNode = mFirst;
// Handle ranges within a single character data node.
if (startIsData && aStart.Container() == aEnd.Container()) {
mFirst = aStart.Container()->AsContent();
mLast = mFirst;
mCurNode = mFirst;
return NS_OK;
}
return NS_OK;
}
// Find first node in range.
nsIContent* cChild = nullptr;
// Valid start indices are 0 <= aStartOffset <= childCount. That means if
// start node has no children, only offset 0 is valid.
if (!startIsData && aStartOffset < aStartContainer->GetChildCount()) {
cChild = aStartContainer->GetChildAt(aStartOffset);
NS_WARNING_ASSERTION(cChild, "GetChildAt returned null");
// Try to get the child at our starting point. This might return null if
// aStart is immediately after the last node in aStart.Container().
if (!startIsData) {
cChild = aStart.GetChildAtOffset();
}
if (!cChild) {
@ -368,33 +362,31 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
// is a non-container node (e.g. <br>), we don't skip the node in this
// case in order to address bug 1215798.
bool startIsContainer = true;
if (aStartContainer->IsHTMLElement()) {
nsIAtom* name = aStartContainer->NodeInfo()->NameAtom();
if (aStart.Container()->IsHTMLElement()) {
nsIAtom* name = aStart.Container()->NodeInfo()->NameAtom();
startIsContainer =
nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
}
if (!startIsData && (startIsContainer || aStartOffset)) {
mFirst = GetNextSibling(aStartContainer);
if (!startIsData && (startIsContainer || !aStart.IsStartOfContainer())) {
mFirst = GetNextSibling(aStart.Container());
NS_WARNING_ASSERTION(mFirst, "GetNextSibling returned null");
// Does mFirst node really intersect the range? The range could be
// 'degenerate', i.e., not collapsed but still contain no content.
if (mFirst &&
NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre,
aStartContainer, aStartOffset,
aEndContainer, aEndOffset))) {
NS_WARN_IF(!NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd))) {
mFirst = nullptr;
}
} else {
mFirst = aStartContainer->AsContent();
mFirst = aStart.Container()->AsContent();
}
} else {
// post-order
if (NS_WARN_IF(!aStartContainer->IsContent())) {
if (NS_WARN_IF(!aStart.Container()->IsContent())) {
// What else can we do?
mFirst = nullptr;
} else {
mFirst = aStartContainer->AsContent();
mFirst = aStart.Container()->AsContent();
}
}
} else {
@ -408,9 +400,7 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
// Does mFirst node really intersect the range? The range could be
// 'degenerate', i.e., not collapsed but still contain no content.
if (mFirst &&
!NodeIsInTraversalRange(mFirst, mPre, aStartContainer, aStartOffset,
aEndContainer, aEndOffset)) {
if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd)) {
mFirst = nullptr;
}
}
@ -419,11 +409,11 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
// Find last node in range.
bool endIsData = aEndContainer->IsNodeOfType(nsINode::eDATA_NODE);
bool endIsData = aEnd.Container()->IsNodeOfType(nsINode::eDATA_NODE);
if (endIsData || !aEndContainer->HasChildren() || !aEndOffset) {
if (endIsData || !aEnd.Container()->HasChildren() || aEnd.IsStartOfContainer()) {
if (mPre) {
if (NS_WARN_IF(!aEndContainer->IsContent())) {
if (NS_WARN_IF(!aEnd.Container()->IsContent())) {
// Not much else to do here...
mLast = nullptr;
} else {
@ -431,22 +421,22 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
// the last element should be the previous node (i.e., shouldn't
// include the end node in the range).
bool endIsContainer = true;
if (aEndContainer->IsHTMLElement()) {
nsIAtom* name = aEndContainer->NodeInfo()->NameAtom();
if (aEnd.Container()->IsHTMLElement()) {
nsIAtom* name = aEnd.Container()->NodeInfo()->NameAtom();
endIsContainer =
nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
}
if (!endIsData && !endIsContainer && !aEndOffset) {
mLast = PrevNode(aEndContainer);
if (!endIsData && !endIsContainer && aEnd.IsStartOfContainer()) {
mLast = PrevNode(aEnd.Container());
NS_WARNING_ASSERTION(mLast, "PrevNode returned null");
if (mLast && mLast != mFirst &&
NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
mFirst, 0,
aEndContainer, aEndOffset))) {
RawRangeBoundary(mFirst, 0),
aEnd))) {
mLast = nullptr;
}
} else {
mLast = aEndContainer->AsContent();
mLast = aEnd.Container()->AsContent();
}
}
} else {
@ -456,22 +446,18 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
// cdata node, should we set mLast to the prev sibling?
if (!endIsData) {
mLast = GetPrevSibling(aEndContainer);
mLast = GetPrevSibling(aEnd.Container());
NS_WARNING_ASSERTION(mLast, "GetPrevSibling returned null");
if (!NodeIsInTraversalRange(mLast, mPre,
aStartContainer, aStartOffset,
aEndContainer, aEndOffset)) {
if (!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd)) {
mLast = nullptr;
}
} else {
mLast = aEndContainer->AsContent();
mLast = aEnd.Container()->AsContent();
}
}
} else {
int32_t indx = aEndOffset;
cChild = aEndContainer->GetChildAt(--indx);
cChild = aEnd.Ref();
if (NS_WARN_IF(!cChild)) {
// No child at offset!
@ -483,9 +469,7 @@ nsContentIterator::InitInternal(nsINode* aStartContainer, uint32_t aStartOffset,
mLast = GetDeepLastChild(cChild);
NS_WARNING_ASSERTION(mLast, "GetDeepLastChild returned null");
if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre,
aStartContainer, aStartOffset,
aEndContainer, aEndOffset))) {
if (NS_WARN_IF(!NodeIsInTraversalRange(mLast, mPre, aStart, aEnd))) {
mLast = nullptr;
}
} else {
@ -784,51 +768,42 @@ nsContentIterator::PositionAt(nsINode* aCurNode)
// Check to see if the node falls within the traversal range.
nsINode* firstNode = mFirst;
nsINode* lastNode = mLast;
int32_t firstOffset = 0, lastOffset = 0;
RawRangeBoundary first(mFirst, 0);
RawRangeBoundary last(mLast, 0);
if (firstNode && lastNode) {
if (mFirst && mLast) {
if (mPre) {
firstNode = NodeToParentOffset(mFirst, &firstOffset);
NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
// In pre we want to record the point immediately before mFirst, which is
// the point immediately after mFirst's previous sibling.
first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
if (lastNode->GetChildCount()) {
lastOffset = 0;
} else {
lastNode = NodeToParentOffset(mLast, &lastOffset);
NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
++lastOffset;
// If mLast has no children, then we want to make sure to include it.
if (!mLast->HasChildren()) {
last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
}
} else {
uint32_t numChildren = firstNode->GetChildCount();
if (numChildren) {
firstOffset = numChildren;
NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
// If the first node has any children, we want to be immediately after the
// last. Otherwise we want to be immediately before mFirst.
if (mFirst->HasChildren()) {
first.SetAfterRef(mFirst, mFirst->GetLastChild());
} else {
firstNode = NodeToParentOffset(mFirst, &firstOffset);
NS_WARNING_ASSERTION(firstNode, "NodeToParentOffset returned null");
NS_WARNING_ASSERTION(firstOffset >= 0, "bad firstOffset");
first.SetAfterRef(mFirst->GetParentNode(), mFirst->GetPreviousSibling());
}
lastNode = NodeToParentOffset(mLast, &lastOffset);
NS_WARNING_ASSERTION(lastNode, "NodeToParentOffset returned null");
NS_WARNING_ASSERTION(lastOffset >= 0, "bad lastOffset");
++lastOffset;
// Set the last point immediately after the final node.
last.SetAfterRef(mLast->GetParentNode(), mLast->AsContent());
}
}
NS_WARNING_ASSERTION(first.IsSetAndValid(), "first is not valid");
NS_WARNING_ASSERTION(last.IsSetAndValid(), "last is not valid");
// The end positions are always in the range even if it has no parent. We
// need to allow that or 'iter->Init(root)' would assert in Last() or First()
// for example, bug 327694.
if (mFirst != mCurNode && mLast != mCurNode &&
(NS_WARN_IF(!firstNode) || NS_WARN_IF(!lastNode) ||
NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre,
firstNode, firstOffset,
lastNode, lastOffset)))) {
(NS_WARN_IF(!first.IsSet()) || NS_WARN_IF(!last.IsSet()) ||
NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mPre, first, last)))) {
mIsDone = true;
return NS_ERROR_FAILURE;
}
@ -886,6 +861,9 @@ public:
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset) override;
virtual nsresult Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd) override;
virtual void Next() override;
virtual void Prev() override;
@ -987,22 +965,26 @@ nsContentSubtreeIterator::Init(nsIDOMRange* aRange)
nsresult
nsContentSubtreeIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset)
{
return Init(RawRangeBoundary(aStartContainer, aStartOffset),
RawRangeBoundary(aEndContainer, aEndOffset));
}
nsresult
nsContentSubtreeIterator::Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
mIsDone = false;
RefPtr<nsRange> range;
nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
aEndContainer, aEndOffset,
getter_AddRefs(range));
nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
NS_WARN_IF(!range->IsPositioned())) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(range->GetStartContainer() != aStartContainer) ||
NS_WARN_IF(range->GetEndContainer() != aEndContainer) ||
NS_WARN_IF(range->StartOffset() != aStartOffset) ||
NS_WARN_IF(range->EndOffset() != aEndOffset)) {
if (NS_WARN_IF(range->StartRef() != aStart) ||
NS_WARN_IF(range->EndRef() != aEnd)) {
return NS_ERROR_UNEXPECTED;
}

Просмотреть файл

@ -9,6 +9,7 @@
#include "nsISupports.h"
#include "nsCOMPtr.h"
#include "mozilla/RangeBoundary.h"
class nsINode;
class nsIDOMRange;
@ -39,6 +40,13 @@ public:
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset) = 0;
/* Initializes an iterator for the subtree between aStart and aEnd.
Callers should guarantee that the start point and end point are in
document order.
*/
virtual nsresult Init(const mozilla::RawRangeBoundary& aStart,
const mozilla::RawRangeBoundary& aEnd) = 0;
/** First will reset the list.
*/
virtual void First() = 0;

Просмотреть файл

@ -99,20 +99,24 @@ nsFilteredContentIterator::Init(nsIDOMRange* aRange)
nsresult
nsFilteredContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset)
{
return Init(RawRangeBoundary(aStartContainer, aStartOffset),
RawRangeBoundary(aEndContainer, aEndOffset));
}
nsresult
nsFilteredContentIterator::Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd)
{
RefPtr<nsRange> range;
nsresult rv = nsRange::CreateRange(aStartContainer, aStartOffset,
aEndContainer, aEndOffset,
getter_AddRefs(range));
nsresult rv = nsRange::CreateRange(aStart, aEnd, getter_AddRefs(range));
if (NS_WARN_IF(NS_FAILED(rv)) || NS_WARN_IF(!range) ||
NS_WARN_IF(!range->IsPositioned())) {
return NS_ERROR_INVALID_ARG;
}
MOZ_ASSERT(range->GetStartContainer() == aStartContainer);
MOZ_ASSERT(range->GetEndContainer() == aEndContainer);
MOZ_ASSERT(range->StartOffset() == aStartOffset);
MOZ_ASSERT(range->EndOffset() == aEndOffset);
MOZ_ASSERT(range->StartRef() == aStart);
MOZ_ASSERT(range->EndRef() == aEnd);
mRange = Move(range);

Просмотреть файл

@ -34,6 +34,8 @@ public:
virtual nsresult Init(nsIDOMRange* aRange) override;
virtual nsresult Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset) override;
virtual nsresult Init(const mozilla::RawRangeBoundary& aStart,
const mozilla::RawRangeBoundary& aEnd) override;
virtual void First() override;
virtual void Last() override;
virtual void Next() override;

Просмотреть файл

@ -107,6 +107,12 @@ public:
NS_NOTREACHED("internal error");
return NS_ERROR_NOT_IMPLEMENTED;
}
virtual nsresult Init(const RawRangeBoundary& aStart,
const RawRangeBoundary& aEnd) override
{
NS_NOTREACHED("internal error");
return NS_ERROR_NOT_IMPLEMENTED;
}
// Not a range because one of the endpoints may be anonymous.
nsresult Init(nsIDOMNode* aStartNode, int32_t aStartOffset,
nsIDOMNode* aEndNode, int32_t aEndOffset);