OLD | NEW |
---|---|
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 |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
103 frame.page()->focusController().focusedFrame() == frame), | 103 frame.page()->focusController().focusedFrame() == frame), |
104 m_frameCaret(new FrameCaret(frame, *m_selectionEditor)) {} | 104 m_frameCaret(new FrameCaret(frame, *m_selectionEditor)) {} |
105 | 105 |
106 FrameSelection::~FrameSelection() {} | 106 FrameSelection::~FrameSelection() {} |
107 | 107 |
108 const DisplayItemClient& FrameSelection::caretDisplayItemClientForTesting() | 108 const DisplayItemClient& FrameSelection::caretDisplayItemClientForTesting() |
109 const { | 109 const { |
110 return m_frameCaret->displayItemClient(); | 110 return m_frameCaret->displayItemClient(); |
111 } | 111 } |
112 | 112 |
113 const Document& FrameSelection::document() const { | 113 Document& FrameSelection::document() const { |
114 DCHECK(lifecycleContext()); | 114 DCHECK(lifecycleContext()); |
115 return *lifecycleContext(); | 115 return *lifecycleContext(); |
116 } | 116 } |
117 | 117 |
118 Document& FrameSelection::document() { | 118 bool FrameSelection::isHandleVisible() const { |
119 DCHECK(lifecycleContext()); | 119 return selectionInDOMTree().isHandleVisible(); |
120 return *lifecycleContext(); | |
121 } | 120 } |
122 | 121 |
122 // TODO(yosin): We should replace // |visibleSelection<EditingStrategy>()| to | |
tkent
2017/02/10 08:47:40
nit: Looks the second "//" is unnecessary.
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
123 // |computeVisibleSelectionInDOMTree()|. | |
123 // TODO(yosin): To avoid undefined symbols in clang, we explicitly | 124 // TODO(yosin): To avoid undefined symbols in clang, we explicitly |
124 // have specialized version of |FrameSelection::visibleSelection<Strategy>| | 125 // have specialized version of |FrameSelection::visibleSelection<Strategy>| |
125 // before |FrameSelection::selection()| which refers this. | 126 // before |FrameSelection::selection()| which refers this. |
126 template <> | 127 template <> |
127 const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>() | 128 const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>() |
128 const { | 129 const { |
129 return m_selectionEditor->visibleSelection<EditingStrategy>(); | 130 return m_selectionEditor->visibleSelection<EditingStrategy>(); |
130 } | 131 } |
131 | 132 |
133 // TODO(yosin): We should replace | |
134 // |visibleSelection<EditingInFlatTreeStrategy>()| to | |
tkent
2017/02/10 08:47:41
nit |to| -> |with| or |by|
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
135 // |computeVisibleSelectionInFlatTree()|. | |
132 template <> | 136 template <> |
133 const VisibleSelectionInFlatTree& | 137 const VisibleSelectionInFlatTree& |
134 FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const { | 138 FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const { |
135 return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>(); | 139 return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>(); |
136 } | 140 } |
137 | 141 |
142 const VisibleSelection& FrameSelection::computeVisibleSelectionInDOMTree() | |
143 const { | |
144 return m_selectionEditor->computeVisibleSelectionInDOMTree(); | |
145 } | |
146 | |
147 const VisibleSelectionInFlatTree& | |
148 FrameSelection::computeVisibleSelectionInFlatTree() const { | |
149 return m_selectionEditor->computeVisibleSelectionInFlatTree(); | |
150 } | |
151 | |
152 const SelectionInDOMTree& FrameSelection::selectionInDOMTree() const { | |
153 return m_selectionEditor->selectionInDOMTree(); | |
154 } | |
155 | |
138 Element* FrameSelection::rootEditableElementOrDocumentElement() const { | 156 Element* FrameSelection::rootEditableElementOrDocumentElement() const { |
139 Element* selectionRoot = selection().rootEditableElement(); | 157 Element* selectionRoot = selection().rootEditableElement(); |
140 return selectionRoot ? selectionRoot : document().documentElement(); | 158 return selectionRoot ? selectionRoot : document().documentElement(); |
141 } | 159 } |
142 | 160 |
143 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { | 161 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { |
144 Element* selectionRoot = selection().rootEditableElement(); | 162 Element* selectionRoot = selection().rootEditableElement(); |
145 if (selectionRoot) | 163 if (selectionRoot) |
146 return selectionRoot; | 164 return selectionRoot; |
147 | 165 |
148 Node* node = selection().base().computeContainerNode(); | 166 Node* node = selection().base().computeContainerNode(); |
149 return node ? &node->treeScope().rootNode() : 0; | 167 return node ? &node->treeScope().rootNode() : 0; |
150 } | 168 } |
151 | 169 |
170 // TODO(yosin): We should rename |FrameSelection::selection()| to | |
171 // |selectionDeprecated()|. | |
152 const VisibleSelection& FrameSelection::selection() const { | 172 const VisibleSelection& FrameSelection::selection() const { |
153 return visibleSelection<EditingStrategy>(); | 173 // TODO(yosin): We should hoist updateStyleAndLayoutIgnorePendingStylesheets |
174 // to caller. See http://crbug.com/590369 for more details. | |
175 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
176 return computeVisibleSelectionInDOMTree(); | |
154 } | 177 } |
155 | 178 |
156 const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const { | 179 const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const { |
157 return visibleSelection<EditingInFlatTreeStrategy>(); | 180 return visibleSelection<EditingInFlatTreeStrategy>(); |
158 } | 181 } |
159 | 182 |
160 void FrameSelection::moveCaretSelection(const IntPoint& point) { | 183 void FrameSelection::moveCaretSelection(const IntPoint& point) { |
161 DCHECK(!document().needsLayoutTreeUpdate()); | 184 DCHECK(!document().needsLayoutTreeUpdate()); |
162 | 185 |
163 Element* const editable = rootEditableElement(); | 186 Element* const editable = rootEditableElement(); |
164 if (!editable) | 187 if (!editable) |
165 return; | 188 return; |
166 | 189 |
167 const VisiblePosition position = | 190 const VisiblePosition position = |
168 visiblePositionForContentsPoint(point, frame()); | 191 visiblePositionForContentsPoint(point, frame()); |
169 SelectionInDOMTree::Builder builder; | 192 SelectionInDOMTree::Builder builder; |
170 builder.setIsDirectional(selection().isDirectional()); | 193 builder.setIsDirectional(selection().isDirectional()); |
171 builder.setIsHandleVisible(true); | 194 builder.setIsHandleVisible(true); |
172 if (position.isNotNull()) | 195 if (position.isNotNull()) |
173 builder.collapse(position.toPositionWithAffinity()); | 196 builder.collapse(position.toPositionWithAffinity()); |
174 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); | 197 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); |
175 } | 198 } |
176 | 199 |
177 template <typename Strategy> | 200 void FrameSelection::setSelection(const SelectionInDOMTree& passedSelection, |
178 void FrameSelection::setSelectionAlgorithm( | 201 SetSelectionOptions options, |
179 const VisibleSelectionTemplate<Strategy>& newSelection, | 202 CursorAlignOnScroll align, |
180 HandleVisibility handleVisibility, | 203 TextGranularity granularity) { |
181 SetSelectionOptions options, | |
182 CursorAlignOnScroll align, | |
183 TextGranularity granularity) { | |
184 DCHECK(isAvailable()); | 204 DCHECK(isAvailable()); |
185 DCHECK(newSelection.isValidFor(document())); | 205 passedSelection.assertValidFor(document()); |
186 const Document& currentDocument = document(); | 206 |
207 SelectionInDOMTree::Builder builder(passedSelection); | |
208 if (shouldAlwaysUseDirectionalSelection(m_frame)) | |
209 builder.setIsDirectional(true); | |
210 SelectionInDOMTree newSelection = builder.build(); | |
187 if (m_granularityStrategy && | 211 if (m_granularityStrategy && |
188 (options & FrameSelection::DoNotClearStrategy) == 0) | 212 (options & FrameSelection::DoNotClearStrategy) == 0) |
189 m_granularityStrategy->Clear(); | 213 m_granularityStrategy->Clear(); |
190 bool closeTyping = options & CloseTyping; | 214 bool closeTyping = options & CloseTyping; |
191 bool shouldClearTypingStyle = options & ClearTypingStyle; | 215 bool shouldClearTypingStyle = options & ClearTypingStyle; |
192 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | |
193 | |
194 // TODO(editing-dev): We should rename variable |s| to another name to avoid | |
195 // using one letter variable name. | |
196 VisibleSelectionTemplate<Strategy> s = newSelection; | |
197 if (shouldAlwaysUseDirectionalSelection(m_frame)) | |
198 s.setIsDirectional(true); | |
199 | |
200 m_granularity = granularity; | 216 m_granularity = granularity; |
201 | 217 |
202 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 218 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
203 // |Editor| class. | 219 // |Editor| class. |
204 if (closeTyping) | 220 if (closeTyping) |
205 TypingCommand::closeTyping(m_frame); | 221 TypingCommand::closeTyping(m_frame); |
206 | 222 |
207 if (shouldClearTypingStyle) | 223 if (shouldClearTypingStyle) |
208 m_frame->editor().clearTypingStyle(); | 224 m_frame->editor().clearTypingStyle(); |
209 | 225 |
210 if (m_selectionEditor->visibleSelection<Strategy>() == s && | 226 const SelectionInDOMTree oldSelectionInDOMTree = |
211 m_handleVisibility == handleVisibility) { | 227 m_selectionEditor->selectionInDOMTree(); |
212 // Even if selection was not changed, selection offsets may have been | 228 if (oldSelectionInDOMTree == newSelection) |
213 // changed. | |
214 notifyLayoutObjectOfSelectionChange(userTriggered); | |
215 return; | 229 return; |
216 } | 230 m_selectionEditor->setSelection(newSelection); |
217 | |
218 const VisibleSelectionTemplate<Strategy> oldSelection = | |
219 visibleSelection<Strategy>(); | |
220 const Position& oldSelectionStart = selection().start(); | |
221 | |
222 m_handleVisibility = handleVisibility; | |
223 m_selectionEditor->setVisibleSelection(s, options); | |
224 scheduleVisualUpdateForPaintInvalidationIfNeeded(); | 231 scheduleVisualUpdateForPaintInvalidationIfNeeded(); |
225 | 232 |
226 if (!s.isNone() && !(options & DoNotSetFocus)) { | 233 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets |
234 // needs to be audited. see http://crbug.com/590369 for more details. | |
235 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
236 | |
237 const Document& currentDocument = document(); | |
238 // TODO(yosin): We should get rid of unsued |options| for | |
239 // |Editor::respondToChangedSelection()|. | |
240 // Note: Since, setting focus can modify DOM tree, we should use | |
241 // |oldSelection| before setting focus | |
242 m_frame->editor().respondToChangedSelection( | |
243 createVisibleSelection(oldSelectionInDOMTree).start(), options); | |
244 DCHECK_EQ(currentDocument, document()); | |
245 | |
246 if (!selection().isNone() && !(options & DoNotSetFocus)) { | |
227 setFocusedNodeIfNeeded(); | 247 setFocusedNodeIfNeeded(); |
228 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and | 248 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and |
229 // "FocusIn", |m_frame| may associate to another document. | 249 // "FocusIn", |m_frame| may associate to another document. |
230 if (!isAvailable() || document() != currentDocument) { | 250 if (!isAvailable() || document() != currentDocument) { |
231 // Once we get test case to reach here, we should change this | 251 // Once we get test case to reach here, we should change this |
232 // if-statement to |DCHECK()|. | 252 // if-statement to |DCHECK()|. |
233 NOTREACHED(); | 253 NOTREACHED(); |
234 return; | 254 return; |
235 } | 255 } |
236 } | 256 } |
237 | 257 |
238 if (!(options & DoNotUpdateAppearance)) { | 258 if (!(options & DoNotUpdateAppearance)) { |
239 m_frameCaret->stopCaretBlinkTimer(); | 259 m_frameCaret->stopCaretBlinkTimer(); |
240 updateAppearance(); | 260 updateAppearance(); |
241 } | 261 } |
242 | 262 |
243 // Always clear the x position used for vertical arrow navigation. | 263 // Always clear the x position used for vertical arrow navigation. |
244 // It will be restored by the vertical arrow navigation code if necessary. | 264 // It will be restored by the vertical arrow navigation code if necessary. |
245 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); | 265 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); |
266 // TODO(yosin): Can we move this to at end of this function? | |
246 // This may dispatch a synchronous focus-related events. | 267 // This may dispatch a synchronous focus-related events. |
247 selectFrameElementInParentIfFullySelected(); | 268 selectFrameElementInParentIfFullySelected(); |
248 if (!isAvailable() || document() != currentDocument) { | 269 if (!isAvailable() || document() != currentDocument) { |
249 // editing/selection/selectallchildren-crash.html and | 270 // editing/selection/selectallchildren-crash.html and |
250 // editing/selection/longpress-selection-in-iframe-removed-crash.html | 271 // editing/selection/longpress-selection-in-iframe-removed-crash.html |
251 // reach here. | 272 // reach here. |
252 return; | 273 return; |
253 } | 274 } |
275 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | |
254 notifyLayoutObjectOfSelectionChange(userTriggered); | 276 notifyLayoutObjectOfSelectionChange(userTriggered); |
255 // If the selections are same in the DOM tree but not in the flat tree, | |
256 // don't fire events. For example, if the selection crosses shadow tree | |
257 // boundary, selection for the DOM tree is shrunk while that for the | |
258 // flat tree is not. Additionally, this case occurs in some edge cases. | |
259 // See also: editing/pasteboard/4076267-3.html | |
260 if (oldSelection == m_selectionEditor->visibleSelection<Strategy>()) | |
261 return; | |
262 | |
263 m_frame->editor().respondToChangedSelection(oldSelectionStart, options); | |
264 if (userTriggered == UserTriggered) { | 277 if (userTriggered == UserTriggered) { |
265 ScrollAlignment alignment; | 278 ScrollAlignment alignment; |
266 | 279 |
267 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) | 280 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) |
268 alignment = (align == CursorAlignOnScroll::Always) | 281 alignment = (align == CursorAlignOnScroll::Always) |
269 ? ScrollAlignment::alignCenterAlways | 282 ? ScrollAlignment::alignCenterAlways |
270 : ScrollAlignment::alignCenterIfNeeded; | 283 : ScrollAlignment::alignCenterIfNeeded; |
271 else | 284 else |
272 alignment = (align == CursorAlignOnScroll::Always) | 285 alignment = (align == CursorAlignOnScroll::Always) |
273 ? ScrollAlignment::alignTopAlways | 286 ? ScrollAlignment::alignTopAlways |
274 : ScrollAlignment::alignToEdgeIfNeeded; | 287 : ScrollAlignment::alignToEdgeIfNeeded; |
275 | 288 |
289 // TODO(editing-dev): The use of | |
290 // updateStyleAndLayoutIgnorePendingStylesheets | |
291 // needs to be audited. See http://crbug.com/590369 for more details. | |
292 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
293 | |
276 revealSelection(alignment, RevealExtent); | 294 revealSelection(alignment, RevealExtent); |
277 } | 295 } |
278 | 296 |
279 notifyAccessibilityForSelectionChange(); | 297 notifyAccessibilityForSelectionChange(); |
280 notifyCompositorForSelectionChange(); | 298 notifyCompositorForSelectionChange(); |
281 notifyEventHandlerForSelectionChange(); | 299 notifyEventHandlerForSelectionChange(); |
282 m_frame->domWindow()->enqueueDocumentEvent( | 300 m_frame->domWindow()->enqueueDocumentEvent( |
283 Event::create(EventTypeNames::selectionchange)); | 301 Event::create(EventTypeNames::selectionchange)); |
284 } | 302 } |
285 | 303 |
286 // TODO(yosin): We will make |selectionInDOMTree| version of |SetSelection()| | |
287 // as primary function instead of wrapper. | |
288 void FrameSelection::setSelection(const SelectionInDOMTree& newSelection, | |
289 SetSelectionOptions options, | |
290 CursorAlignOnScroll align, | |
291 TextGranularity granularity) { | |
292 if (!newSelection.isNone()) { | |
293 // TODO(editing-dev): The use of | |
294 // updateStyleAndLayoutIgnorePendingStylesheets | |
295 // needs to be audited. See http://crbug.com/590369 for more details. | |
296 newSelection.base() | |
297 .document() | |
298 ->updateStyleAndLayoutIgnorePendingStylesheets(); | |
299 } | |
300 setSelection(createVisibleSelection(newSelection), | |
301 newSelection.isHandleVisible() ? HandleVisibility::Visible | |
302 : HandleVisibility::NotVisible, | |
303 options, align, granularity); | |
304 } | |
305 | |
306 // TODO(yosin): We will make |selectionInFlatTree| version of |SetSelection()| | |
307 // as primary function instead of wrapper. | |
308 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, | 304 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, |
309 SetSelectionOptions options, | 305 SetSelectionOptions options, |
310 CursorAlignOnScroll align, | 306 CursorAlignOnScroll align, |
311 TextGranularity granularity) { | 307 TextGranularity granularity) { |
312 if (!newSelection.isNone()) { | 308 newSelection.assertValidFor(document()); |
313 // TODO(editing-dev): The use of | 309 SelectionInDOMTree::Builder builder; |
314 // updateStyleAndLayoutIgnorePendingStylesheets | 310 builder.setAffinity(newSelection.affinity()) |
315 // needs to be audited. See http://crbug.com/590369 for more details. | 311 .setBaseAndExtent(toPositionInDOMTree(newSelection.base()), |
316 newSelection.base() | 312 toPositionInDOMTree(newSelection.extent())) |
317 .document() | 313 .setGranularity(newSelection.granularity()) |
318 ->updateStyleAndLayoutIgnorePendingStylesheets(); | 314 .setIsDirectional(newSelection.isDirectional()) |
319 } | 315 .setIsHandleVisible(newSelection.isHandleVisible()) |
320 setSelection(createVisibleSelection(newSelection), | 316 .setHasTrailingWhitespace(newSelection.hasTrailingWhitespace()); |
321 newSelection.isHandleVisible() ? HandleVisibility::Visible | 317 return setSelection(builder.build(), options, align, granularity); |
322 : HandleVisibility::NotVisible, | |
323 options, align, granularity); | |
324 } | 318 } |
325 | 319 |
326 void FrameSelection::setSelection(const VisibleSelection& newSelection, | 320 void FrameSelection::setSelection(const VisibleSelection& newSelection, |
327 HandleVisibility handleVisibility, | 321 HandleVisibility handleVisibility, |
328 SetSelectionOptions options, | 322 SetSelectionOptions options, |
329 CursorAlignOnScroll align, | 323 CursorAlignOnScroll align, |
330 TextGranularity granularity) { | 324 TextGranularity granularity) { |
331 setSelectionAlgorithm<EditingStrategy>(newSelection, handleVisibility, | 325 setSelection( |
332 options, align, granularity); | 326 SelectionInDOMTree::Builder(newSelection.asSelection()) |
327 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) | |
328 .build(), | |
329 options, align, granularity); | |
333 } | 330 } |
334 | 331 |
335 void FrameSelection::setSelection(const VisibleSelection& newSelection, | 332 void FrameSelection::setSelection(const VisibleSelection& newSelection, |
336 SetSelectionOptions options) { | 333 SetSelectionOptions options) { |
337 setSelection(newSelection, HandleVisibility::NotVisible, options); | 334 setSelection(newSelection.asSelection(), options); |
338 } | 335 } |
339 | 336 |
340 void FrameSelection::setSelection( | 337 void FrameSelection::setSelection( |
341 const VisibleSelectionInFlatTree& newSelection, | 338 const VisibleSelectionInFlatTree& newSelection, |
342 HandleVisibility handleVisibility, | 339 HandleVisibility handleVisibility, |
343 SetSelectionOptions options, | 340 SetSelectionOptions options, |
344 CursorAlignOnScroll align, | 341 CursorAlignOnScroll align, |
345 TextGranularity granularity) { | 342 TextGranularity granularity) { |
346 setSelectionAlgorithm<EditingInFlatTreeStrategy>( | 343 setSelection( |
347 newSelection, handleVisibility, options, align, granularity); | 344 SelectionInFlatTree::Builder(newSelection.asSelection()) |
345 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) | |
346 .build(), | |
347 options, align, granularity); | |
348 } | 348 } |
349 | 349 |
350 void FrameSelection::setSelection( | 350 void FrameSelection::setSelection( |
351 const VisibleSelectionInFlatTree& newSelection, | 351 const VisibleSelectionInFlatTree& newSelection, |
352 SetSelectionOptions options) { | 352 SetSelectionOptions options) { |
353 setSelection(newSelection, HandleVisibility::NotVisible, options); | 353 setSelection(newSelection.asSelection(), options); |
354 } | 354 } |
355 | 355 |
356 static bool removingNodeRemovesPosition(Node& node, const Position& position) { | 356 // TODO(yosin): We should remove |computePositionForChildrenRemoval()| to |
tkent
2017/02/10 08:47:40
nit: remove -> move
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
357 if (!position.anchorNode()) | 357 // "SelectionEditor.cpp" since it used only in |
358 return false; | 358 // |SelectionEditor::nodeChildrenWillBeRemoved()|. |
359 | |
360 if (position.anchorNode() == node) | |
361 return true; | |
362 | |
363 if (!node.isElementNode()) | |
364 return false; | |
365 | |
366 Element& element = toElement(node); | |
367 return element.isShadowIncludingInclusiveAncestorOf(position.anchorNode()); | |
368 } | |
369 | |
370 static Position computePositionForChildrenRemoval(const Position& position, | 359 static Position computePositionForChildrenRemoval(const Position& position, |
371 ContainerNode& container) { | 360 ContainerNode& container) { |
372 Node* node = position.computeContainerNode(); | 361 Node* node = position.computeContainerNode(); |
373 if (container.containsIncludingHostElements(*node)) | 362 if (container.containsIncludingHostElements(*node)) |
374 return Position::firstPositionInNode(&container); | 363 return Position::firstPositionInNode(&container); |
375 return position; | 364 return position; |
376 } | 365 } |
377 | 366 |
378 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { | 367 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { |
379 if (isNone() || !container.inActiveDocument()) | 368 if (!container.inActiveDocument()) |
380 return; | 369 return; |
381 const Position& oldStart = selection().start(); | |
382 const Position& newStart = | |
383 computePositionForChildrenRemoval(oldStart, container); | |
384 const Position& oldEnd = selection().end(); | |
385 const Position& newEnd = computePositionForChildrenRemoval(oldEnd, container); | |
386 const Position& oldBase = selection().base(); | |
387 const Position& newBase = | |
388 computePositionForChildrenRemoval(oldBase, container); | |
389 const Position& oldExtent = selection().extent(); | |
390 const Position& newExtent = | |
391 computePositionForChildrenRemoval(oldExtent, container); | |
392 if (newStart == oldStart && newEnd == oldEnd && newBase == oldBase && | |
393 newExtent == oldExtent) | |
394 return; | |
395 if (selection().isBaseFirst()) | |
396 m_selectionEditor->setWithoutValidation(newStart, newEnd); | |
397 else | |
398 m_selectionEditor->setWithoutValidation(newEnd, newStart); | |
399 if (document().isRunningExecCommand()) | |
400 return; | |
401 TypingCommand::closeTyping(m_frame); | |
402 } | |
403 | |
404 void FrameSelection::nodeWillBeRemoved(Node& node) { | |
405 // There can't be a selection inside a fragment, so if a fragment's node is | |
406 // being removed, the selection in the document that created the fragment | |
407 // needs no adjustment. | |
408 if (isNone() || !node.inActiveDocument()) | |
409 return; | |
410 | |
411 respondToNodeModification( | |
412 node, removingNodeRemovesPosition(node, selection().base()), | |
413 removingNodeRemovesPosition(node, selection().extent()), | |
414 removingNodeRemovesPosition(node, selection().start()), | |
415 removingNodeRemovesPosition(node, selection().end())); | |
416 } | |
417 | |
418 static SelectionState selectionStateOf(const Node& node) { | |
419 const LayoutObject* layoutObject = node.layoutObject(); | |
420 if (!layoutObject) | |
421 return SelectionNone; | |
422 return layoutObject->getSelectionState(); | |
423 } | |
424 | |
425 void FrameSelection::respondToNodeModification(Node& node, | |
426 bool baseRemoved, | |
427 bool extentRemoved, | |
428 bool startRemoved, | |
429 bool endRemoved) { | |
430 DCHECK(node.document().isActive()) << node; | |
431 | |
432 bool clearLayoutTreeSelection = false; | |
433 bool clearDOMTreeSelection = false; | |
434 | |
435 if (startRemoved || endRemoved) { | |
436 Position start = selection().start(); | |
437 Position end = selection().end(); | |
438 if (startRemoved) | |
439 updatePositionForNodeRemoval(start, node); | |
440 if (endRemoved) | |
441 updatePositionForNodeRemoval(end, node); | |
442 | |
443 if (Position::commonAncestorTreeScope(start, end) && start.isNotNull() && | |
444 end.isNotNull()) { | |
445 if (selection().isBaseFirst()) | |
446 m_selectionEditor->setWithoutValidation(start, end); | |
447 else | |
448 m_selectionEditor->setWithoutValidation(end, start); | |
449 } else { | |
450 clearDOMTreeSelection = true; | |
451 } | |
452 | |
453 clearLayoutTreeSelection = true; | |
454 } else if (baseRemoved || extentRemoved) { | |
455 // The base and/or extent are about to be removed, but the start and end | |
456 // aren't. Change the base and extent to the start and end, but don't | |
457 // re-validate the selection, since doing so could move the start and end | |
458 // into the node that is about to be removed. | |
459 if (selection().isBaseFirst()) | |
460 m_selectionEditor->setWithoutValidation(selection().start(), | |
461 selection().end()); | |
462 else | |
463 m_selectionEditor->setWithoutValidation(selection().end(), | |
464 selection().start()); | |
465 } else if (selectionStateOf(node) != SelectionNone) { | |
466 // When node to be removed is part of selection, we invalidate | |
467 // selection to paint again. | |
468 // TODO(yosin): We should paint changed area only rather than whole | |
469 // selected range. | |
470 clearLayoutTreeSelection = true; | |
471 } | |
472 | |
473 if (clearLayoutTreeSelection) | |
474 selection().start().document()->layoutViewItem().clearSelection(); | |
475 | |
476 if (clearDOMTreeSelection) | |
477 setSelection(SelectionInDOMTree(), DoNotSetFocus); | |
478 | |
479 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 370 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
480 // |Editor| class. | 371 // |Editor| class. |
481 if (!document().isRunningExecCommand()) | 372 if (!document().isRunningExecCommand()) |
482 TypingCommand::closeTyping(m_frame); | 373 TypingCommand::closeTyping(m_frame); |
483 } | 374 } |
484 | 375 |
376 // TODO(yosin): We should move |SelectionEditor::nodeChildrenWillBeRemoved()| | |
377 // to "SelectionEditor.cpp". | |
378 void SelectionEditor::nodeChildrenWillBeRemoved(ContainerNode& container) { | |
379 if (m_selection.isNone()) | |
380 return; | |
381 const Position oldBase = m_selection.m_base; | |
382 const Position oldExtent = m_selection.m_extent; | |
383 const Position& newBase = | |
384 computePositionForChildrenRemoval(oldBase, container); | |
385 const Position& newExtent = | |
386 computePositionForChildrenRemoval(oldExtent, container); | |
387 if (newBase == oldBase && newExtent == oldExtent) | |
388 return; | |
389 m_selection = SelectionInDOMTree::Builder() | |
390 .setBaseAndExtent(newBase, newExtent) | |
391 .build(); | |
392 markCacheDirty(); | |
393 } | |
394 | |
395 void FrameSelection::nodeWillBeRemoved(Node& node) { | |
396 // There can't be a selection inside a fragment, so if a fragment's node is | |
397 // being removed, | |
tkent
2017/02/10 08:47:40
nit: Text wrapping looks weird.
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
398 // the selection in the document that created the fragment needs no | |
399 // adjustment. | |
400 if (!node.inActiveDocument()) | |
401 return; | |
402 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | |
403 // |Editor| class. | |
404 if (!document().isRunningExecCommand()) | |
405 TypingCommand::closeTyping(m_frame); | |
406 } | |
407 | |
408 // TODO(yosin): We should remove |updatePositionAfterAdoptingTextReplacement()| | |
tkent
2017/02/10 08:47:41
nit: remove -> move
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
409 // to // "SelectionEditor.cpp" since it used only in | |
tkent
2017/02/10 08:47:40
nit: the second '//' looks unnecessary.
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
410 // |SelectionEditor::didUpdateCharacterData()|. | |
485 static Position updatePositionAfterAdoptingTextReplacement( | 411 static Position updatePositionAfterAdoptingTextReplacement( |
486 const Position& position, | 412 const Position& position, |
487 CharacterData* node, | 413 CharacterData* node, |
488 unsigned offset, | 414 unsigned offset, |
489 unsigned oldLength, | 415 unsigned oldLength, |
490 unsigned newLength) { | 416 unsigned newLength) { |
491 if (!position.anchorNode() || position.anchorNode() != node || | 417 if (position.anchorNode() != node) |
492 !position.isOffsetInAnchor()) | |
493 return position; | 418 return position; |
494 | 419 |
420 if (position.isBeforeAnchor()) { | |
421 return updatePositionAfterAdoptingTextReplacement( | |
422 Position(node, 0), node, offset, oldLength, newLength); | |
423 } | |
424 if (position.isAfterAnchor()) { | |
425 return updatePositionAfterAdoptingTextReplacement( | |
426 Position(node, oldLength), node, offset, oldLength, newLength); | |
427 } | |
428 | |
495 // See: | 429 // See: |
496 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation | 430 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation |
497 DCHECK_GE(position.offsetInContainerNode(), 0); | 431 DCHECK_GE(position.offsetInContainerNode(), 0); |
498 unsigned positionOffset = | 432 unsigned positionOffset = |
499 static_cast<unsigned>(position.offsetInContainerNode()); | 433 static_cast<unsigned>(position.offsetInContainerNode()); |
500 // Replacing text can be viewed as a deletion followed by insertion. | 434 // Replacing text can be viewed as a deletion followed by insertion. |
501 if (positionOffset >= offset && positionOffset <= offset + oldLength) | 435 if (positionOffset >= offset && positionOffset <= offset + oldLength) |
502 positionOffset = offset; | 436 positionOffset = offset; |
503 | 437 |
504 // Adjust the offset if the position is after the end of the deleted contents | 438 // Adjust the offset if the position is after the end of the deleted contents |
505 // (positionOffset > offset + oldLength) to avoid having a stale offset. | 439 // (positionOffset > offset + oldLength) to avoid having a stale offset. |
506 if (positionOffset > offset + oldLength) | 440 if (positionOffset > offset + oldLength) |
507 positionOffset = positionOffset - oldLength + newLength; | 441 positionOffset = positionOffset - oldLength + newLength; |
508 | 442 |
509 // Due to case folding | 443 // Due to case folding |
510 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText | 444 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText |
511 // length may be different from Text length. A correct implementation would | 445 // length may be different from Text length. A correct implementation would |
512 // translate the LayoutText offset to a Text offset; this is just a safety | 446 // translate the LayoutText offset to a Text offset; this is just a safety |
513 // precaution to avoid offset values that run off the end of the Text. | 447 // precaution to avoid offset values that run off the end of the Text. |
514 if (positionOffset > node->length()) | 448 if (positionOffset > node->length()) |
515 positionOffset = node->length(); | 449 positionOffset = node->length(); |
516 | 450 |
517 // CharacterNode in VisibleSelection must be Text node, because Comment | 451 return Position(node, positionOffset); |
518 // and ProcessingInstruction node aren't visible. | |
519 return Position(toText(node), positionOffset); | |
520 } | 452 } |
521 | 453 |
522 void FrameSelection::didUpdateCharacterData(CharacterData* node, | 454 void SelectionEditor::didUpdateCharacterData(CharacterData* node, |
tkent
2017/02/10 08:47:40
nit: Add TODO comment about moving it to Selection
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
523 unsigned offset, | 455 unsigned offset, |
524 unsigned oldLength, | 456 unsigned oldLength, |
525 unsigned newLength) { | 457 unsigned newLength) { |
526 // The fragment check is a performance optimization. See | 458 // The fragment check is a performance optimization. See |
527 // http://trac.webkit.org/changeset/30062. | 459 // http://trac.webkit.org/changeset/30062. |
528 if (isNone() || !node || !node->isConnected()) | 460 if (m_selection.isNone() || !node || !node->isConnected()) { |
461 didFinishDOMMutation(); | |
529 return; | 462 return; |
530 | 463 } |
531 Position base = updatePositionAfterAdoptingTextReplacement( | 464 const Position& newBase = updatePositionAfterAdoptingTextReplacement( |
532 selection().base(), node, offset, oldLength, newLength); | 465 m_selection.m_base, node, offset, oldLength, newLength); |
533 Position extent = updatePositionAfterAdoptingTextReplacement( | 466 const Position& newExtent = updatePositionAfterAdoptingTextReplacement( |
534 selection().extent(), node, offset, oldLength, newLength); | 467 m_selection.m_extent, node, offset, oldLength, newLength); |
535 Position start = updatePositionAfterAdoptingTextReplacement( | 468 didFinishTextChange(newBase, newExtent); |
536 selection().start(), node, offset, oldLength, newLength); | |
537 Position end = updatePositionAfterAdoptingTextReplacement( | |
538 selection().end(), node, offset, oldLength, newLength); | |
539 updateSelectionIfNeeded(base, extent, start, end); | |
540 } | 469 } |
541 | 470 |
471 // TODO(yosin): We should remove |updatePostionAfterAdoptingTextNodesMerged()| | |
tkent
2017/02/10 08:47:41
nit: remove -> move
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
472 // to // "SelectionEditor.cpp" since it used only in | |
tkent
2017/02/10 08:47:40
nit: the second '//' looks unnecessary.
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
473 // |SelectionEditor::didMergeTextNodes()|. | |
542 static Position updatePostionAfterAdoptingTextNodesMerged( | 474 static Position updatePostionAfterAdoptingTextNodesMerged( |
543 const Position& position, | 475 const Position& position, |
544 const Text& oldNode, | 476 const Text& mergedNode, |
545 unsigned offset) { | 477 const NodeWithIndex& nodeToBeRemovedWithIndex, |
546 if (!position.anchorNode() || !position.isOffsetInAnchor()) | 478 unsigned oldLength) { |
547 return position; | 479 Node* const anchorNode = position.anchorNode(); |
548 | 480 const Node& nodeToBeRemoved = nodeToBeRemovedWithIndex.node(); |
549 DCHECK_GE(position.offsetInContainerNode(), 0); | 481 switch (position.anchorType()) { |
550 unsigned positionOffset = | 482 case PositionAnchorType::BeforeChildren: |
551 static_cast<unsigned>(position.offsetInContainerNode()); | 483 case PositionAnchorType::AfterChildren: |
552 | 484 return position; |
553 if (position.anchorNode() == &oldNode) | 485 case PositionAnchorType::BeforeAnchor: |
554 return Position(toText(oldNode.previousSibling()), positionOffset + offset); | 486 if (anchorNode == nodeToBeRemoved) |
555 | 487 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); |
556 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset) | 488 return position; |
557 return Position(toText(oldNode.previousSibling()), offset); | 489 case PositionAnchorType::AfterAnchor: |
558 | 490 if (anchorNode == nodeToBeRemoved) |
491 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); | |
492 if (anchorNode == mergedNode) | |
493 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
494 return position; | |
495 case PositionAnchorType::OffsetInAnchor: { | |
496 const int offset = position.offsetInContainerNode(); | |
497 if (anchorNode == nodeToBeRemoved) | |
498 return Position(const_cast<Text*>(&mergedNode), oldLength + offset); | |
499 if (anchorNode == nodeToBeRemoved.parentNode() && | |
500 offset == nodeToBeRemovedWithIndex.index()) { | |
501 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
502 } | |
503 return position; | |
504 } | |
505 } | |
506 NOTREACHED() << position; | |
559 return position; | 507 return position; |
560 } | 508 } |
561 | 509 |
562 void FrameSelection::didMergeTextNodes( | 510 // TODO(yosin): We should move |SelectionEditor::didMergeTextNodes()| to |
511 // "SelectionEditor.cpp". | |
512 void SelectionEditor::didMergeTextNodes( | |
563 const Text& mergedNode, | 513 const Text& mergedNode, |
564 const NodeWithIndex& nodeToBeRemovedWithIndex, | 514 const NodeWithIndex& nodeToBeRemovedWithIndex, |
565 unsigned offset) { | 515 unsigned oldLength) { |
566 const Text& oldNode = toText(nodeToBeRemovedWithIndex.node()); | 516 if (m_selection.isNone()) { |
567 if (isNone() || !oldNode.isConnected()) | 517 didFinishDOMMutation(); |
568 return; | 518 return; |
569 Position base = updatePostionAfterAdoptingTextNodesMerged(selection().base(), | 519 } |
570 oldNode, offset); | 520 const Position& newBase = updatePostionAfterAdoptingTextNodesMerged( |
571 Position extent = updatePostionAfterAdoptingTextNodesMerged( | 521 m_selection.m_base, mergedNode, nodeToBeRemovedWithIndex, oldLength); |
572 selection().extent(), oldNode, offset); | 522 const Position& newExtent = updatePostionAfterAdoptingTextNodesMerged( |
573 Position start = updatePostionAfterAdoptingTextNodesMerged( | 523 m_selection.m_extent, mergedNode, nodeToBeRemovedWithIndex, oldLength); |
574 selection().start(), oldNode, offset); | 524 didFinishTextChange(newBase, newExtent); |
575 Position end = updatePostionAfterAdoptingTextNodesMerged(selection().end(), | |
576 oldNode, offset); | |
577 updateSelectionIfNeeded(base, extent, start, end); | |
578 } | 525 } |
579 | 526 |
527 // TODO(yosin): We should remove |updatePostionAfterAdoptingTextNodeSplit()| | |
tkent
2017/02/10 08:47:40
nit: remove -> move
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
528 // to // "SelectionEditor.cpp" since it used only in | |
tkent
2017/02/10 08:47:41
nit: the second '//' looks unnecessary.
yosin_UTC9
2017/02/10 10:13:20
Done.
| |
529 // |SelectionEditor::didSplitTextNode()|. | |
580 static Position updatePostionAfterAdoptingTextNodeSplit( | 530 static Position updatePostionAfterAdoptingTextNodeSplit( |
581 const Position& position, | 531 const Position& position, |
582 const Text& oldNode) { | 532 const Text& oldNode) { |
583 if (!position.anchorNode() || position.anchorNode() != &oldNode || | 533 if (!position.anchorNode() || position.anchorNode() != &oldNode || |
584 !position.isOffsetInAnchor()) | 534 !position.isOffsetInAnchor()) |
585 return position; | 535 return position; |
586 // See: | 536 // See: |
587 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation | 537 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range- Mutation |
588 DCHECK_GE(position.offsetInContainerNode(), 0); | 538 DCHECK_GE(position.offsetInContainerNode(), 0); |
589 unsigned positionOffset = | 539 unsigned positionOffset = |
590 static_cast<unsigned>(position.offsetInContainerNode()); | 540 static_cast<unsigned>(position.offsetInContainerNode()); |
591 unsigned oldLength = oldNode.length(); | 541 unsigned oldLength = oldNode.length(); |
592 if (positionOffset <= oldLength) | 542 if (positionOffset <= oldLength) |
593 return position; | 543 return position; |
594 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); | 544 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); |
595 } | 545 } |
596 | 546 |
597 void FrameSelection::didSplitTextNode(const Text& oldNode) { | 547 // TODO(yosin): We should move |SelectionEditor::didSplitTextNode()| to |
598 if (isNone() || !oldNode.isConnected()) | 548 // "SelectionEditor.cpp". |
549 void SelectionEditor::didSplitTextNode(const Text& oldNode) { | |
550 if (m_selection.isNone() || !oldNode.isConnected()) { | |
551 didFinishDOMMutation(); | |
599 return; | 552 return; |
600 Position base = | 553 } |
601 updatePostionAfterAdoptingTextNodeSplit(selection().base(), oldNode); | 554 const Position& newBase = |
602 Position extent = | 555 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_base, oldNode); |
603 updatePostionAfterAdoptingTextNodeSplit(selection().extent(), oldNode); | 556 const Position& newExtent = |
604 Position start = | 557 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_extent, oldNode); |
605 updatePostionAfterAdoptingTextNodeSplit(selection().start(), oldNode); | 558 didFinishTextChange(newBase, newExtent); |
606 Position end = | |
607 updatePostionAfterAdoptingTextNodeSplit(selection().end(), oldNode); | |
608 updateSelectionIfNeeded(base, extent, start, end); | |
609 } | |
610 | |
611 void FrameSelection::updateSelectionIfNeeded(const Position& base, | |
612 const Position& extent, | |
613 const Position& start, | |
614 const Position& end) { | |
615 if (base == selection().base() && extent == selection().extent() && | |
616 start == selection().start() && end == selection().end()) | |
617 return; | |
618 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | |
619 // |Editor| class. | |
620 if (!document().isRunningExecCommand()) | |
621 TypingCommand::closeTyping(m_frame); | |
622 VisibleSelection newSelection; | |
623 if (selection().isBaseFirst()) | |
624 newSelection.setWithoutValidation(start, end); | |
625 else | |
626 newSelection.setWithoutValidation(end, start); | |
627 setSelection(newSelection, DoNotSetFocus); | |
628 } | 559 } |
629 | 560 |
630 void FrameSelection::didChangeFocus() { | 561 void FrameSelection::didChangeFocus() { |
631 // Hits in | 562 // Hits in |
632 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable d.html | 563 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable d.html |
633 DisableCompositingQueryAsserts disabler; | 564 DisableCompositingQueryAsserts disabler; |
634 updateAppearance(); | 565 updateAppearance(); |
635 } | 566 } |
636 | 567 |
637 static DispatchEventResult dispatchSelectStart( | 568 static DispatchEventResult dispatchSelectStart( |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
724 } | 655 } |
725 | 656 |
726 void FrameSelection::contextDestroyed(Document* document) { | 657 void FrameSelection::contextDestroyed(Document* document) { |
727 m_granularity = CharacterGranularity; | 658 m_granularity = CharacterGranularity; |
728 | 659 |
729 LayoutViewItem view = m_frame->contentLayoutItem(); | 660 LayoutViewItem view = m_frame->contentLayoutItem(); |
730 if (!view.isNull()) | 661 if (!view.isNull()) |
731 view.clearSelection(); | 662 view.clearSelection(); |
732 | 663 |
733 m_frame->editor().clearTypingStyle(); | 664 m_frame->editor().clearTypingStyle(); |
734 m_selectionEditor->documentDetached(*document); | |
735 } | 665 } |
736 | 666 |
737 void FrameSelection::clearPreviousCaretVisualRect(const LayoutBlock& block) { | 667 void FrameSelection::clearPreviousCaretVisualRect(const LayoutBlock& block) { |
738 m_frameCaret->clearPreviousVisualRect(block); | 668 m_frameCaret->clearPreviousVisualRect(block); |
739 } | 669 } |
740 | 670 |
741 void FrameSelection::layoutBlockWillBeDestroyed(const LayoutBlock& block) { | 671 void FrameSelection::layoutBlockWillBeDestroyed(const LayoutBlock& block) { |
742 m_frameCaret->layoutBlockWillBeDestroyed(block); | 672 m_frameCaret->layoutBlockWillBeDestroyed(block); |
743 } | 673 } |
744 | 674 |
745 void FrameSelection::updateStyleAndLayoutIfNeeded() { | 675 void FrameSelection::updateStyleAndLayoutIfNeeded() { |
746 m_frameCaret->updateStyleAndLayoutIfNeeded(); | 676 m_frameCaret->updateStyleAndLayoutIfNeeded(); |
747 } | 677 } |
748 | 678 |
749 void FrameSelection::invalidatePaintIfNeeded( | 679 void FrameSelection::invalidatePaintIfNeeded( |
750 const LayoutBlock& block, | 680 const LayoutBlock& block, |
751 const PaintInvalidatorContext& context, | 681 const PaintInvalidatorContext& context, |
752 PaintInvalidationReason reason) { | 682 PaintInvalidationReason reason) { |
753 m_frameCaret->invalidatePaintIfNeeded(block, context, reason); | 683 m_frameCaret->invalidatePaintIfNeeded(block, context, reason); |
754 } | 684 } |
755 | 685 |
756 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const { | 686 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const { |
757 DCHECK(selection().isValidFor(document())); | 687 DCHECK_GE(document().lifecycle().state(), DocumentLifecycle::LayoutClean); |
758 | |
759 bool result = m_frameCaret->shouldPaintCaret(block); | 688 bool result = m_frameCaret->shouldPaintCaret(block); |
760 DCHECK(!result || (isCaret() && hasEditableStyle())); | 689 DCHECK(!result || (isCaret() && hasEditableStyle())); |
761 return result; | 690 return result; |
762 } | 691 } |
763 | 692 |
764 IntRect FrameSelection::absoluteCaretBounds() { | 693 IntRect FrameSelection::absoluteCaretBounds() { |
765 DCHECK(selection().isValidFor(*m_frame->document())); | 694 DCHECK(selection().isValidFor(*m_frame->document())); |
766 return m_frameCaret->absoluteCaretBounds(); | 695 return m_frameCaret->absoluteCaretBounds(); |
767 } | 696 } |
768 | 697 |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1068 | 997 |
1069 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { | 998 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { |
1070 return m_pendingSelection->commit(layoutView); | 999 return m_pendingSelection->commit(layoutView); |
1071 } | 1000 } |
1072 | 1001 |
1073 void FrameSelection::didLayout() { | 1002 void FrameSelection::didLayout() { |
1074 updateAppearance(); | 1003 updateAppearance(); |
1075 } | 1004 } |
1076 | 1005 |
1077 void FrameSelection::updateAppearance() { | 1006 void FrameSelection::updateAppearance() { |
1078 m_frameCaret->updateAppearance(); | 1007 DCHECK(!m_frame->contentLayoutItem().isNull()); |
1079 | 1008 m_frameCaret->scheduleVisualUpdateForPaintInvalidationIfNeeded(); |
1080 if (m_frame->contentLayoutItem().isNull()) | |
1081 return; | |
1082 m_pendingSelection->setHasPendingSelection(); | 1009 m_pendingSelection->setHasPendingSelection(); |
1083 } | 1010 } |
1084 | 1011 |
1085 void FrameSelection::notifyLayoutObjectOfSelectionChange( | 1012 void FrameSelection::notifyLayoutObjectOfSelectionChange( |
1086 EUserTriggered userTriggered) { | 1013 EUserTriggered userTriggered) { |
1087 if (TextControlElement* textControl = enclosingTextControl(start())) | 1014 if (TextControlElement* textControl = enclosingTextControl(start())) |
1088 textControl->selectionChanged(userTriggered == UserTriggered); | 1015 textControl->selectionChanged(userTriggered == UserTriggered); |
1089 } | 1016 } |
1090 | 1017 |
1091 // Helper function that tells whether a particular node is an element that has | 1018 // Helper function that tells whether a particular node is an element that has |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1274 | 1201 |
1275 Document* document = m_frame->document(); | 1202 Document* document = m_frame->document(); |
1276 if (!isNone() || !(blink::hasEditableStyle(*document))) | 1203 if (!isNone() || !(blink::hasEditableStyle(*document))) |
1277 return; | 1204 return; |
1278 | 1205 |
1279 Element* documentElement = document->documentElement(); | 1206 Element* documentElement = document->documentElement(); |
1280 if (!documentElement) | 1207 if (!documentElement) |
1281 return; | 1208 return; |
1282 if (HTMLBodyElement* body = | 1209 if (HTMLBodyElement* body = |
1283 Traversal<HTMLBodyElement>::firstChild(*documentElement)) { | 1210 Traversal<HTMLBodyElement>::firstChild(*documentElement)) { |
1284 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
1285 // needs to be audited. See http://crbug.com/590369 for more details. | |
1286 document->updateStyleAndLayoutIgnorePendingStylesheets(); | |
1287 | |
1288 setSelection(SelectionInDOMTree::Builder() | 1211 setSelection(SelectionInDOMTree::Builder() |
1289 .collapse(firstPositionInOrBeforeNode(body)) | 1212 .collapse(firstPositionInOrBeforeNode(body)) |
1290 .build()); | 1213 .build()); |
1291 } | 1214 } |
1292 } | 1215 } |
1293 | 1216 |
1294 // TODO(yoichio): We should have LocalFrame having FrameCaret, | 1217 // TODO(yoichio): We should have LocalFrame having FrameCaret, |
1295 // Editor and PendingSelection using FrameCaret directly | 1218 // Editor and PendingSelection using FrameCaret directly |
1296 // and get rid of this. | 1219 // and get rid of this. |
1297 bool FrameSelection::shouldShowBlockCursor() const { | 1220 bool FrameSelection::shouldShowBlockCursor() const { |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1388 FrameSelection::DoNotClearStrategy | UserTriggered, | 1311 FrameSelection::DoNotClearStrategy | UserTriggered, |
1389 CursorAlignOnScroll::IfNeeded, CharacterGranularity); | 1312 CursorAlignOnScroll::IfNeeded, CharacterGranularity); |
1390 } | 1313 } |
1391 | 1314 |
1392 // TODO(yosin): We should make |FrameSelection::moveRangeSelection()| to take | 1315 // TODO(yosin): We should make |FrameSelection::moveRangeSelection()| to take |
1393 // two |IntPoint| instead of two |VisiblePosition| like | 1316 // two |IntPoint| instead of two |VisiblePosition| like |
1394 // |moveRangeSelectionExtent()|. | 1317 // |moveRangeSelectionExtent()|. |
1395 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, | 1318 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, |
1396 const VisiblePosition& extentPosition, | 1319 const VisiblePosition& extentPosition, |
1397 TextGranularity granularity) { | 1320 TextGranularity granularity) { |
1398 VisibleSelection newSelection = createVisibleSelection( | 1321 SelectionInDOMTree newSelection = |
1399 SelectionInDOMTree::Builder() | 1322 SelectionInDOMTree::Builder() |
1400 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), | 1323 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), |
1401 extentPosition.deepEquivalent()) | 1324 extentPosition.deepEquivalent()) |
1402 .setAffinity(basePosition.affinity()) | 1325 .setAffinity(basePosition.affinity()) |
1403 .setGranularity(granularity) | 1326 .setGranularity(granularity) |
1404 .build()); | 1327 .setIsHandleVisible(isHandleVisible()) |
1328 .build(); | |
1405 | 1329 |
1406 if (newSelection.isNone()) | 1330 if (newSelection.isNone()) |
1407 return; | 1331 return; |
1408 | 1332 |
1409 setSelection(newSelection, m_handleVisibility, CloseTyping | ClearTypingStyle, | 1333 setSelection(newSelection, CloseTyping | ClearTypingStyle, |
1410 CursorAlignOnScroll::IfNeeded, granularity); | 1334 CursorAlignOnScroll::IfNeeded, granularity); |
1411 } | 1335 } |
1412 | 1336 |
1413 void FrameSelection::updateIfNeeded() { | 1337 void FrameSelection::updateIfNeeded() { |
1414 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); | 1338 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); |
1415 m_selectionEditor->updateIfNeeded(); | 1339 m_selectionEditor->updateIfNeeded(); |
1416 } | 1340 } |
1417 | 1341 |
1418 void FrameSelection::setCaretVisible(bool caretIsVisible) { | 1342 void FrameSelection::setCaretVisible(bool caretIsVisible) { |
1419 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible | 1343 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible |
(...skipping 29 matching lines...) Expand all Loading... | |
1449 } | 1373 } |
1450 | 1374 |
1451 void showTree(const blink::FrameSelection* sel) { | 1375 void showTree(const blink::FrameSelection* sel) { |
1452 if (sel) | 1376 if (sel) |
1453 sel->showTreeForThis(); | 1377 sel->showTreeForThis(); |
1454 else | 1378 else |
1455 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; | 1379 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; |
1456 } | 1380 } |
1457 | 1381 |
1458 #endif | 1382 #endif |
OLD | NEW |