📄 visibleselection.cpp
字号:
VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); // If the end of this line is at the end of a paragraph, include the space // after the end of the line in the selection. if (isEndOfParagraph(end)) { VisiblePosition next = end.next(); if (next.isNotNull()) end = next; } m_end = end.deepEquivalent(); break; } case LineBoundary: m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphGranularity: { VisiblePosition pos(m_start, m_affinity); if (isStartOfLine(pos) && isEndOfDocument(pos)) pos = pos.previous(); m_start = startOfParagraph(pos).deepEquivalent(); VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); // Include the "paragraph break" (the space from the end of this paragraph to the start // of the next one) in the selection. VisiblePosition end(visibleParagraphEnd.next()); if (Node* table = isFirstPositionAfterTable(end)) { // The paragraph break after the last paragraph in the last cell of a block table ends // at the start of the paragraph after the table, not at the position just after the table. if (isBlock(table)) end = end.next(true); // There is no parargraph break after the last paragraph in the last cell of an inline table. else end = visibleParagraphEnd; } if (end.isNull()) end = visibleParagraphEnd; m_end = end.deepEquivalent(); break; } case DocumentBoundary: m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case ParagraphBoundary: m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; case SentenceBoundary: m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); break; } // Make sure we do not have a dangling start or end. if (m_start.isNull()) m_start = m_end; if (m_end.isNull()) m_end = m_start;}void VisibleSelection::updateSelectionType(){ if (m_start.isNull()) { ASSERT(m_end.isNull()); m_selectionType = NoSelection; } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { m_selectionType = CaretSelection; } else m_selectionType = RangeSelection; // Affinity only makes sense for a caret if (m_selectionType != CaretSelection) m_affinity = DOWNSTREAM;}void VisibleSelection::validate(){ setBaseAndExtentToDeepEquivalents(); setStartAndEndFromBaseAndExtentRespectingGranularity(); adjustSelectionToAvoidCrossingEditingBoundaries(); updateSelectionType(); if (selectionType() == RangeSelection) { // "Constrain" the selection to be the smallest equivalent range of nodes. // This is a somewhat arbitrary choice, but experience shows that it is // useful to make to make the selection "canonical" (if only for // purposes of comparing selections). This is an ideal point of the code // to do this operation, since all selection changes that result in a RANGE // come through here before anyone uses it. // FIXME: Canonicalizing is good, but haven't we already done it (when we // set these two positions to VisiblePosition deepEquivalent()s above)? m_start = m_start.downstream(); m_end = m_end.upstream(); }}// FIXME: This function breaks the invariant of this class.// But because we use VisibleSelection to store values in editing commands for use when// undoing the command, we need to be able to create a selection that while currently// invalid, will be valid once the changes are undone. This is a design problem.// To fix it we either need to change the invariants of VisibleSelection or create a new// class for editing to use that can manipulate selections that are not currently valid.void VisibleSelection::setWithoutValidation(const Position& base, const Position& extent){ ASSERT(!base.isNull()); ASSERT(!extent.isNull()); ASSERT(base != extent); ASSERT(m_affinity == DOWNSTREAM); ASSERT(m_granularity == CharacterGranularity); m_base = base; m_extent = extent; m_baseIsFirst = comparePositions(base, extent) <= 0; if (m_baseIsFirst) { m_start = base; m_end = extent; } else { m_start = extent; m_end = base; } m_selectionType = RangeSelection;}void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries(){ if (m_base.isNull() || m_start.isNull() || m_end.isNull()) return; Node* baseRoot = highestEditableRoot(m_base); Node* startRoot = highestEditableRoot(m_start); Node* endRoot = highestEditableRoot(m_end); Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); // The base, start and end are all in the same region. No adjustment necessary. if (baseRoot == startRoot && baseRoot == endRoot) return; // The selection is based in editable content. if (baseRoot) { // If the start is outside the base's editable root, cap it at the start of that root. // If the start is in non-editable content that is inside the base's editable root, put it // at the first editable position after start inside the base's editable root. if (startRoot != baseRoot) { VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot); m_start = first.deepEquivalent(); if (m_start.isNull()) { ASSERT_NOT_REACHED(); m_start = m_end; } } // If the end is outside the base's editable root, cap it at the end of that root. // If the end is in non-editable content that is inside the base's root, put it // at the last editable position before the end inside the base's root. if (endRoot != baseRoot) { VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot); m_end = last.deepEquivalent(); if (m_end.isNull()) { ASSERT_NOT_REACHED(); m_end = m_start; } } // The selection is based in non-editable content. } else { // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable // pieces in non-editable content are atomic. // The selection ends in editable content or non-editable content inside a different editable ancestor, // move backward until non-editable content inside the same lowest editable ancestor is reached. Node* endEditableAncestor = lowestEditableAncestor(m_end.node()); if (endRoot || endEditableAncestor != baseEditableAncestor) { Position p = previousVisuallyDistinctCandidate(m_end); Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0; if (p.isNull() && endRoot && (shadowAncestor != endRoot)) p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { Node* root = editableRootForPosition(p); shadowAncestor = root ? root->shadowAncestorNode() : 0; p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); if (p.isNull() && (shadowAncestor != root)) p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); } VisiblePosition previous(p); if (previous.isNull()) { // The selection crosses an Editing boundary. This is a // programmer error in the editing code. Happy debugging! ASSERT_NOT_REACHED(); m_base = Position(); m_extent = Position(); validate(); return; } m_end = previous.deepEquivalent(); } // The selection starts in editable content or non-editable content inside a different editable ancestor, // move forward until non-editable content inside the same lowest editable ancestor is reached. Node* startEditableAncestor = lowestEditableAncestor(m_start.node()); if (startRoot || startEditableAncestor != baseEditableAncestor) { Position p = nextVisuallyDistinctCandidate(m_start); Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0; if (p.isNull() && startRoot && (shadowAncestor != startRoot)) p = Position(shadowAncestor, 0); while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { Node* root = editableRootForPosition(p); shadowAncestor = root ? root->shadowAncestorNode() : 0; p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); if (p.isNull() && (shadowAncestor != root)) p = Position(shadowAncestor, 0); } VisiblePosition next(p); if (next.isNull()) { // The selection crosses an Editing boundary. This is a // programmer error in the editing code. Happy debugging! ASSERT_NOT_REACHED(); m_base = Position(); m_extent = Position(); validate(); return; } m_start = next.deepEquivalent(); } } // Correct the extent if necessary. if (baseEditableAncestor != lowestEditableAncestor(m_extent.node())) m_extent = m_baseIsFirst ? m_end : m_start;}bool VisibleSelection::isContentEditable() const{ return isEditablePosition(start());}bool VisibleSelection::isContentRichlyEditable() const{ return isRichlyEditablePosition(start());}Element* VisibleSelection::rootEditableElement() const{ return editableRootForPosition(start());}Node* VisibleSelection::shadowTreeRootNode() const{ return start().node() ? start().node()->shadowTreeRootNode() : 0;}void VisibleSelection::debugPosition() const{ if (!m_start.node()) return; fprintf(stderr, "VisibleSelection =================\n"); if (m_start == m_end) { Position pos = m_start; fprintf(stderr, "pos: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); } else { Position pos = m_start; fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); fprintf(stderr, "-----------------------------------\n"); pos = m_end; fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); fprintf(stderr, "-----------------------------------\n"); } fprintf(stderr, "================================\n");}#ifndef NDEBUGvoid VisibleSelection::formatForDebugger(char* buffer, unsigned length) const{ String result; String s; if (isNone()) { result = "<none>"; } else { const int FormatBufferSize = 1024; char s[FormatBufferSize]; result += "from "; start().formatForDebugger(s, FormatBufferSize); result += s; result += " to "; end().formatForDebugger(s, FormatBufferSize); result += s; } strncpy(buffer, result.utf8().data(), length - 1);}void VisibleSelection::showTreeForThis() const{ if (start().node()) { start().node()->showTreeAndMark(start().node(), "S", end().node(), "E"); fprintf(stderr, "start offset: %d, end offset: %d\n", start().offset(), end().offset()); }}#endif} // namespace WebCore#ifndef NDEBUGvoid showTree(const WebCore::VisibleSelection& sel){ sel.showTreeForThis();}void showTree(const WebCore::VisibleSelection* sel){ if (sel) sel->showTreeForThis();}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -