Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(278)

Side by Side Diff: third_party/WebKit/Source/core/editing/SelectionEditor.cpp

Issue 2680943004: Make FrameSelection to hold non-canonicalized positions (Closed)
Patch Set: 2017-02-09T15:13:43 Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved. 2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution. 11 * documentation and/or other materials provided with the distribution.
12 * 12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */ 24 */
25 25
26 #include "core/editing/SelectionEditor.h" 26 #include "core/editing/SelectionEditor.h"
27 27
28 #include "core/dom/NodeWithIndex.h"
29 #include "core/dom/Text.h"
28 #include "core/editing/EditingUtilities.h" 30 #include "core/editing/EditingUtilities.h"
29 #include "core/editing/Editor.h" 31 #include "core/editing/Editor.h"
30 #include "core/editing/SelectionAdjuster.h" 32 #include "core/editing/SelectionAdjuster.h"
31 #include "core/frame/LocalFrame.h" 33 #include "core/frame/LocalFrame.h"
32 34
33 namespace blink { 35 namespace blink {
34 36
35 SelectionEditor::SelectionEditor(LocalFrame& frame) 37 SelectionEditor::SelectionEditor(LocalFrame& frame) : m_frame(frame) {
36 : m_frame(frame), m_observingVisibleSelection(false) {
37 clearVisibleSelection(); 38 clearVisibleSelection();
38 } 39 }
39 40
40 SelectionEditor::~SelectionEditor() {} 41 SelectionEditor::~SelectionEditor() {}
41 42
43 void SelectionEditor::assertSelectionValid() const {
44 #if DCHECK_IS_ON()
45 // Since We don't track dom tree version during attribute changes, we can't
46 // use it for validity of |m_selection|.
47 const_cast<SelectionEditor*>(this)->m_selection.m_domTreeVersion =
48 document().domTreeVersion();
49 #endif
50 m_selection.assertValidFor(document());
51 }
52
42 void SelectionEditor::clearVisibleSelection() { 53 void SelectionEditor::clearVisibleSelection() {
43 m_selection = VisibleSelection(); 54 m_selection = SelectionInDOMTree();
44 m_selectionInFlatTree = VisibleSelectionInFlatTree(); 55 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
56 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
57 m_cacheIsDirty = false;
45 if (!shouldAlwaysUseDirectionalSelection()) 58 if (!shouldAlwaysUseDirectionalSelection())
46 return; 59 return;
47 m_selection.setIsDirectional(true); 60 m_selection.m_isDirectional = true;
48 m_selectionInFlatTree.setIsDirectional(true);
49 } 61 }
50 62
51 void SelectionEditor::dispose() { 63 void SelectionEditor::dispose() {
52 resetLogicalRange(); 64 resetLogicalRange();
53 clearVisibleSelection(); 65 clearVisibleSelection();
54 } 66 }
55 67
56 const Document& SelectionEditor::document() const { 68 const Document& SelectionEditor::document() const {
57 DCHECK(m_document); 69 DCHECK(lifecycleContext());
58 return *m_document; 70 return *lifecycleContext();
71 }
72
73 Document& SelectionEditor::document() {
74 DCHECK(lifecycleContext());
75 return *lifecycleContext();
59 } 76 }
60 77
61 template <> 78 template <>
62 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() 79 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>()
63 const { 80 const {
64 DCHECK_EQ(frame()->document(), document()); 81 return computeVisibleSelectionInDOMTree();
65 DCHECK_EQ(frame(), document().frame());
66 if (m_selection.isNone())
67 return m_selection;
68 DCHECK_EQ(m_selection.base().document(), document());
69 return m_selection;
70 } 82 }
71 83
72 template <> 84 template <>
73 const VisibleSelectionInFlatTree& 85 const VisibleSelectionInFlatTree&
74 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { 86 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const {
87 return computeVisibleSelectionInFlatTree();
88 }
89
90 const VisibleSelection& SelectionEditor::computeVisibleSelectionInDOMTree()
91 const {
75 DCHECK_EQ(frame()->document(), document()); 92 DCHECK_EQ(frame()->document(), document());
76 DCHECK_EQ(frame(), document().frame()); 93 DCHECK_EQ(frame(), document().frame());
77 if (m_selectionInFlatTree.isNone()) 94 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded();
yoichio 2017/02/10 05:24:52 Why do you const_cast the same type?
yosin_UTC9 2017/02/10 06:38:41 This is const member function, |this| is type |con
yoichio 2017/02/13 04:38:19 I prefer const-clean code flow. Could you rm |cons
78 return m_selectionInFlatTree; 95 if (m_cachedVisibleSelectionInDOMTree.isNone())
79 DCHECK_EQ(m_selectionInFlatTree.base().document(), document()); 96 return m_cachedVisibleSelectionInDOMTree;
80 return m_selectionInFlatTree; 97 DCHECK_EQ(m_cachedVisibleSelectionInDOMTree.base().document(), document());
98 return m_cachedVisibleSelectionInDOMTree;
81 } 99 }
82 100
83 void SelectionEditor::setVisibleSelection( 101 const VisibleSelectionInFlatTree&
84 const VisibleSelection& newSelection, 102 SelectionEditor::computeVisibleSelectionInFlatTree() const {
85 FrameSelection::SetSelectionOptions options) { 103 DCHECK_EQ(frame()->document(), document());
86 DCHECK(newSelection.isValidFor(document())) << newSelection; 104 DCHECK_EQ(frame(), document().frame());
105 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded();
106 if (m_cachedVisibleSelectionInFlatTree.isNone())
107 return m_cachedVisibleSelectionInFlatTree;
108 DCHECK_EQ(m_cachedVisibleSelectionInFlatTree.base().document(), document());
109 return m_cachedVisibleSelectionInFlatTree;
110 }
111
112 const SelectionInDOMTree& SelectionEditor::selectionInDOMTree() const {
113 assertSelectionValid();
114 return m_selection;
115 }
116
117 bool SelectionEditor::hasEditableStyle() const {
118 return computeVisibleSelectionInDOMTree().hasEditableStyle();
119 }
120
121 bool SelectionEditor::isContentEditable() const {
122 return computeVisibleSelectionInDOMTree().isContentEditable();
123 }
124
125 bool SelectionEditor::isContentRichlyEditable() const {
126 return computeVisibleSelectionInDOMTree().isContentRichlyEditable();
127 }
128
129 void SelectionEditor::markCacheDirty() {
130 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
131 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
132 m_cacheIsDirty = true;
133 }
134
135 void SelectionEditor::setSelection(const SelectionInDOMTree& newSelection) {
136 newSelection.assertValidFor(document());
137 if (m_selection == newSelection)
138 return;
87 resetLogicalRange(); 139 resetLogicalRange();
140 markCacheDirty();
88 m_selection = newSelection; 141 m_selection = newSelection;
89 if (options & FrameSelection::DoNotAdjustInFlatTree) { 142 }
90 m_selectionInFlatTree.setWithoutValidation( 143
91 toPositionInFlatTree(m_selection.base()), 144 void SelectionEditor::didChangeChildren(const ContainerNode&) {
92 toPositionInFlatTree(m_selection.extent())); 145 markCacheDirty();
146 didFinishDOMMutation();
147 }
148
149 static Position positionAfterPreviousSiblingOf(const Node& node) {
150 Node* const previousSibling = NodeTraversal::previousSibling(node);
151 if (!previousSibling) {
152 // Returns |BeforeChildren| of a parent of |node| since |node| is the
153 // first child.
154 return Position::firstPositionInNode(NodeTraversal::parent(node));
155 }
156 if (previousSibling->isCharacterDataNode()) {
157 return Position(previousSibling,
158 toCharacterData(previousSibling)->length());
159 }
160 return Position::afterNode(previousSibling);
161 }
162
163 static Position computePositionForNodeRemoval(const Position& position,
164 Node& nodeToBeRemoved) {
165 Node* const anchorNode = position.anchorNode();
166 if (anchorNode == nodeToBeRemoved)
167 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
168 if (position.isOffsetInAnchor() &&
169 anchorNode == nodeToBeRemoved.parentNode()) {
170 const int positionOffset = position.offsetInContainerNode();
171 if (positionOffset == 0)
172 return position;
173 // |nodeToBeRemoved| is sibling or itself of |position|.
174 int offset = 0;
175 for (Node& child :
176 NodeTraversal::childrenOf(*nodeToBeRemoved.parentNode())) {
177 if (nodeToBeRemoved == child) {
178 // |position| is after |nodeToBeRemoved|.
179 return Position(anchorNode, positionOffset - 1);
180 }
181 if (positionOffset == offset) {
182 // |position| is before |nodeToBeRemoved|.
183 return position;
184 }
185 ++offset;
186 }
187 NOTREACHED() << position << " should be handled.";
188 }
189 if (!nodeToBeRemoved.isContainerNode())
190 return position;
191 if (toContainerNode(nodeToBeRemoved)
192 .containsIncludingHostElements(*anchorNode))
193 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
194 return position;
195 }
196
197 void SelectionEditor::didFinishTextChange(const Position& newBase,
198 const Position& newExtent) {
199 if (newBase == m_selection.m_base && newExtent == m_selection.m_extent) {
200 didFinishDOMMutation();
93 return; 201 return;
94 } 202 }
95 203 m_selection.m_base = newBase;
96 SelectionAdjuster::adjustSelectionInFlatTree(&m_selectionInFlatTree, 204 m_selection.m_extent = newExtent;
97 m_selection); 205 markCacheDirty();
206 didFinishDOMMutation();
98 } 207 }
99 208
100 void SelectionEditor::setVisibleSelection( 209 void SelectionEditor::didFinishDOMMutation() {
101 const VisibleSelectionInFlatTree& newSelection, 210 assertSelectionValid();
102 FrameSelection::SetSelectionOptions options) {
103 DCHECK(newSelection.isValidFor(document())) << newSelection;
104 DCHECK(!(options & FrameSelection::DoNotAdjustInFlatTree));
105 resetLogicalRange();
106 m_selectionInFlatTree = newSelection;
107 SelectionAdjuster::adjustSelectionInDOMTree(&m_selection,
108 m_selectionInFlatTree);
109 } 211 }
110 212
111 void SelectionEditor::setWithoutValidation(const Position& base, 213 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) {
112 const Position& extent) { 214 if (m_selection.isNone())
113 resetLogicalRange(); 215 return;
114 if (base.isNotNull()) 216 const Position oldBase = m_selection.m_base;
115 DCHECK_EQ(base.document(), document()); 217 const Position oldExtent = m_selection.m_extent;
116 if (extent.isNotNull()) 218 const Position& newBase =
117 DCHECK_EQ(extent.document(), document()); 219 computePositionForNodeRemoval(oldBase, nodeToBeRemoved);
118 m_selection.setWithoutValidation(base, extent); 220 const Position& newExtent =
119 m_selectionInFlatTree.setWithoutValidation(toPositionInFlatTree(base), 221 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved);
120 toPositionInFlatTree(extent)); 222 if (newBase == oldBase && newExtent == oldExtent)
223 return;
224 m_selection = SelectionInDOMTree::Builder()
225 .setBaseAndExtent(newBase, newExtent)
226 .build();
227 markCacheDirty();
121 } 228 }
122 229
123 void SelectionEditor::documentAttached(Document* document) { 230 void SelectionEditor::documentAttached(Document* document) {
124 DCHECK(document); 231 DCHECK(document);
125 DCHECK(!m_document) << m_document; 232 DCHECK(!lifecycleContext()) << lifecycleContext();
126 m_document = document; 233 m_styleVersion = static_cast<uint64_t>(-1);
234 clearVisibleSelection();
235 setContext(document);
127 } 236 }
128 237
129 void SelectionEditor::documentDetached(const Document& document) { 238 void SelectionEditor::contextDestroyed(Document*) {
130 DCHECK_EQ(m_document, &document);
131 dispose(); 239 dispose();
132 m_document = nullptr; 240 m_styleVersion = static_cast<uint64_t>(-1);
241 m_selection = SelectionInDOMTree();
242 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
243 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
244 m_cacheIsDirty = false;
133 } 245 }
134 246
135 void SelectionEditor::resetLogicalRange() { 247 void SelectionEditor::resetLogicalRange() {
136 // Non-collapsed ranges are not allowed to start at the end of a line that 248 // Non-collapsed ranges are not allowed to start at the end of a line that
137 // is wrapped, they start at the beginning of the next line instead 249 // is wrapped, they start at the beginning of the next line instead
138 if (!m_logicalRange) 250 if (!m_logicalRange)
139 return; 251 return;
140 m_logicalRange->dispose(); 252 m_logicalRange->dispose();
141 m_logicalRange = nullptr; 253 m_logicalRange = nullptr;
142 } 254 }
143 255
144 void SelectionEditor::setLogicalRange(Range* range) { 256 void SelectionEditor::setLogicalRange(Range* range) {
145 DCHECK_EQ(range->ownerDocument(), document()); 257 DCHECK_EQ(range->ownerDocument(), document());
146 DCHECK(!m_logicalRange) << "A logical range should be one."; 258 DCHECK(!m_logicalRange) << "A logical range should be one.";
147 m_logicalRange = range; 259 m_logicalRange = range;
148 } 260 }
149 261
150 Range* SelectionEditor::firstRange() const { 262 Range* SelectionEditor::firstRange() const {
151 if (m_logicalRange) 263 if (m_logicalRange)
152 return m_logicalRange->cloneRange(); 264 return m_logicalRange->cloneRange();
153 return firstRangeOf(m_selection); 265 return firstRangeOf(computeVisibleSelectionInDOMTree());
154 } 266 }
155 267
156 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { 268 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const {
157 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional(); 269 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional();
158 } 270 }
159 271
160 void SelectionEditor::updateIfNeeded() { 272 void SelectionEditor::updateIfNeeded() {
161 DCHECK(m_selection.isValidFor(document())) << m_selection; 273 // TODO(yosin): We should unify |SelectionEditor::updateIfNeeded()| and
162 DCHECK(m_selectionInFlatTree.isValidFor(document())) << m_selection; 274 // |updateCachedVisibleSelectionIfNeeded()|
163 m_selection.updateIfNeeded(); 275 updateCachedVisibleSelectionIfNeeded();
164 m_selectionInFlatTree.updateIfNeeded(); 276 }
277
278 bool SelectionEditor::needsUpdateVisibleSelection() const {
279 return m_cacheIsDirty || m_styleVersion != document().styleVersion();
280 }
281
282 void SelectionEditor::updateCachedVisibleSelectionIfNeeded() {
283 // Note: Since we |FrameCaret::updateApperance()| is called from
284 // |FrameView::performPostLayoutTasks()|, we check lifecycle against
285 // |AfterPerformLayout| instead of |LayoutClean|.
286 DCHECK_GE(document().lifecycle().state(),
287 DocumentLifecycle::AfterPerformLayout);
288 assertSelectionValid();
289 if (!needsUpdateVisibleSelection())
290 return;
291
292 m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection);
293 m_cachedVisibleSelectionInFlatTree = createVisibleSelection(
294 SelectionInFlatTree::Builder()
295 .setBaseAndExtent(toPositionInFlatTree(m_selection.base()),
296 toPositionInFlatTree(m_selection.extent()))
297 .setAffinity(m_selection.affinity())
298 .setHasTrailingWhitespace(m_selection.hasTrailingWhitespace())
299 .setGranularity(m_selection.granularity())
300 .setIsDirectional(m_selection.isDirectional())
301 .build());
302 m_styleVersion = document().styleVersion();
303 m_cacheIsDirty = false;
165 } 304 }
166 305
167 DEFINE_TRACE(SelectionEditor) { 306 DEFINE_TRACE(SelectionEditor) {
168 visitor->trace(m_document);
169 visitor->trace(m_frame); 307 visitor->trace(m_frame);
170 visitor->trace(m_selection); 308 visitor->trace(m_selection);
171 visitor->trace(m_selectionInFlatTree); 309 visitor->trace(m_cachedVisibleSelectionInDOMTree);
310 visitor->trace(m_cachedVisibleSelectionInFlatTree);
172 visitor->trace(m_logicalRange); 311 visitor->trace(m_logicalRange);
312 SynchronousMutationObserver::trace(visitor);
173 } 313 }
174 314
175 } // namespace blink 315 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698