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; }
VisiblePosition endOfParagraph(const VisiblePosition &c) { if (c.isNull()) return VisiblePosition(); Position p = c.deepEquivalent(); Node* startNode = p.node(); if (isRenderedAsNonInlineTableImageOrHR(startNode)) return lastDeepEditingPositionForNode(startNode); Node* startBlock = enclosingBlock(startNode); Node *stayInsideBlock = startBlock; Node *node = startNode; int offset = p.deprecatedEditingOffset(); Node *n = startNode; while (n) { if (n->isContentEditable() != startNode->isContentEditable()) break; RenderObject *r = n->renderer(); if (!r) { n = n->traverseNextNode(stayInsideBlock); continue; } RenderStyle *style = r->style(); if (style->visibility() != VISIBLE) { n = n->traverseNextNode(stayInsideBlock); continue; } if (r->isBR() || isBlock(n)) break; // FIXME: We avoid returning a position where the renderer can't accept the caret. // We should probably do this in other cases such as startOfParagraph. if (r->isText() && r->caretMaxRenderedOffset() > 0) { int length = toRenderText(r)->textLength(); if (style->preserveNewline()) { const UChar* chars = toRenderText(r)->characters(); int o = n == startNode ? offset : 0; for (int i = o; i < length; ++i) if (chars[i] == '\n') return VisiblePosition(n, i, DOWNSTREAM); } node = n; offset = r->caretMaxOffset(); n = n->traverseNextNode(stayInsideBlock); } else if (editingIgnoresContent(n) || isTableElement(n)) { node = n; offset = lastOffsetForEditing(n); n = n->traverseNextSibling(stayInsideBlock); } else n = n->traverseNextNode(stayInsideBlock); } return VisiblePosition(node, offset, DOWNSTREAM); }
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; }
// 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; }