bool Position::inRenderedContent() const { if (isNull()) return false; RenderObject *renderer = node()->renderer(); if (!renderer) return false; if (renderer->style()->visibility() != VISIBLE) return false; // FIXME: This check returns false for a <br> at the end of a line! if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) { return offset() == 0; } else if (renderer->isText()) { RenderText *textRenderer = static_cast<RenderText *>(renderer); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (offset() >= box->m_start && offset() <= box->m_start + box->m_len) { return true; } else if (offset() < box->m_start) { // The offset we're looking for is before this node // this means the offset must be in content that is // not rendered. Return false. return false; } } } else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) { // return true for replaced elements, for inline flows if they have a line box // and for blocks if they are empty if (renderer->isReplaced() || (renderer->isInlineFlow() && static_cast<RenderFlow *>(renderer)->firstLineBox()) || (renderer->isBlockFlow() && !renderer->firstChild() && renderer->height())) return true; } return false; }
int caretMinOffset(const Node* n) { RenderObject* r = n->renderer(); ASSERT(!n->isCharacterDataNode() || !r || r->isText()); // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. return r ? r->caretMinOffset() : 0; }
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; }