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

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

Issue 1958093002: CONTINUED Make FrameSelection to hold non-canonicalized positions Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: 2017-01-16T20:04:35 Created 3 years, 11 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"
34 #include <iterator>
35 #include <ostream> // NOLINT
32 36
33 namespace blink { 37 namespace blink {
34 38
39 #define FOR_EACH_STATE(V) \
40 V(VisibleSelectionMayBeValid) \
41 V(DOMTreeIsChanged) \
42 V(DOMTreeIsChanging) \
43 V(SelectionInDOMTreeIsSource)
44
45 enum class SelectionEditor::CacheState {
46 #define V(name) name,
47 FOR_EACH_STATE(V)
48 #undef V
49 };
50
51 std::ostream& operator<<(std::ostream& ostream,
52 SelectionEditor::CacheState state) {
53 static const char* names[] = {
54 #define V(name) #name,
55 FOR_EACH_STATE(V)
56 #undef V
57 };
58 const auto& it = std::begin(names) + static_cast<size_t>(state);
59 if (it < std::begin(names) || it >= std::end(names))
60 return ostream << "CacheState::Bad(" << static_cast<int>(state) << ')';
61 return ostream << *it;
62 }
63
35 SelectionEditor::SelectionEditor(LocalFrame& frame) 64 SelectionEditor::SelectionEditor(LocalFrame& frame)
36 : m_frame(frame), m_observingVisibleSelection(false) { 65 : m_frame(frame), m_cacheState(CacheState::VisibleSelectionMayBeValid) {
37 clearVisibleSelection(); 66 clearVisibleSelection();
38 } 67 }
39 68
40 SelectionEditor::~SelectionEditor() {} 69 SelectionEditor::~SelectionEditor() {}
41 70
42 void SelectionEditor::clearVisibleSelection() { 71 void SelectionEditor::clearVisibleSelection() {
43 m_selection = VisibleSelection(); 72 m_selection = SelectionInDOMTree();
44 m_selectionInFlatTree = VisibleSelectionInFlatTree(); 73 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
74 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
45 if (!shouldAlwaysUseDirectionalSelection()) 75 if (!shouldAlwaysUseDirectionalSelection())
46 return; 76 return;
47 m_selection.setIsDirectional(true); 77 m_selection.m_isDirectional = true;
48 m_selectionInFlatTree.setIsDirectional(true);
49 } 78 }
50 79
51 void SelectionEditor::dispose() { 80 void SelectionEditor::dispose() {
52 resetLogicalRange(); 81 resetLogicalRange();
53 clearVisibleSelection(); 82 clearVisibleSelection();
54 } 83 }
55 84
56 const Document& SelectionEditor::document() const { 85 Document& SelectionEditor::document() const {
57 DCHECK(m_document); 86 DCHECK(lifecycleContext());
58 return *m_document; 87 return *lifecycleContext();
59 } 88 }
60 89
61 template <> 90 const SelectionInDOMTree& SelectionEditor::selectionInDOMTree() const {
62 const VisibleSelection& SelectionEditor::visibleSelection<EditingStrategy>() 91 m_selection.assertValidFor(document());
92 return m_selection;
93 }
94
95 bool SelectionEditor::hasEditableStyle() const {
96 return computeVisibleSelectionInDOMTree().hasEditableStyle();
97 }
98
99 bool SelectionEditor::isContentEditable() const {
100 return computeVisibleSelectionInDOMTree().isContentEditable();
101 }
102
103 bool SelectionEditor::isContentRichlyEditable() const {
104 return computeVisibleSelectionInDOMTree().isContentRichlyEditable();
105 }
106
107 const VisibleSelection& SelectionEditor::computeVisibleSelectionInDOMTree()
63 const { 108 const {
64 DCHECK_EQ(frame()->document(), document()); 109 DCHECK_EQ(frame()->document(), document());
65 DCHECK_EQ(frame(), document().frame()); 110 DCHECK_EQ(frame(), document().frame());
66 if (m_selection.isNone()) 111 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded();
67 return m_selection; 112 if (m_cachedVisibleSelectionInDOMTree.isNone())
68 DCHECK_EQ(m_selection.base().document(), document()); 113 return m_cachedVisibleSelectionInDOMTree;
69 return m_selection; 114 DCHECK_EQ(m_cachedVisibleSelectionInDOMTree.base().document(), document());
70 } 115 return m_cachedVisibleSelectionInDOMTree;
71 116 }
72 template <> 117
73 const VisibleSelectionInFlatTree& 118 const VisibleSelectionInFlatTree&
74 SelectionEditor::visibleSelection<EditingInFlatTreeStrategy>() const { 119 SelectionEditor::computeVisibleSelectionInFlatTree() const {
75 DCHECK_EQ(frame()->document(), document()); 120 DCHECK_EQ(frame()->document(), document());
76 DCHECK_EQ(frame(), document().frame()); 121 DCHECK_EQ(frame(), document().frame());
77 if (m_selectionInFlatTree.isNone()) 122 const_cast<SelectionEditor*>(this)->updateCachedVisibleSelectionIfNeeded();
78 return m_selectionInFlatTree; 123 if (m_cachedVisibleSelectionInFlatTree.isNone())
79 DCHECK_EQ(m_selectionInFlatTree.base().document(), document()); 124 return m_cachedVisibleSelectionInFlatTree;
80 return m_selectionInFlatTree; 125 DCHECK_EQ(m_cachedVisibleSelectionInFlatTree.base().document(), document());
81 } 126 return m_cachedVisibleSelectionInFlatTree;
82 127 }
83 void SelectionEditor::setVisibleSelection( 128
84 const VisibleSelection& newSelection, 129 void SelectionEditor::setSelection(const SelectionInDOMTree& newSelection) {
85 FrameSelection::SetSelectionOptions options) { 130 m_selection.assertValidFor(document());
86 DCHECK(newSelection.isValidFor(document())) << newSelection; 131 newSelection.assertValidFor(document());
132 if (m_selection == newSelection)
133 return;
87 resetLogicalRange(); 134 resetLogicalRange();
135 m_cacheState = CacheState::SelectionInDOMTreeIsSource;
136 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
137 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
138
88 m_selection = newSelection; 139 m_selection = newSelection;
89 if (options & FrameSelection::DoNotAdjustInFlatTree) { 140
90 m_selectionInFlatTree.setWithoutValidation( 141 m_domTreeVersion = document().domTreeVersion();
91 toPositionInFlatTree(m_selection.base()), 142 m_styleVersion = document().styleVersion();
92 toPositionInFlatTree(m_selection.extent())); 143 }
93 return; 144
94 } 145 void SelectionEditor::didChangeAttribute(const Element&) {
95 146 didFinishDOMMutation();
96 SelectionAdjuster::adjustSelectionInFlatTree(&m_selectionInFlatTree, 147 }
97 m_selection); 148
98 } 149 void SelectionEditor::didChangeChildren(const ContainerNode&) {
99 150 didFinishDOMMutation();
100 void SelectionEditor::setVisibleSelection( 151 }
101 const VisibleSelectionInFlatTree& newSelection, 152
102 FrameSelection::SetSelectionOptions options) { 153 void SelectionEditor::didMoveTreeToNewDocument() {
103 DCHECK(newSelection.isValidFor(document())) << newSelection; 154 didFinishDOMMutation();
104 DCHECK(!(options & FrameSelection::DoNotAdjustInFlatTree)); 155 }
105 resetLogicalRange(); 156
106 m_selectionInFlatTree = newSelection; 157 static Position computePositionForChildrenRemoval(const Position& position,
107 SelectionAdjuster::adjustSelectionInDOMTree(&m_selection, 158 ContainerNode& container) {
108 m_selectionInFlatTree); 159 Node* node = position.computeContainerNode();
109 } 160 if (container.containsIncludingHostElements(*node))
110 161 return Position::firstPositionInNode(&container);
111 void SelectionEditor::setWithoutValidation(const Position& base, 162 return position;
112 const Position& extent) { 163 }
113 resetLogicalRange(); 164
114 if (base.isNotNull()) 165 void SelectionEditor::nodeChildrenWillBeRemoved(ContainerNode& container) {
115 DCHECK_EQ(base.document(), document()); 166 if (m_selection.isNone())
116 if (extent.isNotNull()) 167 return;
117 DCHECK_EQ(extent.document(), document()); 168 const Position oldBase = m_selection.m_base;
118 m_selection.setWithoutValidation(base, extent); 169 const Position oldExtent = m_selection.m_extent;
119 m_selectionInFlatTree.setWithoutValidation(toPositionInFlatTree(base), 170 const Position& newBase =
120 toPositionInFlatTree(extent)); 171 computePositionForChildrenRemoval(oldBase, container);
172 const Position& newExtent =
173 computePositionForChildrenRemoval(oldExtent, container);
174 if (newBase == oldBase && newExtent == oldExtent)
175 return;
176 m_selection = SelectionInDOMTree::Builder()
177 .setBaseAndExtent(newBase, newExtent)
178 .build();
179 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
180 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
181 }
182
183 static Position positionAfterPreviousSiblingOf(const Node& node) {
184 Node* const previousSibling = NodeTraversal::previousSibling(node);
185 if (!previousSibling) {
186 // Returns |BeforeChildren| of a parent of |node| since |node| is the
187 // first child.
188 return Position::firstPositionInNode(NodeTraversal::parent(node));
189 }
190 #if 0
191 // When enable this code fragment,
192 // "editing/selection/mouse/drag-user-select-all-textarea.html" is failed.
193 //
194 // When disable this code fragment,
195 // "editing/selection/selection-after-text-normalize.html" is failed.
196 if (previousSibling->isCharacterDataNode()) {
197 return Position(previousSibling,
198 toCharacterData(previousSibling)->length());
199 }
200 #endif
201 return Position::afterNode(previousSibling);
202 }
203
204 static Position computePositionForNodeRemoval(const Position& position,
205 Node& nodeToBeRemoved) {
206 Node* const anchorNode = position.anchorNode();
207 if (anchorNode == nodeToBeRemoved)
208 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
209 if (position.isOffsetInAnchor() &&
210 anchorNode == nodeToBeRemoved.parentNode()) {
211 const int positionOffset = position.offsetInContainerNode();
212 if (positionOffset == 0)
213 return position;
214 // |nodeToBeRemoved| is sibling or itself of |position|.
215 int offset = 0;
216 for (Node& child :
217 NodeTraversal::childrenOf(*nodeToBeRemoved.parentNode())) {
218 if (nodeToBeRemoved == child) {
219 // |position| is after |nodeToBeRemoved|.
220 return Position(anchorNode, positionOffset - 1);
221 }
222 if (positionOffset == offset) {
223 // |position| is before |nodeToBeRemoved|.
224 return position;
225 }
226 ++offset;
227 }
228 NOTREACHED() << position << " should be handled.";
229 }
230 if (!nodeToBeRemoved.isContainerNode())
231 return position;
232 if (toContainerNode(nodeToBeRemoved)
233 .containsIncludingHostElements(*anchorNode))
234 return positionAfterPreviousSiblingOf(nodeToBeRemoved);
235 return position;
236 }
237
238 static Position updatePositionAfterAdoptingTextReplacement(
239 const Position& position,
240 CharacterData* node,
241 unsigned offset,
242 unsigned oldLength,
243 unsigned newLength) {
244 if (position.anchorNode() != node)
245 return position;
246
247 if (position.isBeforeAnchor()) {
248 return updatePositionAfterAdoptingTextReplacement(
249 Position(node, 0), node, offset, oldLength, newLength);
250 }
251 if (position.isAfterAnchor()) {
252 return updatePositionAfterAdoptingTextReplacement(
253 Position(node, oldLength), node, offset, oldLength, newLength);
254 }
255
256 // See:
257 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation
258 DCHECK_GE(position.offsetInContainerNode(), 0);
259 unsigned positionOffset =
260 static_cast<unsigned>(position.offsetInContainerNode());
261 // Replacing text can be viewed as a deletion followed by insertion.
262 if (positionOffset >= offset && positionOffset <= offset + oldLength)
263 positionOffset = offset;
264
265 // Adjust the offset if the position is after the end of the deleted contents
266 // (positionOffset > offset + oldLength) to avoid having a stale offset.
267 if (positionOffset > offset + oldLength)
268 positionOffset = positionOffset - oldLength + newLength;
269
270 // Due to case folding
271 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt),
272 // LayoutText length may be different from Text length. A correct
273 // implementation
274 // would translate the LayoutText offset to a Text offset; this is just a
275 // safety
276 // precaution to avoid offset values that run off the end of the Text.
277 if (positionOffset > node->length())
278 positionOffset = node->length();
279
280 return Position(node, positionOffset);
281 }
282
283 void SelectionEditor::didUpdateCharacterData(CharacterData* node,
284 unsigned offset,
285 unsigned oldLength,
286 unsigned newLength) {
287 // The fragment check is a performance optimization. See
288 // http://trac.webkit.org/changeset/30062.
289 if (m_selection.isNone() || !node || !node->isConnected()) {
290 didFinishDOMMutation();
291 return;
292 }
293 const Position& newBase = updatePositionAfterAdoptingTextReplacement(
294 m_selection.m_base, node, offset, oldLength, newLength);
295 const Position& newExtent = updatePositionAfterAdoptingTextReplacement(
296 m_selection.m_extent, node, offset, oldLength, newLength);
297 didFinishTextChange(newBase, newExtent);
298 }
299
300 static Position updatePostionAfterAdoptingTextNodesMerged(
301 const Position& position,
302 const Text& mergedNode,
303 const NodeWithIndex& nodeToBeRemovedWithIndex,
304 unsigned oldLength) {
305 Node* const anchorNode = position.anchorNode();
306 const Node& nodeToBeRemoved = nodeToBeRemovedWithIndex.node();
307 switch (position.anchorType()) {
308 case PositionAnchorType::BeforeChildren:
309 case PositionAnchorType::AfterChildren:
310 return position;
311 case PositionAnchorType::BeforeAnchor:
312 if (anchorNode == nodeToBeRemoved)
313 return Position(mergedNode, mergedNode.length());
314 return position;
315 case PositionAnchorType::AfterAnchor:
316 if (anchorNode == nodeToBeRemoved)
317 return Position(mergedNode, mergedNode.length());
318 if (anchorNode == mergedNode)
319 return Position(mergedNode, oldLength);
320 return position;
321 case PositionAnchorType::OffsetInAnchor: {
322 const int offset = position.offsetInContainerNode();
323 if (anchorNode == nodeToBeRemoved)
324 return Position(mergedNode, oldLength + offset);
325 if (anchorNode == nodeToBeRemoved.parentNode() &&
326 offset == nodeToBeRemovedWithIndex.index()) {
327 return Position(mergedNode, oldLength);
328 }
329 return position;
330 }
331 }
332 NOTREACHED() << position;
333 return position;
334 }
335
336 void SelectionEditor::didMergeTextNodes(
337 const Text& mergedNode,
338 const NodeWithIndex& nodeToBeRemovedWithIndex,
339 unsigned oldLength) {
340 if (m_selection.isNone()) {
341 didFinishDOMMutation();
342 return;
343 }
344 const Position& newBase = updatePostionAfterAdoptingTextNodesMerged(
345 m_selection.m_base, mergedNode, nodeToBeRemovedWithIndex, oldLength);
346 const Position& newExtent = updatePostionAfterAdoptingTextNodesMerged(
347 m_selection.m_extent, mergedNode, nodeToBeRemovedWithIndex, oldLength);
348 didFinishTextChange(newBase, newExtent);
349 }
350
351 static Position updatePostionAfterAdoptingTextNodeSplit(
352 const Position& position,
353 const Text& oldNode) {
354 if (position.anchorNode() != &oldNode || !position.isOffsetInAnchor())
355 return position;
356 // See:
357 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation
358 DCHECK_GE(position.offsetInContainerNode(), 0);
359 unsigned positionOffset =
360 static_cast<unsigned>(position.offsetInContainerNode());
361 unsigned oldLength = oldNode.length();
362 if (positionOffset <= oldLength)
363 return position;
364 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
365 }
366
367 void SelectionEditor::didSplitTextNode(const Text& oldNode) {
368 if (m_selection.isNone() || !oldNode.isConnected()) {
369 didFinishDOMMutation();
370 return;
371 }
372 const Position& newBase =
373 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_base, oldNode);
374 const Position& newExtent =
375 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_extent, oldNode);
376 didFinishTextChange(newBase, newExtent);
377 }
378
379 void SelectionEditor::didFinishTextChange(const Position& newBase,
380 const Position& newExtent) {
381 if (newBase != m_selection.m_base || newExtent != m_selection.m_extent) {
382 m_selection.m_base = newBase;
383 m_selection.m_extent = newExtent;
384 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
385 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
386 }
387 didFinishDOMMutation();
388 }
389
390 void SelectionEditor::didFinishDOMMutation() {
391 m_domTreeVersion = document().domTreeVersion();
392 #if ENABLE(ASSERT)
393 m_selection.m_domTreeVersion = m_domTreeVersion;
394 #endif
395 m_cacheState = CacheState::DOMTreeIsChanged;
396 // TODO(yosin): Should we notify selection change?
397 m_selection.assertValidFor(document());
398 }
399
400 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) {
401 if (m_selection.isNone())
402 return;
403 const Position oldBase = m_selection.m_base;
404 const Position oldExtent = m_selection.m_extent;
405 const Position& newBase =
406 computePositionForNodeRemoval(oldBase, nodeToBeRemoved);
407 const Position& newExtent =
408 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved);
409 if (newBase == oldBase && newExtent == oldExtent)
410 return;
411 m_cacheState = CacheState::DOMTreeIsChanging;
412 m_selection = SelectionInDOMTree::Builder()
413 .setBaseAndExtent(newBase, newExtent)
414 .build();
415 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
416 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
121 } 417 }
122 418
123 void SelectionEditor::documentAttached(Document* document) { 419 void SelectionEditor::documentAttached(Document* document) {
124 DCHECK(document); 420 DCHECK(document);
125 DCHECK(!m_document) << m_document; 421 DCHECK(!lifecycleContext()) << lifecycleContext();
126 m_document = document; 422 m_domTreeVersion = document->domTreeVersion();
127 } 423 m_styleVersion = static_cast<uint64_t>(-1);
128 424 clearVisibleSelection();
129 void SelectionEditor::documentDetached(const Document& document) { 425 setContext(document);
130 DCHECK_EQ(m_document, &document); 426 }
427
428 void SelectionEditor::contextDestroyed(Document*) {
131 dispose(); 429 dispose();
132 m_document = nullptr; 430 m_domTreeVersion = 0;
431 m_styleVersion = static_cast<uint64_t>(-1);
432 m_selection = SelectionInDOMTree();
433 m_cachedVisibleSelectionInDOMTree = VisibleSelection();
434 m_cachedVisibleSelectionInFlatTree = VisibleSelectionInFlatTree();
133 } 435 }
134 436
135 void SelectionEditor::resetLogicalRange() { 437 void SelectionEditor::resetLogicalRange() {
136 // Non-collapsed ranges are not allowed to start at the end of a line that 438 // 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 439 // is wrapped, they start at the beginning of the next line instead
138 if (!m_logicalRange) 440 if (!m_logicalRange)
139 return; 441 return;
140 m_logicalRange->dispose(); 442 m_logicalRange->dispose();
141 m_logicalRange = nullptr; 443 m_logicalRange = nullptr;
142 } 444 }
143 445
144 void SelectionEditor::setLogicalRange(Range* range) { 446 void SelectionEditor::setLogicalRange(Range* range) {
145 DCHECK_EQ(range->ownerDocument(), document()); 447 DCHECK_EQ(range->ownerDocument(), document());
146 DCHECK(!m_logicalRange) << "A logical range should be one."; 448 DCHECK(!m_logicalRange) << "A logical range should be one.";
147 m_logicalRange = range; 449 m_logicalRange = range;
148 } 450 }
149 451
150 Range* SelectionEditor::firstRange() const { 452 Range* SelectionEditor::firstRange() const {
151 if (m_logicalRange) 453 if (m_logicalRange)
152 return m_logicalRange->cloneRange(); 454 return m_logicalRange->cloneRange();
153 return firstRangeOf(m_selection); 455 return firstRangeOf(computeVisibleSelectionInDOMTree());
154 } 456 }
155 457
156 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const { 458 bool SelectionEditor::shouldAlwaysUseDirectionalSelection() const {
157 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional(); 459 return frame()->editor().behavior().shouldConsiderSelectionAsDirectional();
158 } 460 }
159 461
160 void SelectionEditor::updateIfNeeded() { 462 void SelectionEditor::updateIfNeeded() {
161 DCHECK(m_selection.isValidFor(document())) << m_selection; 463 // TODO(yosin): We should unify |SelectionEditor::updateIfNeeded()| and
162 DCHECK(m_selectionInFlatTree.isValidFor(document())) << m_selection; 464 // |updateCachedVisibleSelectionIfNeeded()|
163 m_selection.updateIfNeeded(); 465 updateCachedVisibleSelectionIfNeeded();
164 m_selectionInFlatTree.updateIfNeeded(); 466 }
467
468 class SelectionEditor::CacheUpdateScope {
469 STACK_ALLOCATED();
470
471 public:
472 CacheUpdateScope(SelectionEditor*);
473 ~CacheUpdateScope();
474
475 private:
476 DEFINE_INLINE_TRACE() { visitor->trace(m_editor); }
477
478 Member<SelectionEditor> m_editor;
479 DISALLOW_COPY_AND_ASSIGN(CacheUpdateScope);
480 };
481
482 SelectionEditor::CacheUpdateScope::CacheUpdateScope(SelectionEditor* editor)
483 : m_editor(editor) {
484 m_editor->m_isUpdatingCachedSelection = true;
485 }
486
487 SelectionEditor::CacheUpdateScope::~CacheUpdateScope() {
488 m_editor->m_styleVersion = m_editor->document().styleVersion();
489 m_editor->m_cacheState = CacheState::VisibleSelectionMayBeValid;
490 m_editor->m_isUpdatingCachedSelection = false;
491 }
492
493 bool SelectionEditor::needsUpdateVisibleSelection() const {
494 DCHECK_EQ(m_domTreeVersion, document().domTreeVersion());
495 if (m_cacheState != CacheState::VisibleSelectionMayBeValid)
496 return true;
497 return m_styleVersion != document().styleVersion();
498 }
499
500 void SelectionEditor::updateCachedVisibleSelectionIfNeeded() {
501 DCHECK(!m_isUpdatingCachedSelection)
502 << "Recursive update should not be happened.";
503 m_selection.assertValidFor(document());
504
505 // We should use |DCHECK(!document().needsLayoutTreeUpdate())| but following
506 // tests are failed:
507 // - media/track/css-cue-for-video-in-shadow-2.html
508 // - media/track/css-cue-for-video-in-shadow.html
509 // - media/track/cue-style-invalidation.html
510 // - media/track/opera/track/webvtt/parsing/001.html
511 // - media/track/text-track-selection-menu-add-track.html
512 // - media/track/track-css-all-cues.html
513 // - media/track/track-css-cue-lifetime.html [ Crash
514 // Note: Since we |FrameCaret::updateApperance()| is called from
515 // |FrameView::performPostLayoutTasks()|, we check lifecycle against
516 // |AfterPerformLayout| instead of |LayoutClean|.
517 DCHECK_GE(document().lifecycle().state(),
518 DocumentLifecycle::AfterPerformLayout);
519 if (!needsUpdateVisibleSelection())
520 return;
521
522 CacheUpdateScope updatingCache(this);
523 switch (m_cacheState) {
524 case CacheState::VisibleSelectionMayBeValid:
525 // TDOO(yosin): We should study why
526 // createVisibleSelection(m_cachedVisibleSelectionInDOMTree.asSelection())
527 // not
528 // work.
529 m_cachedVisibleSelectionInDOMTree.validate(
530 m_cachedVisibleSelectionInDOMTree.m_granularity);
531 m_cachedVisibleSelectionInFlatTree.validate(
532 m_cachedVisibleSelectionInFlatTree.m_granularity);
533 return;
534 case CacheState::DOMTreeIsChanged:
535 case CacheState::SelectionInDOMTreeIsSource: {
536 // TODO(yosin): We should get rid of |adjustSelectionInFlatTree| in
537 // |SelectionAdjuster|.
538 m_cachedVisibleSelectionInDOMTree = createVisibleSelection(m_selection);
539 m_cachedVisibleSelectionInFlatTree = createVisibleSelection(
540 SelectionInFlatTree::Builder()
541 .setBaseAndExtent(toPositionInFlatTree(m_selection.base()),
542 toPositionInFlatTree(m_selection.extent()))
543 .setAffinity(m_selection.affinity())
544 .setHasTrailingWhitespace(m_selection.hasTrailingWhitespace())
545 .setGranularity(m_selection.granularity())
546 .setIsDirectional(m_selection.isDirectional())
547 .build());
548 return;
549 }
550 case CacheState::DOMTreeIsChanging:
551 NOTREACHED() << "We should not update selection during DOM mutation";
552 return;
553 }
554 NOTREACHED() << "We should handle " << m_cacheState;
165 } 555 }
166 556
167 DEFINE_TRACE(SelectionEditor) { 557 DEFINE_TRACE(SelectionEditor) {
168 visitor->trace(m_document);
169 visitor->trace(m_frame); 558 visitor->trace(m_frame);
170 visitor->trace(m_selection); 559 visitor->trace(m_selection);
171 visitor->trace(m_selectionInFlatTree); 560 visitor->trace(m_cachedVisibleSelectionInDOMTree);
561 visitor->trace(m_cachedVisibleSelectionInFlatTree);
172 visitor->trace(m_logicalRange); 562 visitor->trace(m_logicalRange);
563 SynchronousMutationObserver::trace(visitor);
173 } 564 }
174 565
175 } // namespace blink 566 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/editing/SelectionEditor.h ('k') | third_party/WebKit/Source/core/editing/VisibleSelection.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698