OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
tkent
2016/01/27 05:12:48
Please use the copyright headers of SelectionEdito
yosin_UTC9
2016/01/27 05:58:22
Done
| |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "core/editing/SelectionAdjuster.h" | |
6 | |
7 #include "core/editing/EditingUtilities.h" | |
8 | |
9 namespace blink { | |
10 | |
11 namespace { | |
12 | |
13 Node* enclosingShadowHost(Node* node) | |
14 { | |
15 for (Node* runner = node; runner; runner = ComposedTreeTraversal::parent(*ru nner)) { | |
16 if (isShadowHost(runner)) | |
17 return runner; | |
18 } | |
19 return nullptr; | |
20 } | |
21 | |
22 bool isEnclosedBy(const PositionInComposedTree& position, const Node& node) | |
23 { | |
24 ASSERT(position.isNotNull()); | |
25 Node* anchorNode = position.anchorNode(); | |
26 if (anchorNode == node) | |
27 return !position.isAfterAnchor() && !position.isBeforeAnchor(); | |
28 | |
29 return ComposedTreeTraversal::isDescendantOf(*anchorNode, node); | |
30 } | |
31 | |
32 bool isSelectionBoundary(const Node& node) | |
33 { | |
34 return isHTMLTextAreaElement(node) || isHTMLInputElement(node) || isHTMLSele ctElement(node); | |
35 } | |
36 | |
37 Node* enclosingShadowHostForStart(const PositionInComposedTree& position) | |
38 { | |
39 Node* node = position.nodeAsRangeFirstNode(); | |
40 if (!node) | |
41 return nullptr; | |
42 Node* shadowHost = enclosingShadowHost(node); | |
43 if (!shadowHost) | |
44 return nullptr; | |
45 if (!isEnclosedBy(position, *shadowHost)) | |
46 return nullptr; | |
47 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
48 } | |
49 | |
50 Node* enclosingShadowHostForEnd(const PositionInComposedTree& position) | |
51 { | |
52 Node* node = position.nodeAsRangeLastNode(); | |
53 if (!node) | |
54 return nullptr; | |
55 Node* shadowHost = enclosingShadowHost(node); | |
56 if (!shadowHost) | |
57 return nullptr; | |
58 if (!isEnclosedBy(position, *shadowHost)) | |
59 return nullptr; | |
60 return isSelectionBoundary(*shadowHost) ? shadowHost : nullptr; | |
61 } | |
62 | |
63 PositionInComposedTree adjustPositionInComposedTreeForStart(const PositionInComp osedTree& position, Node* shadowHost) | |
64 { | |
65 if (isEnclosedBy(position, *shadowHost)) { | |
66 if (position.isBeforeChildren()) | |
67 return PositionInComposedTree::beforeNode(shadowHost); | |
68 return PositionInComposedTree::afterNode(shadowHost); | |
69 } | |
70 | |
71 // We use |firstChild|'s after instead of beforeAllChildren for backward | |
72 // compatibility. The positions are same but the anchors would be different, | |
73 // and selection painting uses anchor nodes. | |
74 if (Node* firstChild = ComposedTreeTraversal::firstChild(*shadowHost)) | |
75 return PositionInComposedTree::beforeNode(firstChild); | |
76 return PositionInComposedTree(); | |
77 } | |
78 | |
79 Position adjustPositionForEnd(const Position& currentPosition, Node* startContai nerNode) | |
80 { | |
81 TreeScope& treeScope = startContainerNode->treeScope(); | |
82 | |
83 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
84 | |
85 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo ntainerNode())) { | |
86 if (ancestor->contains(startContainerNode)) | |
87 return positionAfterNode(ancestor); | |
88 return positionBeforeNode(ancestor); | |
89 } | |
90 | |
91 if (Node* lastChild = treeScope.rootNode().lastChild()) | |
92 return positionAfterNode(lastChild); | |
93 | |
94 return Position(); | |
95 } | |
96 | |
97 PositionInComposedTree adjustPositionInComposedTreeForEnd(const PositionInCompos edTree& position, Node* shadowHost) | |
98 { | |
99 if (isEnclosedBy(position, *shadowHost)) { | |
100 if (position.isAfterChildren()) | |
101 return PositionInComposedTree::afterNode(shadowHost); | |
102 return PositionInComposedTree::beforeNode(shadowHost); | |
103 } | |
104 | |
105 // We use |lastChild|'s after instead of afterAllChildren for backward | |
106 // compatibility. The positions are same but the anchors would be different, | |
107 // and selection painting uses anchor nodes. | |
108 if (Node* lastChild = ComposedTreeTraversal::lastChild(*shadowHost)) | |
109 return PositionInComposedTree::afterNode(lastChild); | |
110 return PositionInComposedTree(); | |
111 } | |
112 | |
113 Position adjustPositionForStart(const Position& currentPosition, Node* endContai nerNode) | |
114 { | |
115 TreeScope& treeScope = endContainerNode->treeScope(); | |
116 | |
117 ASSERT(currentPosition.computeContainerNode()->treeScope() != treeScope); | |
118 | |
119 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.computeCo ntainerNode())) { | |
120 if (ancestor->contains(endContainerNode)) | |
121 return positionBeforeNode(ancestor); | |
122 return positionAfterNode(ancestor); | |
123 } | |
124 | |
125 if (Node* firstChild = treeScope.rootNode().firstChild()) | |
126 return positionBeforeNode(firstChild); | |
127 | |
128 return Position(); | |
129 } | |
130 | |
131 } // namespace | |
132 | |
133 // Updates |selectionInComposedTree| to match with |selection|. | |
134 void SelectionAdjuster::adjustSelectionInComposedTree(VisibleSelectionInComposed Tree* selectionInComposedTree, const VisibleSelection& selection) | |
135 { | |
136 if (selection.isNone()) { | |
137 *selectionInComposedTree = VisibleSelectionInComposedTree(); | |
138 return; | |
139 } | |
140 | |
141 const PositionInComposedTree& base = toPositionInComposedTree(selection.base ()); | |
142 const PositionInComposedTree& extent = toPositionInComposedTree(selection.ex tent()); | |
143 const PositionInComposedTree& position1 = toPositionInComposedTree(selection .start()); | |
144 const PositionInComposedTree& position2 = toPositionInComposedTree(selection .end()); | |
145 position1.anchorNode()->updateDistribution(); | |
146 position2.anchorNode()->updateDistribution(); | |
147 selectionInComposedTree->m_base = base; | |
148 selectionInComposedTree->m_extent = extent; | |
149 selectionInComposedTree->m_affinity = selection.m_affinity; | |
150 selectionInComposedTree->m_isDirectional = selection.m_isDirectional; | |
151 selectionInComposedTree->m_baseIsFirst = base.isNull() || base.compareTo(ext ent) <= 0; | |
152 if (position1.compareTo(position2) <= 0) { | |
153 selectionInComposedTree->m_start = position1; | |
154 selectionInComposedTree->m_end = position2; | |
155 } else { | |
156 selectionInComposedTree->m_start = position2; | |
157 selectionInComposedTree->m_end = position1; | |
158 } | |
159 selectionInComposedTree->updateSelectionType(); | |
160 } | |
161 | |
162 static bool isCrossingShadowBoundaries(const VisibleSelectionInComposedTree& sel ection) | |
163 { | |
164 if (!selection.isRange()) | |
165 return false; | |
166 TreeScope& treeScope = selection.base().anchorNode()->treeScope(); | |
167 return selection.extent().anchorNode()->treeScope() != treeScope | |
168 || selection.start().anchorNode()->treeScope() != treeScope | |
169 || selection.end().anchorNode()->treeScope() != treeScope; | |
170 } | |
171 | |
172 void SelectionAdjuster::adjustSelectionInDOMTree(VisibleSelection* selection, co nst VisibleSelectionInComposedTree& selectionInComposedTree) | |
173 { | |
174 if (selectionInComposedTree.isNone()) { | |
175 *selection = VisibleSelection(); | |
176 return; | |
177 } | |
178 | |
179 const Position& base = toPositionInDOMTree(selectionInComposedTree.base()); | |
180 const Position& extent = toPositionInDOMTree(selectionInComposedTree.extent( )); | |
181 | |
182 if (isCrossingShadowBoundaries(selectionInComposedTree)) { | |
183 *selection = VisibleSelection(base, extent); | |
184 return; | |
185 } | |
186 | |
187 const Position& position1 = toPositionInDOMTree(selectionInComposedTree.star t()); | |
188 const Position& position2 = toPositionInDOMTree(selectionInComposedTree.end( )); | |
189 selection->m_base = base; | |
190 selection->m_extent = extent; | |
191 selection->m_affinity = selectionInComposedTree.m_affinity; | |
192 selection->m_isDirectional = selectionInComposedTree.m_isDirectional; | |
193 selection->m_baseIsFirst = base.isNull() || base.compareTo(extent) <= 0; | |
194 if (position1.compareTo(position2) <= 0) { | |
195 selection->m_start = position1; | |
196 selection->m_end = position2; | |
197 } else { | |
198 selection->m_start = position2; | |
199 selection->m_end = position1; | |
200 } | |
201 selection->updateSelectionType(); | |
202 } | |
203 | |
204 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe lection* selection) | |
205 { | |
206 // Note: |m_selectionType| isn't computed yet. | |
207 ASSERT(selection->base().isNotNull()); | |
208 ASSERT(selection->extent().isNotNull()); | |
209 ASSERT(selection->start().isNotNull()); | |
210 ASSERT(selection->end().isNotNull()); | |
211 | |
212 // TODO(hajimehoshi): Checking treeScope is wrong when a node is | |
213 // distributed, but we leave it as it is for backward compatibility. | |
214 if (selection->start().anchorNode()->treeScope() == selection->end().anchorN ode()->treeScope()) | |
215 return; | |
216 | |
217 if (selection->isBaseFirst()) { | |
218 const Position& newEnd = adjustPositionForEnd(selection->end(), selectio n->start().computeContainerNode()); | |
219 selection->m_extent = newEnd; | |
220 selection->m_end = newEnd; | |
221 return; | |
222 } | |
223 | |
224 const Position& newStart = adjustPositionForStart(selection->start(), select ion->end().computeContainerNode()); | |
225 selection->m_extent = newStart; | |
226 selection->m_start = newStart; | |
227 } | |
228 | |
229 // This function is called twice. The first is called when |m_start| and |m_end| | |
230 // or |m_extent| are same, and the second when |m_start| and |m_end| are changed | |
231 // after downstream/upstream. | |
232 void SelectionAdjuster::adjustSelectionToAvoidCrossingShadowBoundaries(VisibleSe lectionInComposedTree* selection) | |
233 { | |
234 Node* const shadowHostStart = enclosingShadowHostForStart(selection->start() ); | |
235 Node* const shadowHostEnd = enclosingShadowHostForEnd(selection->end()); | |
236 if (shadowHostStart == shadowHostEnd) | |
237 return; | |
238 | |
239 if (selection->isBaseFirst()) { | |
240 Node* const shadowHost = shadowHostStart ? shadowHostStart : shadowHostE nd; | |
241 const PositionInComposedTree& newEnd = adjustPositionInComposedTreeForEn d(selection->end(), shadowHost); | |
242 selection->m_extent = newEnd; | |
243 selection->m_end = newEnd; | |
244 return; | |
245 } | |
246 Node* const shadowHost = shadowHostEnd ? shadowHostEnd : shadowHostStart; | |
247 const PositionInComposedTree& newStart = adjustPositionInComposedTreeForStar t(selection->start(), shadowHost); | |
248 selection->m_extent = newStart; | |
249 selection->m_start = newStart; | |
250 } | |
251 | |
252 } // namespace blink | |
OLD | NEW |