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 |