--- a/dom/base/nsContentIterator.cpp
+++ b/dom/base/nsContentIterator.cpp
@@ -13,90 +13,72 @@
#include "nsCOMPtr.h"
#include "nsTArray.h"
#include "nsContentUtils.h"
#include "nsINode.h"
#include "nsCycleCollectionParticipant.h"
#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;
}
}
nsINode* parent = aNode->GetParentNode();
if (!parent) {
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;
}
/*
* A simple iterator class for traversing the content in "close tag" order
*/
class nsContentIterator : public nsIContentIterator
@@ -111,16 +93,19 @@ public:
virtual nsresult Init(nsINode* aRoot) override;
virtual nsresult Init(nsIDOMRange* aRange) override;
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;
virtual void Next() override;
virtual void Prev() override;
@@ -134,18 +119,18 @@ protected:
virtual ~nsContentIterator();
/**
* Callers must guarantee that:
* - Neither aStartContainer nor aEndContainer is nullptr.
* - 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.
nsINode* GetDeepFirstChild(nsINode* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
nsIContent* GetDeepFirstChild(nsIContent* aRoot,
nsTArray<int32_t>* aIndexes = nullptr);
nsINode* GetDeepLastChild(nsINode* aRoot,
@@ -318,217 +303,216 @@ nsContentIterator::Init(nsIDOMRange* aDO
return NS_ERROR_INVALID_ARG;
}
nsRange* range = static_cast<nsRange*>(aDOMRange);
if (NS_WARN_IF(!range->IsPositioned())) {
return NS_ERROR_INVALID_ARG;
}
- return InitInternal(range->GetStartContainer(), range->StartOffset(),
- range->GetEndContainer(), range->EndOffset());
+ return InitInternal(range->StartRef().AsRaw(), range->EndRef().AsRaw());
}
nsresult
nsContentIterator::Init(nsINode* aStartContainer, uint32_t aStartOffset,
nsINode* aEndContainer, uint32_t aEndOffset)
{
mIsDone = false;
if (NS_WARN_IF(!nsRange::IsValidPoints(aStartContainer, aStartOffset,
aEndContainer, aEndOffset))) {
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;
- DebugOnly<nsresult> rv = RebuildIndexStack();
- NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
- return NS_OK;
- }
+ DebugOnly<nsresult> rv = RebuildIndexStack();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "RebuildIndexStack failed");
+ 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) {
// No children (possibly a <br> or text node), or index is after last child.
if (mPre) {
// XXX: In the future, if start offset is after the last
// character in the cdata node, should we set mFirst to
// the next sibling?
// Normally we would skip the start node because the start node is outside
// of the range in pre mode. However, if aStartOffset == 0, and the node
// 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 {
if (mPre) {
mFirst = cChild;
} else {
// post-order
mFirst = GetDeepFirstChild(cChild);
NS_WARNING_ASSERTION(mFirst, "GetDeepFirstChild 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 &&
- !NodeIsInTraversalRange(mFirst, mPre, aStartContainer, aStartOffset,
- aEndContainer, aEndOffset)) {
+ if (mFirst && !NodeIsInTraversalRange(mFirst, mPre, aStart, aEnd)) {
mFirst = nullptr;
}
}
}
// 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 {
// If the end node is a non-container element and the end offset is 0,
// 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 {
// post-order
//
// XXX: In the future, if end offset is before the first character in the
// 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!
NS_NOTREACHED("nsContentIterator::nsContentIterator");
return NS_ERROR_FAILURE;
}
if (mPre) {
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 {
// post-order
mLast = cChild;
}
}
@@ -1062,61 +1046,52 @@ nsContentIterator::PositionAt(nsINode* a
// take an early out if this doesn't actually change the position
if (mCurNode == tempNode) {
mIsDone = false; // paranoia
return NS_OK;
}
// 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;
}
// We can be at ANY node in the sequence. Need to regenerate the array of
// indexes back to the root or common parent!
AutoTArray<nsINode*, 8> oldParentStack;
AutoTArray<int32_t, 8> newIndexes;
@@ -1240,16 +1215,19 @@ public:
virtual nsresult Init(nsINode* aRoot) override;
virtual nsresult Init(nsIDOMRange* aRange) override;
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;
virtual nsresult PositionAt(nsINode* aCurNode) override;
// Must override these because we don't do PositionAt
virtual void First() override;
@@ -1342,31 +1320,35 @@ nsContentSubtreeIterator::Init(nsIDOMRan
return InitWithRange();
}
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;
}
mRange = Move(range);
return InitWithRange();
}