Chromium Code Reviews| 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 |