Position Position::upstream(EStayInBlock stayInBlock) const { Position start = equivalentDeepPosition(); NodeImpl *startNode = start.node(); if (!startNode) return Position(); NodeImpl *block = startNode->enclosingBlockFlowOrTableElement(); Position lastVisible; PositionIterator it(start); for (; !it.atStart(); it.previous()) { NodeImpl *currentNode = it.current().node(); if (stayInBlock) { NodeImpl *currentBlock = currentNode->enclosingBlockFlowOrTableElement(); if (block != currentBlock) return it.next(); } RenderObject *renderer = currentNode->renderer(); if (!renderer) continue; if (renderer->style()->visibility() != VISIBLE) continue; lastVisible = it.current(); if (renderer->isReplaced() || renderer->isBR()) { if (it.current().offset() >= renderer->caretMaxOffset()) return Position(currentNode, renderer->caretMaxOffset()); else continue; } if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) { if (currentNode != startNode) return Position(currentNode, renderer->caretMaxOffset()); if (it.current().offset() < 0) continue; uint textOffset = it.current().offset(); 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 it.current(); else if (box != textRenderer->lastTextBox() && !box->nextOnLine() && textOffset == box->start() + box->len() + 1) return it.current(); } } } return lastVisible.isNotNull() ? lastVisible : *this; }
Position Position::downstream(EStayInBlock stayInBlock) const { Position start = equivalentDeepPosition(); NodeImpl *startNode = start.node(); if (!startNode) return Position(); NodeImpl *block = startNode->enclosingBlockFlowOrTableElement(); Position lastVisible; PositionIterator it(start); for (; !it.atEnd(); it.next()) { NodeImpl *currentNode = it.current().node(); if (stayInBlock) { NodeImpl *currentBlock = currentNode->enclosingBlockFlowOrTableElement(); if (block != currentBlock) return it.previous(); } RenderObject *renderer = currentNode->renderer(); if (!renderer) continue; if (renderer->style()->visibility() != VISIBLE) continue; lastVisible = it.current(); if (currentNode != startNode && renderer->isBlockFlow()) { if (it.current().offset() == 0) { // If no first child, or first visible child is a not a block, return; otherwise continue. if (!currentNode->firstChild()) return Position(currentNode, 0); 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. else return Position(child, 0); } } continue; } } if (renderer->isReplaced() || renderer->isBR()) { if (it.current().offset() <= renderer->caretMinOffset()) return Position(currentNode, renderer->caretMinOffset()); else continue; } if (renderer->isText() && static_cast<RenderText *>(renderer)->firstTextBox()) { if (currentNode != start.node()) return Position(currentNode, renderer->caretMinOffset()); if (it.current().offset() < 0) continue; uint textOffset = it.current().offset(); RenderText *textRenderer = static_cast<RenderText *>(renderer); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (textOffset >= box->start() && textOffset <= box->end()) return it.current(); else if (box != textRenderer->lastTextBox() && !box->nextOnLine() && textOffset == box->start() + box->len()) return it.current(); } } } return lastVisible.isNotNull() ? lastVisible : *this; }
// P.downstream() returns the end of the range of positions that map to the same VisiblePosition as P. Position Position::downstream() const { Node* startNode = node(); if (!startNode) return Position(); // iterate forward from there, looking for a qualified position Node* block = enclosingBlock(startNode); PositionIterator lastVisible = *this; PositionIterator currentPos = lastVisible; Node* originalRoot = node()->rootEditableElement(); for (; !currentPos.atEnd(); currentPos.increment()) { Node* currentNode = currentPos.node(); if (currentNode->rootEditableElement() != originalRoot) break; // stop before going above the body, up into the head // return the last visible streamer position if (currentNode->hasTagName(bodyTag) && currentPos.atEndOfNode()) break; // Do not enter a new enclosing block flow or table element, and don't leave the original one. if (block != enclosingBlock(currentNode)) return lastVisible; // 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 before brs, tables, and nodes which have content that can be ignored. if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) { if (currentPos.offsetInLeafNode() <= renderer->caretMinOffset()) return Position(currentNode, renderer->caretMinOffset()); continue; } // return current position if it is in rendered text if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { if (currentNode != startNode) { ASSERT(currentPos.atStartOfNode()); return Position(currentNode, renderer->caretMinOffset()); } unsigned textOffset = currentPos.offsetInLeafNode(); 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; }
// p.upstream() returns the start of the range of positions that map to the same VisiblePosition as P. Position Position::upstream() const { Node* startNode = node(); if (!startNode) return Position(); // iterate backward from there, looking for a qualified position Node* block = enclosingBlock(startNode); PositionIterator lastVisible = *this; PositionIterator currentPos = lastVisible; Node* originalRoot = node()->rootEditableElement(); for (; !currentPos.atStart(); currentPos.decrement()) { Node* currentNode = currentPos.node(); if (currentNode->rootEditableElement() != originalRoot) break; // Don't enter a new enclosing block flow or table element. There is code below that // terminates early if we're about to leave an enclosing block flow or table element. if (block != enclosingBlock(currentNode)) return lastVisible; // 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; // Don't leave a block flow or table element. We could rely on code above to terminate and // return lastVisible on the next iteration, but we terminate early. if (currentNode == enclosingBlock(currentNode) && currentPos.atStartOfNode()) return lastVisible; // Return position after brs, tables, and nodes which have content that can be ignored. if (editingIgnoresContent(currentNode) || renderer->isBR() || isTableElement(currentNode)) { if (currentPos.atEndOfNode()) return Position(currentNode, maxDeepOffset(currentNode)); continue; } // return current position if it is in rendered text if (renderer->isText() && static_cast<RenderText*>(renderer)->firstTextBox()) { if (currentNode != startNode) { // This assertion fires in layout tests in the case-transform.html test because // of a mix-up between offsets in the text in the DOM tree with text in the // render tree which can have a different length due to case transformation. // Until we resolve that, disable this so we can run the layout tests! //ASSERT(currentOffset >= renderer->caretMaxOffset()); return Position(currentNode, renderer->caretMaxOffset()); } unsigned textOffset = currentPos.offsetInLeafNode(); 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; }