/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// 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();
/////////////////////////////////////////////////////////////////////////// // NodeIsInTraversalRange: returns true if content is visited during // the traversal of the range in the specified mode. // staticbool NodeIsInTraversalRange(nsINode* aNode, bool aIsPreMode, const RawRangeBoundary& aStart, const RawRangeBoundary& aEnd) { if (NS_WARN_IF(!aStart.IsSet()) || NS_WARN_IF(!aEnd.IsSet()) ||
NS_WARN_IF(!aNode)) { returnfalse;
}
// If a leaf node contains an end point of the traversal range, it is // always in the traversal range. if (aNode == aStart.Container() || aNode == aEnd.Container()) { if (aNode->IsCharacterData()) { returntrue; // text node or something
} if (!aNode->HasChildren()) {
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"); returntrue;
}
}
if (aIsPreMode) { return ComparePreMode(aStart, aEnd, *aNode);
}
template <typename NodeType>
nsresult ContentIteratorBase<NodeType>::Initializer::Run() { // get common content parent
mIterator.mClosestCommonInclusiveAncestor =
nsContentUtils::GetClosestCommonInclusiveAncestor(mStart.Container(),
mEnd.Container()); if (NS_WARN_IF(!mIterator.mClosestCommonInclusiveAncestor)) { return NS_ERROR_FAILURE;
}
// 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 (IsCollapsedNonCharacterRange()) {
mIterator.SetEmpty(); return NS_OK;
}
// Try to get the child at our starting point. This might return null if // mStart is immediately after the last node in mStart.Container(). if (!mStartIsCharacterData) {
cChild = mStart.GetChildAtOffset();
}
if (!cChild) { // No children (possibly a <br> or text node), or index is after last child.
if (mIterator.mOrder == Order::Pre) { // 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 (mStart.Container()->IsHTMLElement()) {
nsAtom* name = mStart.Container()->NodeInfo()->NameAtom();
startIsContainer =
nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
} if (!mStartIsCharacterData &&
(startIsContainer || !mStart.IsStartOfContainer())) {
nsINode* const result =
ContentIteratorBase::GetNextSibling(mStart.Container());
NS_WARNING_ASSERTION(result, "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 (result &&
NS_WARN_IF(!NodeIsInTraversalRange(
result, mIterator.mOrder == Order::Pre, mStart, mEnd))) { return nullptr;
}
// post-order if (NS_WARN_IF(!mStart.Container()->IsContent())) { // What else can we do? return nullptr;
} return mStart.Container()->AsContent();
}
if (mIterator.mOrder == Order::Pre) { return cChild;
}
// post-order
nsINode* const result = ContentIteratorBase::GetDeepFirstChild(cChild);
NS_WARNING_ASSERTION(result, "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 (result && !NodeIsInTraversalRange(result, mIterator.mOrder == Order::Pre,
mStart, mEnd)) { return nullptr;
}
if (endIsCharacterData || !mEnd.Container()->HasChildren() ||
mEnd.IsStartOfContainer()) { if (mIterator.mOrder == Order::Pre) { if (NS_WARN_IF(!mEnd.Container()->IsContent())) { // Not much else to do here... return nullptr;
}
// 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 (mEnd.Container()->IsHTMLElement()) {
nsAtom* name = mEnd.Container()->NodeInfo()->NameAtom();
endIsContainer =
nsHTMLElement::IsContainer(nsHTMLTags::AtomTagToId(name));
} if (!endIsCharacterData && !endIsContainer && mEnd.IsStartOfContainer()) {
nsINode* const result = mIterator.PrevNode(mEnd.Container());
NS_WARNING_ASSERTION(result, "PrevNode returned null"); if (result && result != mIterator.mFirst &&
NS_WARN_IF(!NodeIsInTraversalRange(
result, mIterator.mOrder == Order::Pre,
RawRangeBoundary(mIterator.mFirst, 0u), mEnd))) { return nullptr;
}
return result;
}
return mEnd.Container()->AsContent();
}
// 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 (!endIsCharacterData) {
nsINode* const result =
ContentIteratorBase::GetPrevSibling(mEnd.Container());
NS_WARNING_ASSERTION(result, "GetPrevSibling returned null");
if (NS_WARN_IF(!cChild)) { // No child at offset!
MOZ_ASSERT_UNREACHABLE("ContentIterator::ContentIterator"); return Err(NS_ERROR_FAILURE);
}
if (mIterator.mOrder == Order::Pre) {
nsINode* const result = ContentIteratorBase::GetDeepLastChild(cChild);
NS_WARNING_ASSERTION(result, "GetDeepLastChild returned null");
if (ShadowRoot* shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot(
node, aAllowCrossShadowBoundary)) { // When finding the deepest child of node, if this node has a // web exposed shadow root, we use this shadow root to find the deepest // child. // If the first candidate should be a slotted content, // shadowRoot->GetFirstChild() should be able to return the <slot> element. // It's probably correct I think. Then it's up to the caller of this // iterator to decide whether to use the slot's assigned nodes or not.
MOZ_ASSERT(aAllowCrossShadowBoundary);
child = shadowRoot->GetFirstChild();
} else {
child = node->GetFirstChild();
}
while (child) {
node = child; if (ShadowRoot* shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot(
node, aAllowCrossShadowBoundary)) { // When finding the deepest child of node, if this node has a // web exposed shadow root, we use this shadow root to find the deepest // child. // If the first candidate should be a slotted content, // shadowRoot->GetFirstChild() should be able to return the <slot> // element. It's probably correct I think. Then it's up to the caller of // this iterator to decide whether to use the slot's assigned nodes or // not.
child = shadowRoot->GetFirstChild();
} else {
child = node->GetFirstChild();
}
}
ShadowRoot* shadowRoot =
ShadowDOMSelectionHelpers::GetShadowRoot(node, aAllowCrossShadowBoundary); // FIXME(sefeng): This doesn't work with slots / flattened tree. while (node->HasChildren() || (shadowRoot && shadowRoot->HasChildren())) { if (node->HasChildren()) {
node = node->GetLastChild();
} else {
MOZ_ASSERT(shadowRoot); // If this node doesn't have a child, but it's also a shadow host // that can be selected, we go into this shadow tree.
node = shadowRoot->GetLastChild();
}
shadowRoot = ShadowDOMSelectionHelpers::GetShadowRoot(
node, aAllowCrossShadowBoundary);
} return node;
}
// Get the next sibling, or parent's next sibling, or shadow host's next // sibling (when aAllowCrossShadowBoundary is true), or grandpa's next // sibling... // // static // template <typename NodeType>
nsIContent* ContentIteratorBase<NodeType>::GetNextSibling(
nsINode* aNode, bool aAllowCrossShadowBoundary) { if (NS_WARN_IF(!aNode)) { return nullptr;
}
if (nsIContent* next = aNode->GetNextSibling()) { return next;
}
if (aAllowCrossShadowBoundary) { // This is temporary solution. // For shadow root, instead of getting to the sibling of the parent // directly, we need to get into the light tree of the parent to handle // slotted contents. if (aNode->IsShadowRoot()) { if (nsIContent* child = parent->GetFirstChild()) { return child;
}
}
}
// if we are a Pre-order iterator, use pre-order if (mOrder == Order::Pre) { // if it has children then next node is first child if (node->HasChildren()) {
nsIContent* firstChild = node->GetFirstChild();
MOZ_ASSERT(firstChild);
return firstChild;
}
// else next sibling is next return ContentIteratorBase::GetNextSibling(node);
}
// post-order
nsINode* parent = node->GetParentNode(); if (NS_WARN_IF(!parent)) {
MOZ_ASSERT(parent, "The node is the root node but not the last node");
mCurNode = nullptr; return node;
}
if (nsIContent* sibling = node->GetNextSibling()) { // next node is sibling's "deep left" child return ContentIteratorBase::GetDeepFirstChild(sibling);
}
// if we are a Pre-order iterator, use pre-order if (mOrder == Order::Pre) {
nsINode* parent = node->GetParentNode(); if (NS_WARN_IF(!parent)) {
MOZ_ASSERT(parent, "The node is the root node but not the first node");
mCurNode = nullptr; return aNode;
}
nsIContent* sibling = node->GetPreviousSibling(); if (sibling) { return ContentIteratorBase::GetDeepLastChild(sibling);
}
return parent;
}
// post-order if (node->HasChildren()) { return node->GetLastChild();
}
// else prev sibling is previous return ContentIteratorBase::GetPrevSibling(node);
}
template <typename NodeType> void ContentIteratorBase<NodeType>::Last() { // Note that mLast can be nullptr if SetEmpty() is called in Init() // since at that time, Init() returns NS_OK. if (!mLast) {
MOZ_ASSERT(IsDone());
mCurNode = nullptr; return;
}
mozilla::DebugOnly<nsresult> rv = PositionAt(mLast);
NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to position iterator!");
}
if (mCurNode == mFirst) {
mCurNode = nullptr; return;
}
mCurNode = PrevNode(mCurNode);
}
// Keeping arrays of indexes for the stack of nodes makes PositionAt // interesting...
NS_INSTANTIATE_CONTENT_ITER_BASE_METHOD(nsresult, PositionAt, nsINode*);
if (mFirst && mLast) { if (mOrder == Order::Pre) { // In pre we want to record the point immediately before mFirst, which is // the point immediately after mFirst's previous sibling.
first = {mFirst->GetParentNode(), mFirst->GetPreviousSibling()};
// If mLast has no children, then we want to make sure to include it. if (!mLast->HasChildren()) {
last = {mLast->GetParentNode(), mLast->AsContent()};
}
} else { // 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 = {mFirst, mFirst->GetLastChild()};
} else {
first = {mFirst->GetParentNode(), mFirst->GetPreviousSibling()};
}
// Set the last point immediately after the final node.
last = {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(!first.IsSet()) || NS_WARN_IF(!last.IsSet()) ||
NS_WARN_IF(!NodeIsInTraversalRange(mCurNode, mOrder == Order::Pre, first,
last)))) {
mCurNode = nullptr; return NS_ERROR_FAILURE;
}
void ContentSubtreeIterator::CacheInclusiveAncestorsOfEndContainer() {
mInclusiveAncestorsOfEndContainer.Clear();
nsINode* const endContainer = ShadowDOMSelectionHelpers::GetEndContainer(
mRange, IterAllowCrossShadowBoundary());
nsIContent* endNode =
endContainer->IsContent() ? endContainer->AsContent() : nullptr; while (endNode) {
mInclusiveAncestorsOfEndContainer.AppendElement(endNode); // Cross the boundary for contents in shadow tree.
nsINode* parent = ShadowDOMSelectionHelpers::GetParentNode(
*endNode, IterAllowCrossShadowBoundary()); if (!parent || !parent->IsContent()) { break;
}
endNode = parent->AsContent();
}
}
nsIContent* ContentSubtreeIterator::DetermineCandidateForFirstContent() const {
nsINode* startContainer = ShadowDOMSelectionHelpers::GetStartContainer(
mRange, IterAllowCrossShadowBoundary());
nsIContent* firstCandidate = nullptr; // find first node in range
nsINode* node = nullptr; if (!startContainer->GetChildCount()) { // no children, start at the node itself
node = startContainer;
} else {
nsIContent* child =
IterAllowCrossShadowBoundary()
? mRange->GetMayCrossShadowBoundaryChildAtStartOffset()
: mRange->GetChildAtStartOffset();
MOZ_ASSERT(child == startContainer->GetChildAt_Deprecated(
ShadowDOMSelectionHelpers::StartOffset(
mRange, IterAllowCrossShadowBoundary()))); if (!child) { // offset after last child
node = startContainer;
} else {
firstCandidate = child;
}
}
if (!firstCandidate) { // then firstCandidate is next node after node
firstCandidate = ContentIteratorBase::GetNextSibling(
node, IterAllowCrossShadowBoundary());
}
if (firstCandidate) {
firstCandidate = ContentIteratorBase::GetDeepFirstChild(
firstCandidate, IterAllowCrossShadowBoundary());
}
// confirm that this first possible contained node is indeed contained. Else // we have a range that does not fully contain any node. const Maybe<bool> isNodeContainedInRange =
RangeUtils::IsNodeContainedInRange(*firstCandidate, mRange);
MOZ_ALWAYS_TRUE(isNodeContainedInRange); if (!isNodeContainedInRange.value()) { return nullptr;
}
// cool, we have the first node in the range. Now we walk up its ancestors // to find the most senior that is still in the range. That's the real first // node. return GetTopAncestorInRange(firstCandidate);
}
nsIContent* ContentSubtreeIterator::DetermineCandidateForLastContent() const {
nsIContent* lastCandidate{nullptr};
nsINode* endContainer = ShadowDOMSelectionHelpers::GetEndContainer(
mRange, IterAllowCrossShadowBoundary()); // now to find the last node
int32_t offset = ShadowDOMSelectionHelpers::EndOffset(
mRange, IterAllowCrossShadowBoundary());
nsINode* node = nullptr; if (offset > numChildren) { // Can happen for text nodes
offset = numChildren;
} if (!offset || !numChildren) {
node = endContainer;
} else {
lastCandidate = IterAllowCrossShadowBoundary()
? mRange->MayCrossShadowBoundaryEndRef().Ref()
: mRange->EndRef().Ref();
MOZ_ASSERT(lastCandidate == endContainer->GetChildAt_Deprecated(--offset));
NS_ASSERTION(lastCandidate, "tree traversal trouble in ContentSubtreeIterator::Init");
}
if (!lastCandidate) { // then lastCandidate is prev node before node
lastCandidate = ContentIteratorBase::GetPrevSibling(
node, IterAllowCrossShadowBoundary());
}
if (lastCandidate) {
lastCandidate = ContentIteratorBase::GetDeepLastChild(
lastCandidate, IterAllowCrossShadowBoundary());
}
// get the start node and offset, convert to nsINode
mClosestCommonInclusiveAncestor =
mRange->GetClosestCommonInclusiveAncestor(mAllowCrossShadowBoundary);
// cool, we have the last node in the range. Now we walk up its ancestors to // find the most senior that is still in the range. That's the real first // node. return GetTopAncestorInRange(lastCandidate);
}
/**************************************************************** * ContentSubtreeIterator overrides of ContentIterator routines
****************************************************************/
// we can't call PositionAt in a subtree iterator... void ContentSubtreeIterator::First() { mCurNode = mFirst; }
// we can't call PositionAt in a subtree iterator... void ContentSubtreeIterator::Last() { mCurNode = mLast; }
void ContentSubtreeIterator::Next() { if (IsDone()) { return;
}
if (mCurNode == mLast) {
mCurNode = nullptr; return;
}
NS_ASSERTION(nextNode, "No next sibling!?! This could mean deadlock!");
int32_t i = mInclusiveAncestorsOfEndContainer.IndexOf(nextNode); while (i != -1) { // as long as we are finding ancestors of the endpoint of the range, // dive down into their children
ShadowRoot* root = ShadowDOMSelectionHelpers::GetShadowRoot(
nextNode, IterAllowCrossShadowBoundary()); if (!root) {
nextNode = nextNode->GetFirstChild();
} else { // If IterAllowCrossShadowBoundary() returns true, it means we should // use shadow-including order for this iterator, that means the shadow // root should always be iterated.
nextNode = IterAllowCrossShadowBoundary() ? root->GetFirstChild()
: nextNode->GetFirstChild();
}
NS_ASSERTION(nextNode, "Iterator error, expected a child node!");
// should be impossible to get a null pointer. If we went all the way // down the child chain to the bottom without finding an interior node, // then the previous node should have been the last, which was // was tested at top of routine.
i = mInclusiveAncestorsOfEndContainer.IndexOf(nextNode);
}
mCurNode = nextNode;
}
void ContentSubtreeIterator::Prev() { // Prev should be optimized to use the mStartNodes, just as Next // uses mInclusiveAncestorsOfEndContainer. if (IsDone()) { return;
}
if (mCurNode == mFirst) {
mCurNode = nullptr; return;
}
// If any of these function calls return null, so will all succeeding ones, // so mCurNode will wind up set to null.
nsINode* prevNode = ContentIteratorBase::GetDeepFirstChild(mCurNode);
// aNode has a parent, so it must be content.
nsIContent* content = aNode->AsContent();
// sanity check: aNode is itself in the range
Maybe<bool> isNodeContainedInRange =
RangeUtils::IsNodeContainedInRange(*aNode, mRange);
NS_ASSERTION(isNodeContainedInRange && isNodeContainedInRange.value(), "aNode isn't in mRange, or something else weird happened"); if (!isNodeContainedInRange || !isNodeContainedInRange.value()) { return nullptr;
}
// content always has a parent. If its parent is the root, however -- // i.e., either it's not content, or it is content but its own parent is // null -- then we're finished, since we don't go up to the root. // // Caveat: If iteration crossing shadow boundary is allowed // and the root is a shadow root, we keep going up to the // shadow host and continue. // // We have to special-case this because CompareNodeToRange treats the root // node differently -- see bug 765205. if (!parent || !ShadowDOMSelectionHelpers::GetParentNode(
*parent, IterAllowCrossShadowBoundary())) { return content;
}
isNodeContainedInRange =
RangeUtils::IsNodeContainedInRange(*parent, mRange);
MOZ_ALWAYS_TRUE(isNodeContainedInRange); if (!isNodeContainedInRange.value()) { if (IterAllowCrossShadowBoundary() && content->IsShadowRoot()) {
MOZ_ASSERT(parent->GetShadowRoot() == content); // host element is not in range, the last content in tree // should be the ancestor.
MOZ_ASSERT(lastContentInShadowTree); return lastContentInShadowTree;
} return content;
}
// When we cross the boundary, we keep a reference to the // last content that is in tree, because if we later // find the shadow host element is not in the range, that means // the last content in the tree should be top ancestor in range. // // Using shadow root doesn't make sense here because it doesn't // represent a actual content. if (IterAllowCrossShadowBoundary() && parent->IsShadowRoot()) {
lastContentInShadowTree = content;
}
content = parent->AsContent();
}
MOZ_CRASH("This should only be possible if aNode was null");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.