📄 position.cpp
字号:
Node* lastNode = startNode; for (; !currentPos.atStart(); currentPos.decrement()) { Node* currentNode = currentPos.node(); // Don't check for an editability change if we haven't moved to a different node, // to avoid the expense of computing isContentEditable(). if (currentNode != lastNode) { // Don't change editability. bool currentEditable = currentNode->isContentEditable(); if (startEditable != currentEditable) break; lastNode = currentNode; } // If we've moved to a position that is visually disinct, return the last saved position. There // is code below that terminates early if we're *about* to move to a visually distinct position. if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) 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 move past a position that is visually distinct. We could rely on code above to terminate and // return lastVisible on the next iteration, but we terminate early to avoid doing a nodeIndex() call. if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentPos.atStartOfNode()) return lastVisible; // Return position after tables and nodes which have content that can be ignored. if (editingIgnoresContent(currentNode) || isTableElement(currentNode)) { if (currentPos.atEndOfNode()) return Position(currentNode, maxDeepOffset(currentNode)); continue; } // return current position if it is in rendered text if (renderer->isText() && toRenderText(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 = toRenderText(renderer); InlineTextBox* lastTextBox = textRenderer->lastTextBox(); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->start() + box->len()) { if (textOffset > box->start()) return currentPos; continue; } if (box == lastTextBox || textOffset != box->start() + box->len() + 1) continue; // The text continues on the next line only if the last text box is not on this line and // none of the boxes on this line have a larger start offset. bool continuesOnNextLine = true; InlineBox* otherBox = box; while (continuesOnNextLine) { otherBox = otherBox->nextLeafChild(); if (!otherBox) break; if (otherBox == lastTextBox || otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() > textOffset) continuesOnNextLine = false; } otherBox = box; while (continuesOnNextLine) { otherBox = otherBox->prevLeafChild(); if (!otherBox) break; if (otherBox == lastTextBox || otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() > textOffset) continuesOnNextLine = false; } if (continuesOnNextLine) return currentPos; } } } return lastVisible;}// This function and upstream() are used for moving back and forth between visually equivalent candidates.// For example, for the text node "foo bar" where whitespace is collapsible, there are two candidates // that map to the VisiblePosition between 'b' and the space. This function will return the right candidate // and upstream() will return the left one.// Also, downstream() will return the last position in the last atomic node in boundary for all of the positions// in boundary after the last candidate, where endsOfNodeAreVisuallyDistinctPositions(boundary).Position Position::downstream() const{ Node* startNode = node(); if (!startNode) return Position(); // iterate forward from there, looking for a qualified position Node* boundary = enclosingVisualBoundary(startNode); PositionIterator lastVisible = *this; PositionIterator currentPos = lastVisible; bool startEditable = startNode->isContentEditable(); Node* lastNode = startNode; for (; !currentPos.atEnd(); currentPos.increment()) { Node* currentNode = currentPos.node(); // Don't check for an editability change if we haven't moved to a different node, // to avoid the expense of computing isContentEditable(). if (currentNode != lastNode) { // Don't change editability. bool currentEditable = currentNode->isContentEditable(); if (startEditable != currentEditable) break; lastNode = currentNode; } // 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 move to a visually distinct position. if (endsOfNodeAreVisuallyDistinctPositions(currentNode) && currentNode != boundary) return lastVisible; // Do not move past a visually disinct position. // Note: The first position after the last in a node whose ends are visually distinct // positions will be [boundary->parentNode(), originalBlock->nodeIndex() + 1]. if (boundary && boundary->parentNode() == 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 tables and nodes which have content that can be ignored. if (editingIgnoresContent(currentNode) || 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() && toRenderText(renderer)->firstTextBox()) { if (currentNode != startNode) { ASSERT(currentPos.atStartOfNode()); return Position(currentNode, renderer->caretMinOffset()); } unsigned textOffset = currentPos.offsetInLeafNode(); RenderText* textRenderer = toRenderText(renderer); InlineTextBox* lastTextBox = textRenderer->lastTextBox(); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (textOffset <= box->end()) { if (textOffset >= box->start()) return currentPos; continue; } if (box == lastTextBox || textOffset != box->start() + box->len()) continue; // The text continues on the next line only if the last text box is not on this line and // none of the boxes on this line have a larger start offset. bool continuesOnNextLine = true; InlineBox* otherBox = box; while (continuesOnNextLine) { otherBox = otherBox->nextLeafChild(); if (!otherBox) break; if (otherBox == lastTextBox || otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() >= textOffset) continuesOnNextLine = false; } otherBox = box; while (continuesOnNextLine) { otherBox = otherBox->prevLeafChild(); if (!otherBox) break; if (otherBox == lastTextBox || otherBox->renderer() == textRenderer && static_cast<InlineTextBox*>(otherBox)->start() >= textOffset) continuesOnNextLine = false; } if (continuesOnNextLine) return currentPos; } } } return lastVisible;}bool Position::hasRenderedNonAnonymousDescendantsWithHeight(RenderObject* renderer){ RenderObject* stop = renderer->nextInPreOrderAfterChildren(); for (RenderObject *o = renderer->firstChild(); o && o != stop; o = o->nextInPreOrder()) if (o->node()) { if ((o->isText() && toRenderText(o)->linesBoundingBox().height()) || (o->isBox() && toRenderBox(o)->borderBoundingBox().height())) return true; } return false;}bool Position::nodeIsUserSelectNone(Node* node){ return node && node->renderer() && node->renderer()->style()->userSelect() == SELECT_NONE;}bool Position::isCandidate() const{ if (isNull()) return false; RenderObject *renderer = node()->renderer(); if (!renderer) return false; if (renderer->style()->visibility() != VISIBLE) return false; if (renderer->isBR()) return offset() == 0 && !nodeIsUserSelectNone(node()->parent()); if (renderer->isText()) return inRenderedText() && !nodeIsUserSelectNone(node()); if (isTableElement(node()) || editingIgnoresContent(node())) return (offset() == 0 || offset() == maxDeepOffset(node())) && !nodeIsUserSelectNone(node()->parent()); if (!node()->hasTagName(htmlTag) && renderer->isBlockFlow() && !hasRenderedNonAnonymousDescendantsWithHeight(renderer) && (toRenderBox(renderer)->height() || node()->hasTagName(bodyTag))) return offset() == 0 && !nodeIsUserSelectNone(node()); return false;}bool Position::inRenderedText() const{ if (isNull() || !node()->isTextNode()) return false; RenderObject *renderer = node()->renderer(); if (!renderer) return false; RenderText *textRenderer = toRenderText(renderer); for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (offset() < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { // 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; } if (box->containsCaretOffset(offset())) // Return false for offsets inside composed characters. return offset() == 0 || offset() == textRenderer->nextOffset(textRenderer->previousOffset(offset())); } return false;}static unsigned caretMaxRenderedOffset(const Node* n){ RenderObject* r = n->renderer(); if (r) return r->caretMaxRenderedOffset(); if (n->isCharacterDataNode()) return static_cast<const CharacterData*>(n)->length(); return 1;}bool Position::isRenderedCharacter() const{ if (isNull() || !node()->isTextNode()) return false; RenderObject* renderer = node()->renderer(); if (!renderer) return false; RenderText* textRenderer = toRenderText(renderer); for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) { if (offset() < static_cast<int>(box->start()) && !textRenderer->containsReversedText()) { // 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; } if (offset() >= static_cast<int>(box->start()) && offset() < static_cast<int>(box->start() + box->len())) return true; } return false;}bool Position::rendersInDifferentPosition(const Position &pos) const{ if (isNull() || pos.isNull()) return false; RenderObject *renderer = node()->renderer(); if (!renderer) return false; RenderObject *posRenderer = pos.node()->renderer(); if (!posRenderer) return false; if (renderer->style()->visibility() != VISIBLE || posRenderer->style()->visibility() != VISIBLE) return false; if (node() == pos.node()) { if (node()->hasTagName(brTag)) return false; if (offset() == pos.offset()) return false; if (!node()->isTextNode() && !pos.node()->isTextNode()) { if (offset() != pos.offset()) return true;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -