📄 dom_position.cpp
字号:
NodeImpl *fromRootEditableElement = node()->rootEditableElement();
bool atEndOfLine = isLastVisiblePositionOnLine(VisiblePosition(*this, affinity));
bool rendered = inRenderedContent();
Position currentPos = *this;
while (!currentPos.atEnd()) {
currentPos = currentPos.next();
if (currentPos.node()->rootEditableElement() != fromRootEditableElement)
return *this;
if (atEndOfLine || !rendered) {
if (currentPos.inRenderedContent())
return currentPos;
} else if (rendersInDifferentPosition(currentPos))
return currentPos;
}
return *this;
}
// upstream() and downstream() want to return positions that are either in a
// text node or at just before a non-text node. This method checks for that.
static bool isStreamer (Position pos)
{
NodeImpl *currentNode = pos.node();
if (!currentNode)
return true;
if (currentNode->isAtomicNode())
return true;
return (pos.offset() == 0);
}
// AFAIK no one has a clear, complete definition for this method and how it is used.
// Here is what I have come to understand from re-working the code after fixing PositionIterator
// for <rdar://problem/4103339>. See also Ken's comments in the header. Fundamentally, upstream()
// scans backward in the DOM starting at "this" to return a visible DOM position that is either in
// a text node, or just after a replaced or BR element (btw downstream() also considers empty blocks).
// If "stayInBlock" is specified, the search stops when it would have entered into a part of the DOM
// with a different enclosing block, including a nested one. Otherwise, the search stops at the start
// of the entire DOM tree. If "stayInBlock" stops the search, this method returns the highest previous
// position that is either in an atomic node (i.e. text) or is the end of a non-atomic node
// (_regardless_ of visibility). If the end-of-DOM stopped the search, this method returns the
// highest previous visible node that is either in an atomic node (i.e. text) or is the end of a
// non-atomic node.
Position Position::upstream(EStayInBlock stayInBlock) const
{
// start at equivalent deep position
Position start = equivalentDeepPosition();
NodeImpl *startNode = start.node();
if (!startNode)
return Position();
// iterate backward from there, looking for a qualified position
NodeImpl *block = stayInBlock ? startNode->enclosingBlockFlowOrTableElement() : 0;
Position lastVisible = *this;
Position lastStreamer = *this;
Position currentPos = start;
for (; !currentPos.atStart(); currentPos = currentPos.previous()) {
NodeImpl *currentNode = currentPos.node();
int currentOffset = currentPos.offset();
// limit traversal to block or table enclosing the original element
// NOTE: This includes not going into nested blocks
if (stayInBlock && block != currentNode->enclosingBlockFlowOrTableElement())
return lastStreamer;
// track last streamer position (regardless of visibility)
if (isStreamer(currentPos))
lastStreamer = currentPos;
// skip position in unrendered or invisible node
RenderObject *renderer = currentNode->renderer();
if (!renderer || renderer->style()->visibility() != VISIBLE)
continue;
// track last visible streamer position
if (isStreamer(currentPos))
lastVisible = currentPos;
// return position after replaced or BR elements
if (renderer->isReplaced() || renderer->isBR()) {
if (currentOffset >= renderer->caretMaxOffset())
return Position(currentNode, renderer->caretMaxOffset());
// we could not have iterated here because we would have returned
// this node, caretMaxOffset, so we must have started here
// assert(currentPos == start);
continue;
}
// return current position if it is in rendered text
if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
if (currentNode != startNode) {
assert(currentOffset >= renderer->caretMaxOffset());
return Position(currentNode, renderer->caretMaxOffset());
}
if (currentOffset < 0)
continue;
uint textOffset = currentOffset;
RenderText *textRenderer = static_cast<RenderText *>(renderer);
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset > box->start() && textOffset <= box->start() + box->len())
return currentPos;
if (box != textRenderer->lastTextBox() &&
!box->nextOnLine() &&
textOffset == box->start() + box->len() + 1)
return currentPos;
}
}
}
return lastVisible;
}
// AFAIK no one has a clear, complete definition for this method and how it is used.
// Here is what I have come to understand from re-working the code after fixing PositionIterator
// for <rdar://problem/4103339>. See also Ken's comments in the header. Fundamentally, downstream()
// scans forward in the DOM starting at "this" to return the first visible DOM position that is
// either in a text node, or just before a replaced, BR element, or empty block flow element (i.e.
// non-text nodes with no children). If "stayInBlock" is specified, the search stops when it would
// have entered into a part of the DOM with a different enclosing block, including a nested one.
// Otherwise, the search stops at the end of the entire DOM tree. If "stayInBlock" stops the search,
// this method returns the first previous position that is either in an atomic node (i.e. text) or is
// at offset 0 (_regardless_ of visibility). If the end-of-DOM stopped the search, this method returns
// the first previous visible node that is either in an atomic node (i.e. text) or is at offset 0.
Position Position::downstream(EStayInBlock stayInBlock) const
{
// start at equivalent deep position
Position start = equivalentDeepPosition();
NodeImpl *startNode = start.node();
if (!startNode)
return Position();
// iterate forward from there, looking for a qualified position
NodeImpl *block = stayInBlock ? startNode->enclosingBlockFlowOrTableElement() : 0;
Position lastVisible = *this;
Position lastStreamer = *this;
Position currentPos = start;
for (; !currentPos.atEnd(); currentPos = currentPos.next()) {
NodeImpl *currentNode = currentPos.node();
int currentOffset = currentPos.offset();
// stop before going above the body, up into the head
// return the last visible streamer position
if (currentNode->id() == ID_BODY && currentOffset >= (int) currentNode->childNodeCount())
break;
// limit traversal to block or table enclosing the original element
// return the last streamer position regardless of visibility
// NOTE: This includes not going into nested blocks
if (stayInBlock && block != currentNode->enclosingBlockFlowOrTableElement())
return lastStreamer;
// track last streamer position (regardless of visibility)
if (isStreamer(currentPos))
lastStreamer = currentPos;
// skip position in unrendered or invisible node
RenderObject *renderer = currentNode->renderer();
if (!renderer || renderer->style()->visibility() != VISIBLE)
continue;
// track last visible streamer position
if (isStreamer(currentPos))
lastVisible = currentPos;
// if now at a offset 0 of a rendered block flow element...
// - return current position if the element has no children (i.e. is a leaf)
// - return child node, offset 0, if the first visible child is not a block flow element
// - otherwise, skip this position (first visible child is a block, and we will
// get there eventually via the iterator)
if ((currentNode != startNode && renderer->isBlockFlow()) && (currentOffset == 0)) {
if (!currentNode->firstChild())
return currentPos;
for (NodeImpl *child = currentNode->firstChild(); child; child = child->nextSibling()) {
RenderObject *r = child->renderer();
if (r && r->style()->visibility() == VISIBLE) {
if (r->isBlockFlow())
break; // break causes continue code below to run.
return Position(child, 0);
}
}
continue;
}
// return position before replaced or BR elements
if (renderer->isReplaced() || renderer->isBR()) {
if (currentOffset <= renderer->caretMinOffset())
return Position(currentNode, renderer->caretMinOffset());
// we could not have iterated here because we would have returned
// this node, offset 0, so we must have started here
// assert(currentPos == start);
continue;
}
// return current position if it is in rendered text
if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) {
if (currentNode != startNode) {
assert(currentOffset == 0);
return Position(currentNode, renderer->caretMinOffset());
}
if (currentOffset < 0)
continue;
uint textOffset = currentOffset;
RenderText *textRenderer = static_cast<RenderText *>(renderer);
for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
if (textOffset >= box->start() && textOffset <= box->end())
return currentPos;
if (box != textRenderer->lastTextBox() &&
!box->nextOnLine() &&
textOffset == box->start() + box->len()) {
return currentPos;
}
}
}
}
return lastVisible;
}
Position Position::equivalentRangeCompliantPosition() const
{
if (isNull())
return Position();
// Make sure that 0 <= constrainedOffset <= num kids, otherwise using this Position
// in DOM calls can result in exceptions.
long maxOffset = node()->isTextNode() ? static_cast<TextImpl *>(node())->length(): node()->childNodeCount();
long constrainedOffset = offset() <= 0 ? 0 : kMin(maxOffset, offset());
if (!node()->parentNode())
return Position(node(), constrainedOffset);
RenderObject *renderer = node()->renderer();
if (!renderer)
return Position(node(), constrainedOffset);
if (!renderer->isReplaced() && !renderer->isBR())
return Position(node(), constrainedOffset);
long o = offset();
const NodeImpl *n = node();
while ((n = n->previousSibling()))
o++;
// Make sure that 0 <= constrainedOffset <= num kids, as above.
NodeImpl *parent = node()->parentNode();
maxOffset = parent->isTextNode() ? static_cast<TextImpl *>(parent)->length(): parent->childNodeCount();
constrainedOffset = o <= 0 ? 0 : kMin(maxOffset, o);
return Position(parent, constrainedOffset);
}
Position Position::equivalentDeepPosition() const
{
if (isNull() || node()->isAtomicNode())
return *this;
NodeImpl *child = 0;
Position pos(*this);
if (offset() >= (int)node()->childNodeCount()) {
child = node()->lastChild();
pos = Position(child, child->caretMaxOffset());
ASSERT(child);
while (!child->isAtomicNode() && pos.node()->hasChildNodes()) {
child = pos.node()->lastChild();
ASSERT(child);
pos = Position(child, child->caretMaxOffset());
}
}
else {
child = node()->childNode(offset());
ASSERT(child);
pos = Position(child, 0);
while (!child->isAtomicNode() && pos.node()->hasChildNodes()) {
child = pos.node()->firstChild();
ASSERT(child);
pos = Position(child, 0);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -