| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) | |
| 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights | |
| 4 * reserved. | |
| 5 * | |
| 6 * This library is free software; you can redistribute it and/or | |
| 7 * modify it under the terms of the GNU Library General Public | |
| 8 * License as published by the Free Software Foundation; either | |
| 9 * version 2 of the License, or (at your option) any later version. | |
| 10 * | |
| 11 * This library is distributed in the hope that it will be useful, | |
| 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| 14 * Library General Public License for more details. | |
| 15 * | |
| 16 * You should have received a copy of the GNU Library General Public License | |
| 17 * along with this library; see the file COPYING.LIB. If not, write to | |
| 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
| 19 * Boston, MA 02110-1301, USA. | |
| 20 */ | |
| 21 | |
| 22 #include "core/editing/PendingSelection.h" | |
| 23 | |
| 24 #include "core/dom/Document.h" | |
| 25 #include "core/editing/EditingUtilities.h" | |
| 26 #include "core/editing/FrameSelection.h" | |
| 27 #include "core/editing/VisiblePosition.h" | |
| 28 #include "core/editing/VisibleUnits.h" | |
| 29 #include "core/html/TextControlElement.h" | |
| 30 #include "core/layout/LayoutView.h" | |
| 31 | |
| 32 namespace blink { | |
| 33 | |
| 34 PendingSelection::PendingSelection(FrameSelection& frameSelection) | |
| 35 : m_frameSelection(&frameSelection), m_hasPendingSelection(false) {} | |
| 36 | |
| 37 const VisibleSelection& PendingSelection::visibleSelection() const { | |
| 38 return m_frameSelection->computeVisibleSelectionInDOMTree(); | |
| 39 } | |
| 40 | |
| 41 static bool isSelectionInDocument( | |
| 42 const VisibleSelectionInFlatTree& visibleSelection, | |
| 43 const Document& document) { | |
| 44 const PositionInFlatTree& start = visibleSelection.start(); | |
| 45 if (start.isNotNull() && | |
| 46 (!start.isConnected() || start.document() != document)) | |
| 47 return false; | |
| 48 const PositionInFlatTree& end = visibleSelection.end(); | |
| 49 if (end.isNotNull() && (!end.isConnected() || end.document() != document)) | |
| 50 return false; | |
| 51 const PositionInFlatTree extent = visibleSelection.extent(); | |
| 52 if (extent.isNotNull() && | |
| 53 (!extent.isConnected() || extent.document() != document)) | |
| 54 return false; | |
| 55 return true; | |
| 56 } | |
| 57 | |
| 58 SelectionInFlatTree PendingSelection::calcVisibleSelection( | |
| 59 const VisibleSelectionInFlatTree& originalSelection) const { | |
| 60 const PositionInFlatTree& start = originalSelection.start(); | |
| 61 const PositionInFlatTree& end = originalSelection.end(); | |
| 62 SelectionType selectionType = originalSelection.getSelectionType(); | |
| 63 const TextAffinity affinity = originalSelection.affinity(); | |
| 64 | |
| 65 bool paintBlockCursor = | |
| 66 m_frameSelection->shouldShowBlockCursor() && | |
| 67 selectionType == SelectionType::CaretSelection && | |
| 68 !isLogicalEndOfLine(createVisiblePosition(end, affinity)); | |
| 69 if (enclosingTextControl(start.computeContainerNode())) { | |
| 70 // TODO(yosin) We should use |PositionMoveType::CodePoint| to avoid | |
| 71 // ending paint at middle of character. | |
| 72 PositionInFlatTree endPosition = | |
| 73 paintBlockCursor ? nextPositionOf(originalSelection.extent(), | |
| 74 PositionMoveType::CodeUnit) | |
| 75 : end; | |
| 76 return SelectionInFlatTree::Builder() | |
| 77 .setBaseAndExtent(start, endPosition) | |
| 78 .build(); | |
| 79 } | |
| 80 | |
| 81 const VisiblePositionInFlatTree& visibleStart = createVisiblePosition( | |
| 82 start, selectionType == SelectionType::RangeSelection | |
| 83 ? TextAffinity::Downstream | |
| 84 : affinity); | |
| 85 if (visibleStart.isNull()) | |
| 86 return SelectionInFlatTree(); | |
| 87 if (paintBlockCursor) { | |
| 88 const VisiblePositionInFlatTree visibleExtent = nextPositionOf( | |
| 89 createVisiblePosition(end, affinity), CanSkipOverEditingBoundary); | |
| 90 if (visibleExtent.isNull()) | |
| 91 return SelectionInFlatTree(); | |
| 92 SelectionInFlatTree::Builder builder; | |
| 93 builder.collapse(visibleStart.toPositionWithAffinity()); | |
| 94 builder.extend(visibleExtent.deepEquivalent()); | |
| 95 return builder.build(); | |
| 96 } | |
| 97 const VisiblePositionInFlatTree visibleEnd = | |
| 98 createVisiblePosition(end, selectionType == SelectionType::RangeSelection | |
| 99 ? TextAffinity::Upstream | |
| 100 : affinity); | |
| 101 if (visibleEnd.isNull()) | |
| 102 return SelectionInFlatTree(); | |
| 103 SelectionInFlatTree::Builder builder; | |
| 104 builder.collapse(visibleStart.toPositionWithAffinity()); | |
| 105 builder.extend(visibleEnd.deepEquivalent()); | |
| 106 return builder.build(); | |
| 107 } | |
| 108 | |
| 109 void PendingSelection::commit(LayoutView& layoutView) { | |
| 110 if (!hasPendingSelection()) | |
| 111 return; | |
| 112 DCHECK(!layoutView.needsLayout()); | |
| 113 m_hasPendingSelection = false; | |
| 114 | |
| 115 const VisibleSelectionInFlatTree& originalSelection = | |
| 116 m_frameSelection->computeVisibleSelectionInFlatTree(); | |
| 117 | |
| 118 // Skip if pending VisibilePositions became invalid before we reach here. | |
| 119 if (!isSelectionInDocument(originalSelection, layoutView.document())) | |
| 120 return; | |
| 121 | |
| 122 // Construct a new VisibleSolution, since visibleSelection() is not | |
| 123 // necessarily valid, and the following steps assume a valid selection. See | |
| 124 // <https://bugs.webkit.org/show_bug.cgi?id=69563> and | |
| 125 // <rdar://problem/10232866>. | |
| 126 const SelectionInFlatTree selection = calcVisibleSelection(originalSelection); | |
| 127 const VisibleSelectionInFlatTree& visibleSelection = | |
| 128 createVisibleSelection(selection); | |
| 129 | |
| 130 if (!visibleSelection.isRange() || !selectionHasFocus(selection)) { | |
| 131 layoutView.clearSelection(); | |
| 132 return; | |
| 133 } | |
| 134 | |
| 135 // Use the rightmost candidate for the start of the selection, and the | |
| 136 // leftmost candidate for the end of the selection. Example: foo <a>bar</a>. | |
| 137 // Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. | |
| 138 // If we pass [foo, 3] as the start of the selection, the selection painting | |
| 139 // code will think that content on the line containing 'foo' is selected | |
| 140 // and will fill the gap before 'bar'. | |
| 141 PositionInFlatTree startPos = visibleSelection.start(); | |
| 142 PositionInFlatTree candidate = mostForwardCaretPosition(startPos); | |
| 143 if (isVisuallyEquivalentCandidate(candidate)) | |
| 144 startPos = candidate; | |
| 145 PositionInFlatTree endPos = visibleSelection.end(); | |
| 146 candidate = mostBackwardCaretPosition(endPos); | |
| 147 if (isVisuallyEquivalentCandidate(candidate)) | |
| 148 endPos = candidate; | |
| 149 | |
| 150 // We can get into a state where the selection endpoints map to the same | |
| 151 // |VisiblePosition| when a selection is deleted because we don't yet notify | |
| 152 // the |FrameSelection| of text removal. | |
| 153 if (startPos.isNull() || endPos.isNull() || | |
| 154 visibleSelection.visibleStart().deepEquivalent() == | |
| 155 visibleSelection.visibleEnd().deepEquivalent()) | |
| 156 return; | |
| 157 LayoutObject* startLayoutObject = startPos.anchorNode()->layoutObject(); | |
| 158 LayoutObject* endLayoutObject = endPos.anchorNode()->layoutObject(); | |
| 159 if (!startLayoutObject || !endLayoutObject) | |
| 160 return; | |
| 161 DCHECK(layoutView == startLayoutObject->view()); | |
| 162 DCHECK(layoutView == endLayoutObject->view()); | |
| 163 layoutView.setSelection(startLayoutObject, startPos.computeEditingOffset(), | |
| 164 endLayoutObject, endPos.computeEditingOffset()); | |
| 165 } | |
| 166 | |
| 167 DEFINE_TRACE(PendingSelection) { | |
| 168 visitor->trace(m_frameSelection); | |
| 169 } | |
| 170 | |
| 171 } // namespace blink | |
| OLD | NEW |