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

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-10T19:11:26 Changed for tkent@'s comments 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 clearDocumentCachedRange(); 65 clearDocumentCachedRange();
54 clearVisibleSelection(); 66 clearVisibleSelection();
55 } 67 }
56 68
57 const Document& SelectionEditor::document() const { 69 Document& SelectionEditor::document() const {
58 DCHECK(m_document); 70 DCHECK(lifecycleContext());
59 return *m_document; 71 return *lifecycleContext();
60 } 72 }
61 73
62 template <> 74 template <>
63 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() 75 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>()
64 const { 76 const {
65 DCHECK_EQ(frame()->document(), document()); 77 return computeVisibleSelectionInDOMTree();
66 DCHECK_EQ(frame(), document().frame());
67 if (m_selection.isNone())
68 return m_selection;
69 DCHECK_EQ(m_selection.base().document(), document());
70 return m_selection;
71 } 78 }
72 79
73 template <> 80 template <>
74 const VisibleSelectionInFlatTree& 81 const VisibleSelectionInFlatTree&
75 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { 82 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const {
83 return computeVisibleSelectionInFlatTree();
84 }
85
86 const VisibleSelection& SelectionEditor::computeVisibleSelectionInDOMTree()
87 const {
76 DCHECK_EQ(frame()->document(), document()); 88 DCHECK_EQ(frame()->document(), document());
77 DCHECK_EQ(frame(), document().frame()); 89 DCHECK_EQ(frame(), document().frame());
78 if (m_selectionInFlatTree.isNone()) 90 updateCachedVisibleSelectionIfNeeded();
79 return m_selectionInFlatTree; 91 if (m_cachedVisibleSelectionInDOMTree.isNone())
80 DCHECK_EQ(m_selectionInFlatTree.base().document(), document()); 92 return m_cachedVisibleSelectionInDOMTree;
81 return m_selectionInFlatTree; 93 DCHECK_EQ(m_cachedVisibleSelectionInDOMTree.base().document(), document());
94 return m_cachedVisibleSelectionInDOMTree;
82 } 95 }
83 96
84 void SelectionEditor::setVisibleSelection( 97 const VisibleSelectionInFlatTree&
85 const VisibleSelection& newSelection, 98 SelectionEditor::computeVisibleSelectionInFlatTree() const {
yoichio 2017/02/13 06:37:42 Remove const declaration.
yosin_UTC9 2017/02/13 07:28:40 I would like to keep "const" since term "computeXX
yoichio 2017/02/13 08:18:03 Then I prefer changing its name. Caller side devel
tkent 2017/02/13 08:33:27 Maybe updateCachedVisibleSelectionIfNeeded should
yosin_UTC9 2017/02/13 09:24:14 No. I don't want to call this function to computeV
86 FrameSelection::SetSelectionOptions options) { 99 DCHECK_EQ(frame()->document(), document());
87 DCHECK(newSelection.isValidFor(document())) << newSelection; 100 DCHECK_EQ(frame(), document().frame());
101 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded();
102 if (m_cachedVisibleSelectionInFlatTree.isNone())
103 return m_cachedVisibleSelectionInFlatTree;
104 DCHECK_EQ(m_cachedVisibleSelectionInFlatTree.base().document(), document());
105 return m_cachedVisibleSelectionInFlatTree;
106 }
107
108 const SelectionInDOMTree& SelectionEditor::selectionInDOMTree() const {
109 assertSelectionValid();
110 return m_selection;
111 }
112
113 bool SelectionEditor::hasEditableStyle() const {
114 return computeVisibleSelectionInDOMTree().hasEditableStyle();
115 }
116
117 bool SelectionEditor::isContentEditable() const {
118 return computeVisibleSelectionInDOMTree().isContentEditable();
119 }
120
121 bool SelectionEditor::isContentRichlyEditable() const {
122 return computeVisibleSelectionInDOMTree().isContentRichlyEditable();
123 }
124
125 void SelectionEditor::markCacheDirty() {
126 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
127 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
128 m_cacheIsDirty = true;
129 }
130
131 void SelectionEditor::setSelection(const SelectionInDOMTree& newSelection) {
132 newSelection.assertValidFor(document());
133 if (m_selection == newSelection)
134 return;
88 resetLogicalRange(); 135 resetLogicalRange();
89 clearDocumentCachedRange(); 136 clearDocumentCachedRange();
137 markCacheDirty();
138 m_selection = newSelection;
139 }
90 140
91 m_selection = newSelection; 141 void SelectionEditor::didChangeChildren(const ContainerNode&) {
92 if (options & FrameSelection::DoNotAdjustInFlatTree) { 142 markCacheDirty();
93 m_selectionInFlatTree.setWithoutValidation( 143 didFinishDOMMutation();
94 toPositionInFlatTree(m_selection.base()), 144 }
95 toPositionInFlatTree(m_selection.extent())); 145
146 static Position positionAfterPreviousSiblingOf(const Node& node) {
147 Node* const previousSibling = NodeTraversal::previousSibling(node);
148 if (!previousSibling) {
149 // Returns |BeforeChildren| of a parent of |node| since |node| is the
150 // first child.
151 return Position::firstPositionInNode(NodeTraversal::parent(node));
152 }
153 if (previousSibling->isCharacterDataNode()) {
154 return Position(previousSibling,
155 toCharacterData(previousSibling)->length());
156 }
157 return Position::afterNode(previousSibling);
158 }
159
160 static Position computePositionForNodeRemoval(const Position& position,
161 Node& nodeToBeRemoved) {
yoichio 2017/02/13 06:37:42 Why don't you use uodatePositionForNodeRemoval a
yosin_UTC9 2017/02/13 07:28:40 Good catch. I implemented by myself when I need to
162 Node* const anchorNode = position.anchorNode();
163 if (anchorNode == nodeToBeRemoved)
164 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
165 if (position.isOffsetInAnchor() &&
166 anchorNode == nodeToBeRemoved.parentNode()) {
167 const int positionOffset = position.offsetInContainerNode();
168 if (positionOffset == 0)
169 return position;
170 // |nodeToBeRemoved| is sibling or itself of |position|.
171 int offset = 0;
172 for (Node& child :
173 NodeTraversal::childrenOf(*nodeToBeRemoved.parentNode())) {
174 if (nodeToBeRemoved == child) {
175 // |position| is after |nodeToBeRemoved|.
176 return Position(anchorNode, positionOffset - 1);
177 }
178 if (positionOffset == offset) {
179 // |position| is before |nodeToBeRemoved|.
180 return position;
181 }
182 ++offset;
183 }
184 NOTREACHED() << position << " should be handled.";
185 }
186 if (!nodeToBeRemoved.isContainerNode())
187 return position;
188 if (toContainerNode(nodeToBeRemoved)
189 .containsIncludingHostElements(*anchorNode))
190 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
191 return position;
192 }
193
194 void SelectionEditor::didFinishTextChange(const Position& newBase,
195 const Position& newExtent) {
196 if (newBase == m_selection.m_base && newExtent == m_selection.m_extent) {
197 didFinishDOMMutation();
96 return; 198 return;
97 } 199 }
98 200 m_selection.m_base = newBase;
99 SelectionAdjuster::adjustSelectionInFlatTree(&m_selectionInFlatTree, 201 m_selection.m_extent = newExtent;
100 m_selection); 202 markCacheDirty();
203 didFinishDOMMutation();
101 } 204 }
102 205
103 void SelectionEditor::setVisibleSelection( 206 void SelectionEditor::didFinishDOMMutation() {
104 const VisibleSelectionInFlatTree& newSelection, 207 assertSelectionValid();
105 FrameSelection::SetSelectionOptions options) {
106 DCHECK(newSelection.isValidFor(document())) << newSelection;
107 DCHECK(!(options & FrameSelection::DoNotAdjustInFlatTree));
108 resetLogicalRange();
109 clearDocumentCachedRange();
110
111 m_selectionInFlatTree = newSelection;
112 SelectionAdjuster::adjustSelectionInDOMTree(&m_selection,
113 m_selectionInFlatTree);
114 } 208 }
115 209
116 void SelectionEditor::setWithoutValidation(const Position& base, 210 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) {
yoichio 2017/02/13 08:18:04 I prefer setting this function after |FrameSelecti
yosin_UTC9 2017/02/13 09:24:14 Done. Move to "FrameSelection.cpp"
117 const Position& extent) { 211 if (m_selection.isNone())
118 resetLogicalRange(); 212 return;
119 if (base.isNotNull()) 213 const Position oldBase = m_selection.m_base;
120 DCHECK_EQ(base.document(), document()); 214 const Position oldExtent = m_selection.m_extent;
121 if (extent.isNotNull()) 215 const Position& newBase =
122 DCHECK_EQ(extent.document(), document()); 216 computePositionForNodeRemoval(oldBase, nodeToBeRemoved);
123 clearDocumentCachedRange(); 217 const Position& newExtent =
124 218 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved);
125 m_selection.setWithoutValidation(base, extent); 219 if (newBase == oldBase && newExtent == oldExtent)
126 m_selectionInFlatTree.setWithoutValidation(toPositionInFlatTree(base), 220 return;
127 toPositionInFlatTree(extent)); 221 m_selection = SelectionInDOMTree::Builder()
222 .setBaseAndExtent(newBase, newExtent)
223 .build();
224 markCacheDirty();
128 } 225 }
129 226
130 void SelectionEditor::documentAttached(Document* document) { 227 void SelectionEditor::documentAttached(Document* document) {
131 DCHECK(document); 228 DCHECK(document);
132 DCHECK(!m_document) << m_document; 229 DCHECK(!lifecycleContext()) << lifecycleContext();
133 m_document = document; 230 m_styleVersion = static_cast<uint64_t>(-1);
231 clearVisibleSelection();
232 setContext(document);
134 } 233 }
135 234
136 void SelectionEditor::documentDetached(const Document& document) { 235 void SelectionEditor::contextDestroyed(Document*) {
137 DCHECK_EQ(m_document, &document);
138 dispose(); 236 dispose();
139 m_document = nullptr; 237 m_styleVersion = static_cast<uint64_t>(-1);
238 m_selection = SelectionInDOMTree();
239 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
240 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
241 m_cacheIsDirty = false;
140 } 242 }
141 243
142 void SelectionEditor::resetLogicalRange() { 244 void SelectionEditor::resetLogicalRange() {
143 // Non-collapsed ranges are not allowed to start at the end of a line that 245 // Non-collapsed ranges are not allowed to start at the end of a line that
144 // is wrapped, they start at the beginning of the next line instead 246 // is wrapped, they start at the beginning of the next line instead
145 if (!m_logicalRange) 247 if (!m_logicalRange)
146 return; 248 return;
147 m_logicalRange->dispose(); 249 m_logicalRange->dispose();
148 m_logicalRange = nullptr; 250 m_logicalRange = nullptr;
149 } 251 }
150 252
151 void SelectionEditor::setLogicalRange(Range* range) { 253 void SelectionEditor::setLogicalRange(Range* range) {
152 DCHECK_EQ(range->ownerDocument(), document()); 254 DCHECK_EQ(range->ownerDocument(), document());
153 DCHECK(!m_logicalRange) << "A logical range should be one."; 255 DCHECK(!m_logicalRange) << "A logical range should be one.";
154 m_logicalRange = range; 256 m_logicalRange = range;
155 } 257 }
156 258
157 Range* SelectionEditor::firstRange() const { 259 Range* SelectionEditor::firstRange() const {
158 if (m_logicalRange) 260 if (m_logicalRange)
159 return m_logicalRange->cloneRange(); 261 return m_logicalRange->cloneRange();
160 return firstRangeOf(m_selection); 262 return firstRangeOf(computeVisibleSelectionInDOMTree());
161 } 263 }
162 264
163 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { 265 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const {
164 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional(); 266 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional();
165 } 267 }
166 268
167 void SelectionEditor::updateIfNeeded() { 269 void SelectionEditor::updateIfNeeded() {
168 DCHECK(m_selection.isValidFor(document())) << m_selection; 270 // TODO(yosin): We should unify |SelectionEditor::updateIfNeeded()| and
169 DCHECK(m_selectionInFlatTree.isValidFor(document())) << m_selection; 271 // |updateCachedVisibleSelectionIfNeeded()|
170 m_selection.updateIfNeeded(); 272 updateCachedVisibleSelectionIfNeeded();
171 m_selectionInFlatTree.updateIfNeeded(); 273 }
274
275 bool SelectionEditor::needsUpdateVisibleSelection() const {
276 return m_cacheIsDirty || m_styleVersion != document().styleVersion();
277 }
278
279 void SelectionEditor::updateCachedVisibleSelectionIfNeeded() const {
280 // Note: Since we |FrameCaret::updateApperance()| is called from
281 // |FrameView::performPostLayoutTasks()|, we check lifecycle against
282 // |AfterPerformLayout| instead of |LayoutClean|.
283 DCHECK_GE(document().lifecycle().state(),
284 DocumentLifecycle::AfterPerformLayout);
285 assertSelectionValid();
286 if (!needsUpdateVisibleSelection())
287 return;
288
289 m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection);
290 m_cachedVisibleSelectionInFlatTree = createVisibleSelection(
291 SelectionInFlatTree::Builder()
292 .setBaseAndExtent(toPositionInFlatTree(m_selection.base()),
293 toPositionInFlatTree(m_selection.extent()))
294 .setAffinity(m_selection.affinity())
295 .setHasTrailingWhitespace(m_selection.hasTrailingWhitespace())
296 .setGranularity(m_selection.granularity())
297 .setIsDirectional(m_selection.isDirectional())
298 .build());
299 m_styleVersion = document().styleVersion();
300 m_cacheIsDirty = false;
172 } 301 }
173 302
174 void SelectionEditor::cacheRangeOfDocument(Range* range) { 303 void SelectionEditor::cacheRangeOfDocument(Range* range) {
175 m_cachedRange = range; 304 m_cachedRange = range;
176 } 305 }
177 306
178 Range* SelectionEditor::documentCachedRange() const { 307 Range* SelectionEditor::documentCachedRange() const {
179 return m_cachedRange; 308 return m_cachedRange;
180 } 309 }
181 310
182 void SelectionEditor::clearDocumentCachedRange() { 311 void SelectionEditor::clearDocumentCachedRange() {
183 m_cachedRange = nullptr; 312 m_cachedRange = nullptr;
184 } 313 }
185 314
186 DEFINE_TRACE(SelectionEditor) { 315 DEFINE_TRACE(SelectionEditor) {
187 visitor->trace(m_document);
188 visitor->trace(m_frame); 316 visitor->trace(m_frame);
189 visitor->trace(m_selection); 317 visitor->trace(m_selection);
190 visitor->trace(m_selectionInFlatTree); 318 visitor->trace(m_cachedVisibleSelectionInDOMTree);
319 visitor->trace(m_cachedVisibleSelectionInFlatTree);
191 visitor->trace(m_logicalRange); 320 visitor->trace(m_logicalRange);
192 visitor->trace(m_cachedRange); 321 visitor->trace(m_cachedRange);
322 SynchronousMutationObserver::trace(visitor);
193 } 323 }
194 324
195 } // namespace blink 325 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698