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 Document& FrameSelection::document() const { | 113 const Document& FrameSelection::document() const { |
114 DCHECK(lifecycleContext()); | 114 DCHECK(lifecycleContext()); |
115 return *lifecycleContext(); | 115 return *lifecycleContext(); |
116 } | 116 } |
117 | 117 |
118 bool FrameSelection::isHandleVisible() const { | 118 Document& FrameSelection::document() { |
119 return selectionInDOMTree().isHandleVisible(); | 119 DCHECK(lifecycleContext()); |
| 120 return *lifecycleContext(); |
120 } | 121 } |
121 | 122 |
122 // TODO(yosin): We should replace |visibleSelection<EditingStrategy>()| to | |
123 // |computeVisibleSelectionInDOMTree()|. | |
124 // TODO(yosin): To avoid undefined symbols in clang, we explicitly | 123 // TODO(yosin): To avoid undefined symbols in clang, we explicitly |
125 // have specialized version of |FrameSelection::visibleSelection<Strategy>| | 124 // have specialized version of |FrameSelection::visibleSelection<Strategy>| |
126 // before |FrameSelection::selection()| which refers this. | 125 // before |FrameSelection::selection()| which refers this. |
127 template <> | 126 template <> |
128 const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>() | 127 const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>() |
129 const { | 128 const { |
130 return m_selectionEditor->visibleSelection<EditingStrategy>(); | 129 return m_selectionEditor->visibleSelection<EditingStrategy>(); |
131 } | 130 } |
132 | 131 |
133 // TODO(yosin): We should replace | |
134 // |visibleSelection<EditingInFlatTreeStrategy>()| with | |
135 // |computeVisibleSelectionInFlatTree()|. | |
136 template <> | 132 template <> |
137 const VisibleSelectionInFlatTree& | 133 const VisibleSelectionInFlatTree& |
138 FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const { | 134 FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const { |
139 return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>(); | 135 return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>(); |
140 } | 136 } |
141 | 137 |
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 | |
156 Element* FrameSelection::rootEditableElementOrDocumentElement() const { | 138 Element* FrameSelection::rootEditableElementOrDocumentElement() const { |
157 Element* selectionRoot = selection().rootEditableElement(); | 139 Element* selectionRoot = selection().rootEditableElement(); |
158 return selectionRoot ? selectionRoot : document().documentElement(); | 140 return selectionRoot ? selectionRoot : document().documentElement(); |
159 } | 141 } |
160 | 142 |
161 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { | 143 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { |
162 Element* selectionRoot = selection().rootEditableElement(); | 144 Element* selectionRoot = selection().rootEditableElement(); |
163 if (selectionRoot) | 145 if (selectionRoot) |
164 return selectionRoot; | 146 return selectionRoot; |
165 | 147 |
166 Node* node = selection().base().computeContainerNode(); | 148 Node* node = selection().base().computeContainerNode(); |
167 return node ? &node->treeScope().rootNode() : 0; | 149 return node ? &node->treeScope().rootNode() : 0; |
168 } | 150 } |
169 | 151 |
170 // TODO(yosin): We should rename |FrameSelection::selection()| to | |
171 // |selectionDeprecated()|. | |
172 const VisibleSelection& FrameSelection::selection() const { | 152 const VisibleSelection& FrameSelection::selection() const { |
173 // TODO(yosin): We should hoist updateStyleAndLayoutIgnorePendingStylesheets | 153 return visibleSelection<EditingStrategy>(); |
174 // to caller. See http://crbug.com/590369 for more details. | |
175 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
176 return computeVisibleSelectionInDOMTree(); | |
177 } | 154 } |
178 | 155 |
179 const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const { | 156 const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const { |
180 return visibleSelection<EditingInFlatTreeStrategy>(); | 157 return visibleSelection<EditingInFlatTreeStrategy>(); |
181 } | 158 } |
182 | 159 |
183 void FrameSelection::moveCaretSelection(const IntPoint& point) { | 160 void FrameSelection::moveCaretSelection(const IntPoint& point) { |
184 DCHECK(!document().needsLayoutTreeUpdate()); | 161 DCHECK(!document().needsLayoutTreeUpdate()); |
185 | 162 |
186 Element* const editable = rootEditableElement(); | 163 Element* const editable = rootEditableElement(); |
187 if (!editable) | 164 if (!editable) |
188 return; | 165 return; |
189 | 166 |
190 const VisiblePosition position = | 167 const VisiblePosition position = |
191 visiblePositionForContentsPoint(point, frame()); | 168 visiblePositionForContentsPoint(point, frame()); |
192 SelectionInDOMTree::Builder builder; | 169 SelectionInDOMTree::Builder builder; |
193 builder.setIsDirectional(selection().isDirectional()); | 170 builder.setIsDirectional(selection().isDirectional()); |
194 builder.setIsHandleVisible(true); | 171 builder.setIsHandleVisible(true); |
195 if (position.isNotNull()) | 172 if (position.isNotNull()) |
196 builder.collapse(position.toPositionWithAffinity()); | 173 builder.collapse(position.toPositionWithAffinity()); |
197 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); | 174 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); |
198 } | 175 } |
199 | 176 |
200 void FrameSelection::setSelection(const SelectionInDOMTree& passedSelection, | 177 template <typename Strategy> |
201 SetSelectionOptions options, | 178 void FrameSelection::setSelectionAlgorithm( |
202 CursorAlignOnScroll align, | 179 const VisibleSelectionTemplate<Strategy>& newSelection, |
203 TextGranularity granularity) { | 180 HandleVisibility handleVisibility, |
| 181 SetSelectionOptions options, |
| 182 CursorAlignOnScroll align, |
| 183 TextGranularity granularity) { |
204 DCHECK(isAvailable()); | 184 DCHECK(isAvailable()); |
205 passedSelection.assertValidFor(document()); | 185 DCHECK(newSelection.isValidFor(document())); |
206 | 186 const Document& currentDocument = document(); |
207 SelectionInDOMTree::Builder builder(passedSelection); | |
208 if (shouldAlwaysUseDirectionalSelection(m_frame)) | |
209 builder.setIsDirectional(true); | |
210 SelectionInDOMTree newSelection = builder.build(); | |
211 if (m_granularityStrategy && | 187 if (m_granularityStrategy && |
212 (options & FrameSelection::DoNotClearStrategy) == 0) | 188 (options & FrameSelection::DoNotClearStrategy) == 0) |
213 m_granularityStrategy->Clear(); | 189 m_granularityStrategy->Clear(); |
214 bool closeTyping = options & CloseTyping; | 190 bool closeTyping = options & CloseTyping; |
215 bool shouldClearTypingStyle = options & ClearTypingStyle; | 191 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 |
216 m_granularity = granularity; | 200 m_granularity = granularity; |
217 | 201 |
218 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 202 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
219 // |Editor| class. | 203 // |Editor| class. |
220 if (closeTyping) | 204 if (closeTyping) |
221 TypingCommand::closeTyping(m_frame); | 205 TypingCommand::closeTyping(m_frame); |
222 | 206 |
223 if (shouldClearTypingStyle) | 207 if (shouldClearTypingStyle) |
224 m_frame->editor().clearTypingStyle(); | 208 m_frame->editor().clearTypingStyle(); |
225 | 209 |
226 const SelectionInDOMTree oldSelectionInDOMTree = | 210 if (m_selectionEditor->visibleSelection<Strategy>() == s && |
227 m_selectionEditor->selectionInDOMTree(); | 211 m_handleVisibility == handleVisibility) { |
228 if (oldSelectionInDOMTree == newSelection) | 212 // Even if selection was not changed, selection offsets may have been |
| 213 // changed. |
| 214 notifyLayoutObjectOfSelectionChange(userTriggered); |
229 return; | 215 return; |
230 m_selectionEditor->setSelection(newSelection); | 216 } |
| 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); |
231 scheduleVisualUpdateForPaintInvalidationIfNeeded(); | 224 scheduleVisualUpdateForPaintInvalidationIfNeeded(); |
232 | 225 |
233 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets | 226 if (!s.isNone() && !(options & DoNotSetFocus)) { |
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 (!computeVisibleSelectionInDOMTree().isNone() && | |
247 !(options & DoNotSetFocus)) { | |
248 setFocusedNodeIfNeeded(); | 227 setFocusedNodeIfNeeded(); |
249 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and | 228 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and |
250 // "FocusIn", |m_frame| may associate to another document. | 229 // "FocusIn", |m_frame| may associate to another document. |
251 if (!isAvailable() || document() != currentDocument) { | 230 if (!isAvailable() || document() != currentDocument) { |
252 // Once we get test case to reach here, we should change this | 231 // Once we get test case to reach here, we should change this |
253 // if-statement to |DCHECK()|. | 232 // if-statement to |DCHECK()|. |
254 NOTREACHED(); | 233 NOTREACHED(); |
255 return; | 234 return; |
256 } | 235 } |
257 } | 236 } |
258 | 237 |
259 if (!(options & DoNotUpdateAppearance)) { | 238 if (!(options & DoNotUpdateAppearance)) { |
260 m_frameCaret->stopCaretBlinkTimer(); | 239 m_frameCaret->stopCaretBlinkTimer(); |
261 updateAppearance(); | 240 updateAppearance(); |
262 } | 241 } |
263 | 242 |
264 // Always clear the x position used for vertical arrow navigation. | 243 // Always clear the x position used for vertical arrow navigation. |
265 // It will be restored by the vertical arrow navigation code if necessary. | 244 // It will be restored by the vertical arrow navigation code if necessary. |
266 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); | 245 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); |
267 // TODO(yosin): Can we move this to at end of this function? | |
268 // This may dispatch a synchronous focus-related events. | 246 // This may dispatch a synchronous focus-related events. |
269 selectFrameElementInParentIfFullySelected(); | 247 selectFrameElementInParentIfFullySelected(); |
270 if (!isAvailable() || document() != currentDocument) { | 248 if (!isAvailable() || document() != currentDocument) { |
271 // editing/selection/selectallchildren-crash.html and | 249 // editing/selection/selectallchildren-crash.html and |
272 // editing/selection/longpress-selection-in-iframe-removed-crash.html | 250 // editing/selection/longpress-selection-in-iframe-removed-crash.html |
273 // reach here. | 251 // reach here. |
274 return; | 252 return; |
275 } | 253 } |
276 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | |
277 notifyLayoutObjectOfSelectionChange(userTriggered); | 254 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); |
278 if (userTriggered == UserTriggered) { | 264 if (userTriggered == UserTriggered) { |
279 ScrollAlignment alignment; | 265 ScrollAlignment alignment; |
280 | 266 |
281 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) | 267 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) |
282 alignment = (align == CursorAlignOnScroll::Always) | 268 alignment = (align == CursorAlignOnScroll::Always) |
283 ? ScrollAlignment::alignCenterAlways | 269 ? ScrollAlignment::alignCenterAlways |
284 : ScrollAlignment::alignCenterIfNeeded; | 270 : ScrollAlignment::alignCenterIfNeeded; |
285 else | 271 else |
286 alignment = (align == CursorAlignOnScroll::Always) | 272 alignment = (align == CursorAlignOnScroll::Always) |
287 ? ScrollAlignment::alignTopAlways | 273 ? ScrollAlignment::alignTopAlways |
288 : ScrollAlignment::alignToEdgeIfNeeded; | 274 : ScrollAlignment::alignToEdgeIfNeeded; |
289 | 275 |
290 // TODO(editing-dev): The use of | |
291 // updateStyleAndLayoutIgnorePendingStylesheets | |
292 // needs to be audited. See http://crbug.com/590369 for more details. | |
293 document().updateStyleAndLayoutIgnorePendingStylesheets(); | |
294 | |
295 revealSelection(alignment, RevealExtent); | 276 revealSelection(alignment, RevealExtent); |
296 } | 277 } |
297 | 278 |
298 notifyAccessibilityForSelectionChange(); | 279 notifyAccessibilityForSelectionChange(); |
299 notifyCompositorForSelectionChange(); | 280 notifyCompositorForSelectionChange(); |
300 notifyEventHandlerForSelectionChange(); | 281 notifyEventHandlerForSelectionChange(); |
301 m_frame->domWindow()->enqueueDocumentEvent( | 282 m_frame->domWindow()->enqueueDocumentEvent( |
302 Event::create(EventTypeNames::selectionchange)); | 283 Event::create(EventTypeNames::selectionchange)); |
303 } | 284 } |
304 | 285 |
| 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. |
305 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, | 308 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, |
306 SetSelectionOptions options, | 309 SetSelectionOptions options, |
307 CursorAlignOnScroll align, | 310 CursorAlignOnScroll align, |
308 TextGranularity granularity) { | 311 TextGranularity granularity) { |
309 newSelection.assertValidFor(document()); | 312 if (!newSelection.isNone()) { |
310 SelectionInDOMTree::Builder builder; | 313 // TODO(editing-dev): The use of |
311 builder.setAffinity(newSelection.affinity()) | 314 // updateStyleAndLayoutIgnorePendingStylesheets |
312 .setBaseAndExtent(toPositionInDOMTree(newSelection.base()), | 315 // needs to be audited. See http://crbug.com/590369 for more details. |
313 toPositionInDOMTree(newSelection.extent())) | 316 newSelection.base() |
314 .setGranularity(newSelection.granularity()) | 317 .document() |
315 .setIsDirectional(newSelection.isDirectional()) | 318 ->updateStyleAndLayoutIgnorePendingStylesheets(); |
316 .setIsHandleVisible(newSelection.isHandleVisible()) | 319 } |
317 .setHasTrailingWhitespace(newSelection.hasTrailingWhitespace()); | 320 setSelection(createVisibleSelection(newSelection), |
318 return setSelection(builder.build(), options, align, granularity); | 321 newSelection.isHandleVisible() ? HandleVisibility::Visible |
| 322 : HandleVisibility::NotVisible, |
| 323 options, align, granularity); |
319 } | 324 } |
320 | 325 |
321 void FrameSelection::setSelection(const VisibleSelection& newSelection, | 326 void FrameSelection::setSelection(const VisibleSelection& newSelection, |
322 HandleVisibility handleVisibility, | 327 HandleVisibility handleVisibility, |
323 SetSelectionOptions options, | 328 SetSelectionOptions options, |
324 CursorAlignOnScroll align, | 329 CursorAlignOnScroll align, |
325 TextGranularity granularity) { | 330 TextGranularity granularity) { |
326 setSelection( | 331 setSelectionAlgorithm<EditingStrategy>(newSelection, handleVisibility, |
327 SelectionInDOMTree::Builder(newSelection.asSelection()) | 332 options, align, granularity); |
328 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) | |
329 .build(), | |
330 options, align, granularity); | |
331 } | 333 } |
332 | 334 |
333 void FrameSelection::setSelection(const VisibleSelection& newSelection, | 335 void FrameSelection::setSelection(const VisibleSelection& newSelection, |
334 SetSelectionOptions options) { | 336 SetSelectionOptions options) { |
335 setSelection(newSelection.asSelection(), options); | 337 setSelection(newSelection, HandleVisibility::NotVisible, options); |
336 } | 338 } |
337 | 339 |
338 void FrameSelection::setSelection( | 340 void FrameSelection::setSelection( |
339 const VisibleSelectionInFlatTree& newSelection, | 341 const VisibleSelectionInFlatTree& newSelection, |
340 HandleVisibility handleVisibility, | 342 HandleVisibility handleVisibility, |
341 SetSelectionOptions options, | 343 SetSelectionOptions options, |
342 CursorAlignOnScroll align, | 344 CursorAlignOnScroll align, |
343 TextGranularity granularity) { | 345 TextGranularity granularity) { |
344 setSelection( | 346 setSelectionAlgorithm<EditingInFlatTreeStrategy>( |
345 SelectionInFlatTree::Builder(newSelection.asSelection()) | 347 newSelection, handleVisibility, options, align, granularity); |
346 .setIsHandleVisible(handleVisibility == HandleVisibility::Visible) | |
347 .build(), | |
348 options, align, granularity); | |
349 } | 348 } |
350 | 349 |
351 void FrameSelection::setSelection( | 350 void FrameSelection::setSelection( |
352 const VisibleSelectionInFlatTree& newSelection, | 351 const VisibleSelectionInFlatTree& newSelection, |
353 SetSelectionOptions options) { | 352 SetSelectionOptions options) { |
354 setSelection(newSelection.asSelection(), options); | 353 setSelection(newSelection, HandleVisibility::NotVisible, options); |
355 } | 354 } |
356 | 355 |
357 // TODO(yosin): We should move |computePositionForChildrenRemoval()| to | 356 static bool removingNodeRemovesPosition(Node& node, const Position& position) { |
358 // "SelectionEditor.cpp" since it used only in | 357 if (!position.anchorNode()) |
359 // |SelectionEditor::nodeChildrenWillBeRemoved()|. | 358 return false; |
| 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 |
360 static Position computePositionForChildrenRemoval(const Position& position, | 370 static Position computePositionForChildrenRemoval(const Position& position, |
361 ContainerNode& container) { | 371 ContainerNode& container) { |
362 Node* node = position.computeContainerNode(); | 372 Node* node = position.computeContainerNode(); |
363 if (container.containsIncludingHostElements(*node)) | 373 if (container.containsIncludingHostElements(*node)) |
364 return Position::firstPositionInNode(&container); | 374 return Position::firstPositionInNode(&container); |
365 return position; | 375 return position; |
366 } | 376 } |
367 | 377 |
368 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { | 378 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { |
369 if (!container.inActiveDocument()) | 379 if (isNone() || !container.inActiveDocument()) |
370 return; | 380 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 |
371 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 479 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
372 // |Editor| class. | 480 // |Editor| class. |
373 if (!document().isRunningExecCommand()) | 481 if (!document().isRunningExecCommand()) |
374 TypingCommand::closeTyping(m_frame); | 482 TypingCommand::closeTyping(m_frame); |
375 } | 483 } |
376 | 484 |
377 // TODO(yosin): We should move |SelectionEditor::nodeChildrenWillBeRemoved()| | |
378 // to "SelectionEditor.cpp". | |
379 void SelectionEditor::nodeChildrenWillBeRemoved(ContainerNode& container) { | |
380 if (m_selection.isNone()) | |
381 return; | |
382 const Position oldBase = m_selection.m_base; | |
383 const Position oldExtent = m_selection.m_extent; | |
384 const Position& newBase = | |
385 computePositionForChildrenRemoval(oldBase, container); | |
386 const Position& newExtent = | |
387 computePositionForChildrenRemoval(oldExtent, container); | |
388 if (newBase == oldBase && newExtent == oldExtent) | |
389 return; | |
390 m_selection = SelectionInDOMTree::Builder() | |
391 .setBaseAndExtent(newBase, newExtent) | |
392 .build(); | |
393 markCacheDirty(); | |
394 } | |
395 | |
396 // TODO(yosin): We should move |computePositionForChildrenRemoval()| with | |
397 // |nodeWillBeRemoved()| to "SelectionEditor.cpp". | |
398 static Position computePositionForNodeRemoval(const Position& position, | |
399 Node& nodeToBeRemoved) { | |
400 Position result = position; | |
401 // TODO(yosin): We should rename |updatePositionForNodeRemoval()| | |
402 // to |computePositionForNodeRemoval()| to avoid using output parameter. | |
403 updatePositionForNodeRemoval(result, nodeToBeRemoved); | |
404 return result; | |
405 } | |
406 | |
407 // TODO(yosin): We should move |nodeWillBeRemoved()| to | |
408 // "SelectionEditor.cpp". | |
409 void SelectionEditor::nodeWillBeRemoved(Node& nodeToBeRemoved) { | |
410 if (m_selection.isNone()) | |
411 return; | |
412 const Position oldBase = m_selection.m_base; | |
413 const Position oldExtent = m_selection.m_extent; | |
414 const Position& newBase = | |
415 computePositionForNodeRemoval(oldBase, nodeToBeRemoved); | |
416 const Position& newExtent = | |
417 computePositionForNodeRemoval(oldExtent, nodeToBeRemoved); | |
418 if (newBase == oldBase && newExtent == oldExtent) | |
419 return; | |
420 m_selection = SelectionInDOMTree::Builder() | |
421 .setBaseAndExtent(newBase, newExtent) | |
422 .build(); | |
423 markCacheDirty(); | |
424 } | |
425 | |
426 void FrameSelection::nodeWillBeRemoved(Node& node) { | |
427 // There can't be a selection inside a fragment, so if a fragment's node is | |
428 // being removed, the selection in the document that created the fragment | |
429 // needs no adjustment. | |
430 if (!node.inActiveDocument()) | |
431 return; | |
432 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | |
433 // |Editor| class. | |
434 if (!document().isRunningExecCommand()) | |
435 TypingCommand::closeTyping(m_frame); | |
436 } | |
437 | |
438 // TODO(yosin): We should move |updatePositionAfterAdoptingTextReplacement()| | |
439 // to "SelectionEditor.cpp" since it used only in | |
440 // |SelectionEditor::didUpdateCharacterData()|. | |
441 static Position updatePositionAfterAdoptingTextReplacement( | 485 static Position updatePositionAfterAdoptingTextReplacement( |
442 const Position& position, | 486 const Position& position, |
443 CharacterData* node, | 487 CharacterData* node, |
444 unsigned offset, | 488 unsigned offset, |
445 unsigned oldLength, | 489 unsigned oldLength, |
446 unsigned newLength) { | 490 unsigned newLength) { |
447 if (position.anchorNode() != node) | 491 if (!position.anchorNode() || position.anchorNode() != node || |
| 492 !position.isOffsetInAnchor()) |
448 return position; | 493 return position; |
449 | 494 |
450 if (position.isBeforeAnchor()) { | |
451 return updatePositionAfterAdoptingTextReplacement( | |
452 Position(node, 0), node, offset, oldLength, newLength); | |
453 } | |
454 if (position.isAfterAnchor()) { | |
455 return updatePositionAfterAdoptingTextReplacement( | |
456 Position(node, oldLength), node, offset, oldLength, newLength); | |
457 } | |
458 | |
459 // See: | 495 // See: |
460 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | 496 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation |
461 DCHECK_GE(position.offsetInContainerNode(), 0); | 497 DCHECK_GE(position.offsetInContainerNode(), 0); |
462 unsigned positionOffset = | 498 unsigned positionOffset = |
463 static_cast<unsigned>(position.offsetInContainerNode()); | 499 static_cast<unsigned>(position.offsetInContainerNode()); |
464 // Replacing text can be viewed as a deletion followed by insertion. | 500 // Replacing text can be viewed as a deletion followed by insertion. |
465 if (positionOffset >= offset && positionOffset <= offset + oldLength) | 501 if (positionOffset >= offset && positionOffset <= offset + oldLength) |
466 positionOffset = offset; | 502 positionOffset = offset; |
467 | 503 |
468 // Adjust the offset if the position is after the end of the deleted contents | 504 // Adjust the offset if the position is after the end of the deleted contents |
469 // (positionOffset > offset + oldLength) to avoid having a stale offset. | 505 // (positionOffset > offset + oldLength) to avoid having a stale offset. |
470 if (positionOffset > offset + oldLength) | 506 if (positionOffset > offset + oldLength) |
471 positionOffset = positionOffset - oldLength + newLength; | 507 positionOffset = positionOffset - oldLength + newLength; |
472 | 508 |
473 // Due to case folding | 509 // Due to case folding |
474 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText | 510 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText |
475 // length may be different from Text length. A correct implementation would | 511 // length may be different from Text length. A correct implementation would |
476 // translate the LayoutText offset to a Text offset; this is just a safety | 512 // translate the LayoutText offset to a Text offset; this is just a safety |
477 // precaution to avoid offset values that run off the end of the Text. | 513 // precaution to avoid offset values that run off the end of the Text. |
478 if (positionOffset > node->length()) | 514 if (positionOffset > node->length()) |
479 positionOffset = node->length(); | 515 positionOffset = node->length(); |
480 | 516 |
481 return Position(node, positionOffset); | 517 // CharacterNode in VisibleSelection must be Text node, because Comment |
| 518 // and ProcessingInstruction node aren't visible. |
| 519 return Position(toText(node), positionOffset); |
482 } | 520 } |
483 | 521 |
484 // TODO(yosin): We should move |didUpdateCharacterData()| to | 522 void FrameSelection::didUpdateCharacterData(CharacterData* node, |
485 // "SelectionEditor.cpp". | 523 unsigned offset, |
486 void SelectionEditor::didUpdateCharacterData(CharacterData* node, | 524 unsigned oldLength, |
487 unsigned offset, | 525 unsigned newLength) { |
488 unsigned oldLength, | |
489 unsigned newLength) { | |
490 // The fragment check is a performance optimization. See | 526 // The fragment check is a performance optimization. See |
491 // http://trac.webkit.org/changeset/30062. | 527 // http://trac.webkit.org/changeset/30062. |
492 if (m_selection.isNone() || !node || !node->isConnected()) { | 528 if (isNone() || !node || !node->isConnected()) |
493 didFinishDOMMutation(); | |
494 return; | 529 return; |
495 } | 530 |
496 const Position& newBase = updatePositionAfterAdoptingTextReplacement( | 531 Position base = updatePositionAfterAdoptingTextReplacement( |
497 m_selection.m_base, node, offset, oldLength, newLength); | 532 selection().base(), node, offset, oldLength, newLength); |
498 const Position& newExtent = updatePositionAfterAdoptingTextReplacement( | 533 Position extent = updatePositionAfterAdoptingTextReplacement( |
499 m_selection.m_extent, node, offset, oldLength, newLength); | 534 selection().extent(), node, offset, oldLength, newLength); |
500 didFinishTextChange(newBase, newExtent); | 535 Position start = updatePositionAfterAdoptingTextReplacement( |
| 536 selection().start(), node, offset, oldLength, newLength); |
| 537 Position end = updatePositionAfterAdoptingTextReplacement( |
| 538 selection().end(), node, offset, oldLength, newLength); |
| 539 updateSelectionIfNeeded(base, extent, start, end); |
501 } | 540 } |
502 | 541 |
503 // TODO(yosin): We should move |updatePostionAfterAdoptingTextNodesMerged()| | |
504 // to "SelectionEditor.cpp" since it used only in | |
505 // |SelectionEditor::didMergeTextNodes()|. | |
506 // TODO(yosin): We should introduce |Position(const Text&, int)| to avoid | |
507 // |const_cast<Text*>|. | |
508 static Position updatePostionAfterAdoptingTextNodesMerged( | 542 static Position updatePostionAfterAdoptingTextNodesMerged( |
509 const Position& position, | 543 const Position& position, |
510 const Text& mergedNode, | 544 const Text& oldNode, |
511 const NodeWithIndex& nodeToBeRemovedWithIndex, | 545 unsigned offset) { |
512 unsigned oldLength) { | 546 if (!position.anchorNode() || !position.isOffsetInAnchor()) |
513 Node* const anchorNode = position.anchorNode(); | 547 return position; |
514 const Node& nodeToBeRemoved = nodeToBeRemovedWithIndex.node(); | 548 |
515 switch (position.anchorType()) { | 549 DCHECK_GE(position.offsetInContainerNode(), 0); |
516 case PositionAnchorType::BeforeChildren: | 550 unsigned positionOffset = |
517 case PositionAnchorType::AfterChildren: | 551 static_cast<unsigned>(position.offsetInContainerNode()); |
518 return position; | 552 |
519 case PositionAnchorType::BeforeAnchor: | 553 if (position.anchorNode() == &oldNode) |
520 if (anchorNode == nodeToBeRemoved) | 554 return Position(toText(oldNode.previousSibling()), positionOffset + offset); |
521 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); | 555 |
522 return position; | 556 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset) |
523 case PositionAnchorType::AfterAnchor: | 557 return Position(toText(oldNode.previousSibling()), offset); |
524 if (anchorNode == nodeToBeRemoved) | 558 |
525 return Position(const_cast<Text*>(&mergedNode), mergedNode.length()); | |
526 if (anchorNode == mergedNode) | |
527 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
528 return position; | |
529 case PositionAnchorType::OffsetInAnchor: { | |
530 const int offset = position.offsetInContainerNode(); | |
531 if (anchorNode == nodeToBeRemoved) | |
532 return Position(const_cast<Text*>(&mergedNode), oldLength + offset); | |
533 if (anchorNode == nodeToBeRemoved.parentNode() && | |
534 offset == nodeToBeRemovedWithIndex.index()) { | |
535 return Position(const_cast<Text*>(&mergedNode), oldLength); | |
536 } | |
537 return position; | |
538 } | |
539 } | |
540 NOTREACHED() << position; | |
541 return position; | 559 return position; |
542 } | 560 } |
543 | 561 |
544 // TODO(yosin): We should move |SelectionEditor::didMergeTextNodes()| to | 562 void FrameSelection::didMergeTextNodes( |
545 // "SelectionEditor.cpp". | |
546 void SelectionEditor::didMergeTextNodes( | |
547 const Text& mergedNode, | 563 const Text& mergedNode, |
548 const NodeWithIndex& nodeToBeRemovedWithIndex, | 564 const NodeWithIndex& nodeToBeRemovedWithIndex, |
549 unsigned oldLength) { | 565 unsigned offset) { |
550 if (m_selection.isNone()) { | 566 const Text& oldNode = toText(nodeToBeRemovedWithIndex.node()); |
551 didFinishDOMMutation(); | 567 if (isNone() || !oldNode.isConnected()) |
552 return; | 568 return; |
553 } | 569 Position base = updatePostionAfterAdoptingTextNodesMerged(selection().base(), |
554 const Position& newBase = updatePostionAfterAdoptingTextNodesMerged( | 570 oldNode, offset); |
555 m_selection.m_base, mergedNode, nodeToBeRemovedWithIndex, oldLength); | 571 Position extent = updatePostionAfterAdoptingTextNodesMerged( |
556 const Position& newExtent = updatePostionAfterAdoptingTextNodesMerged( | 572 selection().extent(), oldNode, offset); |
557 m_selection.m_extent, mergedNode, nodeToBeRemovedWithIndex, oldLength); | 573 Position start = updatePostionAfterAdoptingTextNodesMerged( |
558 didFinishTextChange(newBase, newExtent); | 574 selection().start(), oldNode, offset); |
| 575 Position end = updatePostionAfterAdoptingTextNodesMerged(selection().end(), |
| 576 oldNode, offset); |
| 577 updateSelectionIfNeeded(base, extent, start, end); |
559 } | 578 } |
560 | 579 |
561 // TODO(yosin): We should move |updatePostionAfterAdoptingTextNodeSplit()| | |
562 // to "SelectionEditor.cpp" since it used only in | |
563 // |SelectionEditor::didSplitTextNode()|. | |
564 static Position updatePostionAfterAdoptingTextNodeSplit( | 580 static Position updatePostionAfterAdoptingTextNodeSplit( |
565 const Position& position, | 581 const Position& position, |
566 const Text& oldNode) { | 582 const Text& oldNode) { |
567 if (!position.anchorNode() || position.anchorNode() != &oldNode || | 583 if (!position.anchorNode() || position.anchorNode() != &oldNode || |
568 !position.isOffsetInAnchor()) | 584 !position.isOffsetInAnchor()) |
569 return position; | 585 return position; |
570 // See: | 586 // See: |
571 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | 587 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation |
572 DCHECK_GE(position.offsetInContainerNode(), 0); | 588 DCHECK_GE(position.offsetInContainerNode(), 0); |
573 unsigned positionOffset = | 589 unsigned positionOffset = |
574 static_cast<unsigned>(position.offsetInContainerNode()); | 590 static_cast<unsigned>(position.offsetInContainerNode()); |
575 unsigned oldLength = oldNode.length(); | 591 unsigned oldLength = oldNode.length(); |
576 if (positionOffset <= oldLength) | 592 if (positionOffset <= oldLength) |
577 return position; | 593 return position; |
578 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); | 594 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); |
579 } | 595 } |
580 | 596 |
581 // TODO(yosin): We should move |SelectionEditor::didSplitTextNode()| to | 597 void FrameSelection::didSplitTextNode(const Text& oldNode) { |
582 // "SelectionEditor.cpp". | 598 if (isNone() || !oldNode.isConnected()) |
583 void SelectionEditor::didSplitTextNode(const Text& oldNode) { | |
584 if (m_selection.isNone() || !oldNode.isConnected()) { | |
585 didFinishDOMMutation(); | |
586 return; | 599 return; |
587 } | 600 Position base = |
588 const Position& newBase = | 601 updatePostionAfterAdoptingTextNodeSplit(selection().base(), oldNode); |
589 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_base, oldNode); | 602 Position extent = |
590 const Position& newExtent = | 603 updatePostionAfterAdoptingTextNodeSplit(selection().extent(), oldNode); |
591 updatePostionAfterAdoptingTextNodeSplit(m_selection.m_extent, oldNode); | 604 Position start = |
592 didFinishTextChange(newBase, newExtent); | 605 updatePostionAfterAdoptingTextNodeSplit(selection().start(), oldNode); |
| 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); |
593 } | 628 } |
594 | 629 |
595 void FrameSelection::didChangeFocus() { | 630 void FrameSelection::didChangeFocus() { |
596 // Hits in | 631 // Hits in |
597 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html | 632 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html |
598 DisableCompositingQueryAsserts disabler; | 633 DisableCompositingQueryAsserts disabler; |
599 updateAppearance(); | 634 updateAppearance(); |
600 } | 635 } |
601 | 636 |
602 static DispatchEventResult dispatchSelectStart( | 637 static DispatchEventResult dispatchSelectStart( |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
689 } | 724 } |
690 | 725 |
691 void FrameSelection::contextDestroyed(Document* document) { | 726 void FrameSelection::contextDestroyed(Document* document) { |
692 m_granularity = CharacterGranularity; | 727 m_granularity = CharacterGranularity; |
693 | 728 |
694 LayoutViewItem view = m_frame->contentLayoutItem(); | 729 LayoutViewItem view = m_frame->contentLayoutItem(); |
695 if (!view.isNull()) | 730 if (!view.isNull()) |
696 view.clearSelection(); | 731 view.clearSelection(); |
697 | 732 |
698 m_frame->editor().clearTypingStyle(); | 733 m_frame->editor().clearTypingStyle(); |
| 734 m_selectionEditor->documentDetached(*document); |
699 } | 735 } |
700 | 736 |
701 void FrameSelection::clearPreviousCaretVisualRect(const LayoutBlock& block) { | 737 void FrameSelection::clearPreviousCaretVisualRect(const LayoutBlock& block) { |
702 m_frameCaret->clearPreviousVisualRect(block); | 738 m_frameCaret->clearPreviousVisualRect(block); |
703 } | 739 } |
704 | 740 |
705 void FrameSelection::layoutBlockWillBeDestroyed(const LayoutBlock& block) { | 741 void FrameSelection::layoutBlockWillBeDestroyed(const LayoutBlock& block) { |
706 m_frameCaret->layoutBlockWillBeDestroyed(block); | 742 m_frameCaret->layoutBlockWillBeDestroyed(block); |
707 } | 743 } |
708 | 744 |
709 void FrameSelection::updateStyleAndLayoutIfNeeded() { | 745 void FrameSelection::updateStyleAndLayoutIfNeeded() { |
710 m_frameCaret->updateStyleAndLayoutIfNeeded(); | 746 m_frameCaret->updateStyleAndLayoutIfNeeded(); |
711 } | 747 } |
712 | 748 |
713 void FrameSelection::invalidatePaintIfNeeded( | 749 void FrameSelection::invalidatePaintIfNeeded( |
714 const LayoutBlock& block, | 750 const LayoutBlock& block, |
715 const PaintInvalidatorContext& context, | 751 const PaintInvalidatorContext& context, |
716 PaintInvalidationReason reason) { | 752 PaintInvalidationReason reason) { |
717 m_frameCaret->invalidatePaintIfNeeded(block, context, reason); | 753 m_frameCaret->invalidatePaintIfNeeded(block, context, reason); |
718 } | 754 } |
719 | 755 |
720 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const { | 756 bool FrameSelection::shouldPaintCaret(const LayoutBlock& block) const { |
721 DCHECK_GE(document().lifecycle().state(), DocumentLifecycle::LayoutClean); | 757 DCHECK(selection().isValidFor(document())); |
| 758 |
722 bool result = m_frameCaret->shouldPaintCaret(block); | 759 bool result = m_frameCaret->shouldPaintCaret(block); |
723 DCHECK(!result || (isCaret() && hasEditableStyle())); | 760 DCHECK(!result || (isCaret() && hasEditableStyle())); |
724 return result; | 761 return result; |
725 } | 762 } |
726 | 763 |
727 IntRect FrameSelection::absoluteCaretBounds() { | 764 IntRect FrameSelection::absoluteCaretBounds() { |
728 DCHECK(selection().isValidFor(*m_frame->document())); | 765 DCHECK(selection().isValidFor(*m_frame->document())); |
729 return m_frameCaret->absoluteCaretBounds(); | 766 return m_frameCaret->absoluteCaretBounds(); |
730 } | 767 } |
731 | 768 |
(...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1031 | 1068 |
1032 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { | 1069 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { |
1033 return m_pendingSelection->commit(layoutView); | 1070 return m_pendingSelection->commit(layoutView); |
1034 } | 1071 } |
1035 | 1072 |
1036 void FrameSelection::didLayout() { | 1073 void FrameSelection::didLayout() { |
1037 updateAppearance(); | 1074 updateAppearance(); |
1038 } | 1075 } |
1039 | 1076 |
1040 void FrameSelection::updateAppearance() { | 1077 void FrameSelection::updateAppearance() { |
1041 DCHECK(!m_frame->contentLayoutItem().isNull()); | 1078 m_frameCaret->updateAppearance(); |
1042 m_frameCaret->scheduleVisualUpdateForPaintInvalidationIfNeeded(); | 1079 |
| 1080 if (m_frame->contentLayoutItem().isNull()) |
| 1081 return; |
1043 m_pendingSelection->setHasPendingSelection(); | 1082 m_pendingSelection->setHasPendingSelection(); |
1044 } | 1083 } |
1045 | 1084 |
1046 void FrameSelection::notifyLayoutObjectOfSelectionChange( | 1085 void FrameSelection::notifyLayoutObjectOfSelectionChange( |
1047 EUserTriggered userTriggered) { | 1086 EUserTriggered userTriggered) { |
1048 if (TextControlElement* textControl = enclosingTextControl(start())) | 1087 if (TextControlElement* textControl = enclosingTextControl(start())) |
1049 textControl->selectionChanged(userTriggered == UserTriggered); | 1088 textControl->selectionChanged(userTriggered == UserTriggered); |
1050 } | 1089 } |
1051 | 1090 |
1052 // Helper function that tells whether a particular node is an element that has | 1091 // 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... |
1235 | 1274 |
1236 Document* document = m_frame->document(); | 1275 Document* document = m_frame->document(); |
1237 if (!isNone() || !(blink::hasEditableStyle(*document))) | 1276 if (!isNone() || !(blink::hasEditableStyle(*document))) |
1238 return; | 1277 return; |
1239 | 1278 |
1240 Element* documentElement = document->documentElement(); | 1279 Element* documentElement = document->documentElement(); |
1241 if (!documentElement) | 1280 if (!documentElement) |
1242 return; | 1281 return; |
1243 if (HTMLBodyElement* body = | 1282 if (HTMLBodyElement* body = |
1244 Traversal<HTMLBodyElement>::firstChild(*documentElement)) { | 1283 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 |
1245 setSelection(SelectionInDOMTree::Builder() | 1288 setSelection(SelectionInDOMTree::Builder() |
1246 .collapse(firstPositionInOrBeforeNode(body)) | 1289 .collapse(firstPositionInOrBeforeNode(body)) |
1247 .build()); | 1290 .build()); |
1248 } | 1291 } |
1249 } | 1292 } |
1250 | 1293 |
1251 // TODO(yoichio): We should have LocalFrame having FrameCaret, | 1294 // TODO(yoichio): We should have LocalFrame having FrameCaret, |
1252 // Editor and PendingSelection using FrameCaret directly | 1295 // Editor and PendingSelection using FrameCaret directly |
1253 // and get rid of this. | 1296 // and get rid of this. |
1254 bool FrameSelection::shouldShowBlockCursor() const { | 1297 bool FrameSelection::shouldShowBlockCursor() const { |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1345 FrameSelection::DoNotClearStrategy | UserTriggered, | 1388 FrameSelection::DoNotClearStrategy | UserTriggered, |
1346 CursorAlignOnScroll::IfNeeded, CharacterGranularity); | 1389 CursorAlignOnScroll::IfNeeded, CharacterGranularity); |
1347 } | 1390 } |
1348 | 1391 |
1349 // TODO(yosin): We should make |FrameSelection::moveRangeSelection()| to take | 1392 // TODO(yosin): We should make |FrameSelection::moveRangeSelection()| to take |
1350 // two |IntPoint| instead of two |VisiblePosition| like | 1393 // two |IntPoint| instead of two |VisiblePosition| like |
1351 // |moveRangeSelectionExtent()|. | 1394 // |moveRangeSelectionExtent()|. |
1352 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, | 1395 void FrameSelection::moveRangeSelection(const VisiblePosition& basePosition, |
1353 const VisiblePosition& extentPosition, | 1396 const VisiblePosition& extentPosition, |
1354 TextGranularity granularity) { | 1397 TextGranularity granularity) { |
1355 SelectionInDOMTree newSelection = | 1398 VisibleSelection newSelection = createVisibleSelection( |
1356 SelectionInDOMTree::Builder() | 1399 SelectionInDOMTree::Builder() |
1357 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), | 1400 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), |
1358 extentPosition.deepEquivalent()) | 1401 extentPosition.deepEquivalent()) |
1359 .setAffinity(basePosition.affinity()) | 1402 .setAffinity(basePosition.affinity()) |
1360 .setGranularity(granularity) | 1403 .setGranularity(granularity) |
1361 .setIsHandleVisible(isHandleVisible()) | 1404 .build()); |
1362 .build(); | |
1363 | 1405 |
1364 if (newSelection.isNone()) | 1406 if (newSelection.isNone()) |
1365 return; | 1407 return; |
1366 | 1408 |
1367 setSelection(newSelection, CloseTyping | ClearTypingStyle, | 1409 setSelection(newSelection, m_handleVisibility, CloseTyping | ClearTypingStyle, |
1368 CursorAlignOnScroll::IfNeeded, granularity); | 1410 CursorAlignOnScroll::IfNeeded, granularity); |
1369 } | 1411 } |
1370 | 1412 |
1371 void FrameSelection::updateIfNeeded() { | 1413 void FrameSelection::updateIfNeeded() { |
1372 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); | 1414 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); |
1373 m_selectionEditor->updateIfNeeded(); | 1415 m_selectionEditor->updateIfNeeded(); |
1374 } | 1416 } |
1375 | 1417 |
1376 void FrameSelection::setCaretVisible(bool caretIsVisible) { | 1418 void FrameSelection::setCaretVisible(bool caretIsVisible) { |
1377 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible | 1419 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible |
(...skipping 29 matching lines...) Expand all Loading... |
1407 } | 1449 } |
1408 | 1450 |
1409 void showTree(const blink::FrameSelection* sel) { | 1451 void showTree(const blink::FrameSelection* sel) { |
1410 if (sel) | 1452 if (sel) |
1411 sel->showTreeForThis(); | 1453 sel->showTreeForThis(); |
1412 else | 1454 else |
1413 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; | 1455 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; |
1414 } | 1456 } |
1415 | 1457 |
1416 #endif | 1458 #endif |
OLD | NEW |