Position previousCandidate(const Position& position) { PositionIterator p = position; while (!p.atStart()) { p.decrement(); if (p.isCandidate()) return p; } return Position(); }
// 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; }