VisibleSelectionTemplate<Strategy> PendingSelection::calcVisibleSelectionAlgorithm(const VisibleSelectionTemplate<Strategy>& originalSelection) const { const PositionTemplate<Strategy> start = originalSelection.start(); const PositionTemplate<Strategy> end = originalSelection.end(); SelectionType selectionType = originalSelection.selectionType(); const TextAffinity affinity = originalSelection.affinity(); bool paintBlockCursor = m_frameSelection->shouldShowBlockCursor() && selectionType == SelectionType::CaretSelection && !isLogicalEndOfLine(createVisiblePosition(end, affinity)); VisibleSelectionTemplate<Strategy> selection; if (enclosingTextFormControl(start.computeContainerNode())) { // TODO(yosin) We should use |PositionMoveType::Character| to avoid // ending paint at middle of character. PositionTemplate<Strategy> endPosition = paintBlockCursor ? nextPositionOf(originalSelection.extent(), PositionMoveType::CodePoint) : end; selection.setWithoutValidation(start, endPosition); return selection; } const VisiblePositionTemplate<Strategy> visibleStart = createVisiblePosition(start, selectionType == SelectionType::RangeSelection ? TextAffinity::Downstream : affinity); if (paintBlockCursor) { VisiblePositionTemplate<Strategy> visibleExtent = createVisiblePosition(end, affinity); visibleExtent = nextPositionOf(visibleExtent, CanSkipOverEditingBoundary); return VisibleSelectionTemplate<Strategy>(visibleStart, visibleExtent); } const VisiblePositionTemplate<Strategy> visibleEnd = createVisiblePosition(end, selectionType == SelectionType::RangeSelection ? TextAffinity::Upstream : affinity); return VisibleSelectionTemplate<Strategy>(visibleStart, visibleEnd); }
EphemeralRangeTemplate<Strategy> CharacterIteratorAlgorithm<Strategy>::range() const { EphemeralRangeTemplate<Strategy> range(m_textIterator.range()); if (m_textIterator.atEnd() || m_textIterator.length() <= 1) return range; PositionTemplate<Strategy> startPosition = range.startPosition().parentAnchoredEquivalent(); PositionTemplate<Strategy> endPosition = range.endPosition().parentAnchoredEquivalent(); Node* node = startPosition.computeContainerNode(); DCHECK_EQ(node, endPosition.computeContainerNode()); int offset = startPosition.offsetInContainerNode() + m_runOffset; return EphemeralRangeTemplate<Strategy>( PositionTemplate<Strategy>(node, offset), PositionTemplate<Strategy>(node, offset + 1)); }
String CreateMarkupAlgorithm<Strategy>::createMarkup( const PositionTemplate<Strategy>& startPosition, const PositionTemplate<Strategy>& endPosition, EAnnotateForInterchange shouldAnnotate, ConvertBlocksToInlines convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs, Node* constrainingAncestor) { if (startPosition.isNull() || endPosition.isNull()) return emptyString(); RELEASE_ASSERT(startPosition.compareTo(endPosition) <= 0); bool collapsed = startPosition == endPosition; if (collapsed) return emptyString(); Node* commonAncestor = Strategy::commonAncestor(*startPosition.computeContainerNode(), *endPosition.computeContainerNode()); if (!commonAncestor) return emptyString(); Document* document = startPosition.document(); DCHECK(!document->needsLayoutTreeUpdate()); DocumentLifecycle::DisallowTransitionScope disallowTransition( document->lifecycle()); HTMLElement* specialCommonAncestor = highestAncestorToWrapMarkup<Strategy>( startPosition, endPosition, shouldAnnotate, constrainingAncestor); StyledMarkupSerializer<Strategy> serializer( shouldResolveURLs, shouldAnnotate, startPosition, endPosition, specialCommonAncestor, convertBlocksToInlines); return serializer.createMarkup(); }
void PendingSelection::commitAlgorithm(LayoutView& layoutView) { if (!hasPendingSelection()) return; ASSERT(!layoutView.needsLayout()); m_hasPendingSelection = false; const VisibleSelectionTemplate<Strategy> originalSelection = m_frameSelection->visibleSelection<Strategy>(); // Skip if pending VisibilePositions became invalid before we reach here. if (!isSelectionInDocument(originalSelection, layoutView.document())) return; // Construct a new VisibleSolution, since visibleSelection() is not necessarily // valid, and the following steps assume a valid selection. // See <https://bugs.webkit.org/show_bug.cgi?id=69563> and // <rdar://problem/10232866>. const VisibleSelectionTemplate<Strategy> selection = calcVisibleSelectionAlgorithm<Strategy>(originalSelection); if (!selection.isRange()) { layoutView.clearSelection(); return; } // Use the rightmost candidate for the start of the selection, and the // leftmost candidate for the end of the selection. Example: foo <a>bar</a>. // Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. // If we pass [foo, 3] as the start of the selection, the selection painting // code will think that content on the line containing 'foo' is selected // and will fill the gap before 'bar'. PositionTemplate<Strategy> startPos = selection.start(); PositionTemplate<Strategy> candidate = mostForwardCaretPosition(startPos); if (isVisuallyEquivalentCandidate(candidate)) startPos = candidate; PositionTemplate<Strategy> endPos = selection.end(); candidate = mostBackwardCaretPosition(endPos); if (isVisuallyEquivalentCandidate(candidate)) endPos = candidate; // We can get into a state where the selection endpoints map to the same // |VisiblePosition| when a selection is deleted because we don't yet notify // the |FrameSelection| of text removal. if (startPos.isNull() || endPos.isNull() || selection.visibleStart().deepEquivalent() == selection.visibleEnd().deepEquivalent()) return; LayoutObject* startLayoutObject = startPos.anchorNode()->layoutObject(); LayoutObject* endLayoutObject = endPos.anchorNode()->layoutObject(); if (!startLayoutObject || !endLayoutObject) return; ASSERT(layoutView == startLayoutObject->view() && layoutView == endLayoutObject->view()); layoutView.setSelection(startLayoutObject, startPos.computeEditingOffset(), endLayoutObject, endPos.computeEditingOffset()); }
PositionIteratorAlgorithm<Strategy>::PositionIteratorAlgorithm( const PositionTemplate<Strategy>& pos) : PositionIteratorAlgorithm(pos.anchorNode(), pos.computeEditingOffset()) {}
static bool isSelectionInDocument(const VisibleSelectionTemplate<Strategy>& visibleSelection, const Document& document) { const PositionTemplate<Strategy> start = visibleSelection.start(); if (start.isNotNull() && (!start.inDocument() || start.document() != document)) return false; const PositionTemplate<Strategy> end = visibleSelection.end(); if (end.isNotNull() && (!end.inDocument() || end.document() != document)) return false; const PositionTemplate<Strategy> extent = visibleSelection.extent(); if (extent.isNotNull() && (!extent.inDocument() || extent.document() != document)) return false; return true; }