| 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 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 #define EDIT_DEBUG 0 | 85 #define EDIT_DEBUG 0 |
| 86 | 86 |
| 87 namespace blink { | 87 namespace blink { |
| 88 | 88 |
| 89 using namespace HTMLNames; | 89 using namespace HTMLNames; |
| 90 | 90 |
| 91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) { | 91 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) { |
| 92 return frame->editor().behavior().shouldConsiderSelectionAsDirectional(); | 92 return frame->editor().behavior().shouldConsiderSelectionAsDirectional(); |
| 93 } | 93 } |
| 94 | 94 |
| 95 FrameSelection* FrameSelection::create(LocalFrame& frame) { |
| 96 return new FrameSelection(frame); |
| 97 } |
| 98 |
| 95 FrameSelection::FrameSelection(LocalFrame& frame) | 99 FrameSelection::FrameSelection(LocalFrame& frame) |
| 96 : m_frame(frame), | 100 : m_frame(frame), |
| 97 m_pendingSelection(PendingSelection::create(*this)), | 101 m_pendingSelection(PendingSelection::create(*this)), |
| 98 m_selectionEditor(SelectionEditor::create(frame)), | 102 m_selectionEditor(SelectionEditor::create(frame)), |
| 99 m_granularity(CharacterGranularity), | 103 m_granularity(CharacterGranularity), |
| 100 m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()), | 104 m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()), |
| 101 m_focused(frame.page() && | 105 m_focused(frame.page() && |
| 102 frame.page()->focusController().focusedFrame() == frame), | 106 frame.page()->focusController().focusedFrame() == frame), |
| 103 m_frameCaret(new FrameCaret(frame, *m_selectionEditor)) {} | 107 m_frameCaret(new FrameCaret(frame, *m_selectionEditor)) {} |
| 104 | 108 |
| 105 FrameSelection::~FrameSelection() {} | 109 FrameSelection::~FrameSelection() {} |
| 106 | 110 |
| 107 const DisplayItemClient& FrameSelection::caretDisplayItemClientForTesting() | 111 const DisplayItemClient& FrameSelection::caretDisplayItemClientForTesting() |
| 108 const { | 112 const { |
| 109 return *m_frameCaret; | 113 return m_frameCaret->displayItemClient(); |
| 110 } | 114 } |
| 111 | 115 |
| 112 const Document& FrameSelection::document() const { | 116 Document& FrameSelection::document() const { |
| 113 DCHECK(m_document); | 117 DCHECK(lifecycleContext()); |
| 114 return *m_document; | 118 return *lifecycleContext(); |
| 115 } | 119 } |
| 116 | 120 |
| 117 Document& FrameSelection::document() { | 121 const VisibleSelection& FrameSelection::computeVisibleSelectionInDOMTree() |
| 118 DCHECK(m_document); | 122 const { |
| 119 return *m_document; | 123 return m_selectionEditor->computeVisibleSelectionInDOMTree(); |
| 120 } | 124 } |
| 121 | 125 |
| 122 // TODO(yosin): To avoid undefined symbols in clang, we explicitly | 126 const VisibleSelectionInFlatTree& |
| 123 // have specialized version of |FrameSelection::visibleSelection<Strategy>| | 127 FrameSelection::computeVisibleSelectionInFlatTree() const { |
| 124 // before |FrameSelection::selection()| which refers this. | 128 return m_selectionEditor->computeVisibleSelectionInFlatTree(); |
| 125 template <> | |
| 126 const VisibleSelection& FrameSelection::visibleSelection<EditingStrategy>() | |
| 127 const { | |
| 128 return m_selectionEditor->visibleSelection<EditingStrategy>(); | |
| 129 } | 129 } |
| 130 | 130 |
| 131 template <> | 131 const SelectionInDOMTree& FrameSelection::selectionInDOMTree() const { |
| 132 const VisibleSelectionInFlatTree& | 132 return m_selectionEditor->selectionInDOMTree(); |
| 133 FrameSelection::visibleSelection<EditingInFlatTreeStrategy>() const { | |
| 134 return m_selectionEditor->visibleSelection<EditingInFlatTreeStrategy>(); | |
| 135 } | 133 } |
| 136 | 134 |
| 137 Element* FrameSelection::rootEditableElementOrDocumentElement() const { | 135 Element* FrameSelection::rootEditableElementOrDocumentElement() const { |
| 138 Element* selectionRoot = selection().rootEditableElement(); | 136 Element* selectionRoot = selection().rootEditableElement(); |
| 139 return selectionRoot ? selectionRoot : document().documentElement(); | 137 return selectionRoot ? selectionRoot : document().documentElement(); |
| 140 } | 138 } |
| 141 | 139 |
| 142 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { | 140 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const { |
| 143 Element* selectionRoot = selection().rootEditableElement(); | 141 Element* selectionRoot = selection().rootEditableElement(); |
| 144 if (selectionRoot) | 142 if (selectionRoot) |
| 145 return selectionRoot; | 143 return selectionRoot; |
| 146 | 144 |
| 147 Node* node = selection().base().computeContainerNode(); | 145 Node* node = selection().base().computeContainerNode(); |
| 148 return node ? &node->treeScope().rootNode() : 0; | 146 return node ? &node->treeScope().rootNode() : 0; |
| 149 } | 147 } |
| 150 | 148 |
| 149 // TODO(yosin): We should rename |FrameSelection::selection()| to |
| 150 // |selectionDeprecated()|. |
| 151 const VisibleSelection& FrameSelection::selection() const { | 151 const VisibleSelection& FrameSelection::selection() const { |
| 152 return visibleSelection<EditingStrategy>(); | 152 // TODO(yosin): We should hoist updateStyleAndLayoutIgnorePendingStylesheets |
| 153 } | 153 // to caller. See http://crbug.com/590369 for more details. |
| 154 | 154 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 155 const VisibleSelectionInFlatTree& FrameSelection::selectionInFlatTree() const { | 155 return computeVisibleSelectionInDOMTree(); |
| 156 return visibleSelection<EditingInFlatTreeStrategy>(); | |
| 157 } | 156 } |
| 158 | 157 |
| 159 void FrameSelection::moveCaretSelection(const IntPoint& point) { | 158 void FrameSelection::moveCaretSelection(const IntPoint& point) { |
| 160 DCHECK(!document().needsLayoutTreeUpdate()); | 159 DCHECK(!document().needsLayoutTreeUpdate()); |
| 161 | 160 |
| 162 Element* const editable = rootEditableElement(); | 161 Element* const editable = rootEditableElement(); |
| 163 if (!editable) | 162 if (!editable) |
| 164 return; | 163 return; |
| 165 | 164 |
| 166 const VisiblePosition position = | 165 const VisiblePosition position = |
| 167 visiblePositionForContentsPoint(point, frame()); | 166 visiblePositionForContentsPoint(point, frame()); |
| 168 SelectionInDOMTree::Builder builder; | 167 SelectionInDOMTree::Builder builder; |
| 169 builder.setIsDirectional(selection().isDirectional()); | 168 builder.setIsDirectional(selection().isDirectional()); |
| 170 if (position.isNotNull()) | 169 if (position.isNotNull()) |
| 171 builder.collapse(position.toPositionWithAffinity()); | 170 builder.collapse(position.toPositionWithAffinity()); |
| 172 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); | 171 setSelection(builder.build(), CloseTyping | ClearTypingStyle | UserTriggered); |
| 173 } | 172 } |
| 174 | 173 |
| 175 template <typename Strategy> | 174 void FrameSelection::setSelection(const SelectionInDOMTree& passedSelection, |
| 176 void FrameSelection::setSelectionAlgorithm( | 175 SetSelectionOptions options, |
| 177 const VisibleSelectionTemplate<Strategy>& newSelection, | 176 CursorAlignOnScroll align, |
| 178 SetSelectionOptions options, | 177 TextGranularity granularity) { |
| 179 CursorAlignOnScroll align, | |
| 180 TextGranularity granularity) { | |
| 181 DCHECK(isAvailable()); | 178 DCHECK(isAvailable()); |
| 182 DCHECK(newSelection.isValidFor(document())); | 179 passedSelection.assertValidFor(document()); |
| 183 const Document& currentDocument = document(); | 180 |
| 181 SelectionInDOMTree::Builder builder(passedSelection); |
| 182 if (shouldAlwaysUseDirectionalSelection(m_frame)) |
| 183 builder.setIsDirectional(true); |
| 184 SelectionInDOMTree newSelection = builder.build(); |
| 184 if (m_granularityStrategy && | 185 if (m_granularityStrategy && |
| 185 (options & FrameSelection::DoNotClearStrategy) == 0) | 186 (options & FrameSelection::DoNotClearStrategy) == 0) |
| 186 m_granularityStrategy->Clear(); | 187 m_granularityStrategy->Clear(); |
| 187 bool closeTyping = options & CloseTyping; | 188 bool closeTyping = options & CloseTyping; |
| 188 bool shouldClearTypingStyle = options & ClearTypingStyle; | 189 bool shouldClearTypingStyle = options & ClearTypingStyle; |
| 189 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); | |
| 190 | |
| 191 // TODO(editing-dev): We should rename variable |s| to another name to avoid | |
| 192 // using one letter variable name. | |
| 193 VisibleSelectionTemplate<Strategy> s = newSelection; | |
| 194 if (shouldAlwaysUseDirectionalSelection(m_frame)) | |
| 195 s.setIsDirectional(true); | |
| 196 | 190 |
| 197 m_granularity = granularity; | 191 m_granularity = granularity; |
| 198 | 192 |
| 199 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 193 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
| 200 // |Editor| class. | 194 // |Editor| class. |
| 201 if (closeTyping) | 195 if (closeTyping) |
| 202 TypingCommand::closeTyping(m_frame); | 196 TypingCommand::closeTyping(m_frame); |
| 203 | 197 |
| 204 if (shouldClearTypingStyle) | 198 if (shouldClearTypingStyle) |
| 205 clearTypingStyle(); | 199 clearTypingStyle(); |
| 206 | 200 |
| 207 if (m_selectionEditor->visibleSelection<Strategy>() == s) { | 201 const SelectionInDOMTree oldSelectionInDOMTree = |
| 208 // Even if selection was not changed, selection offsets may have been | 202 m_selectionEditor->selectionInDOMTree(); |
| 209 // changed. | 203 if (oldSelectionInDOMTree == newSelection) |
| 210 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid(); | |
| 211 notifyLayoutObjectOfSelectionChange(userTriggered); | |
| 212 return; | 204 return; |
| 213 } | 205 m_selectionEditor->setSelection(newSelection); |
| 214 | 206 |
| 215 const VisibleSelectionTemplate<Strategy> oldSelection = | 207 // TODO(yosin): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 216 visibleSelection<Strategy>(); | 208 // needs to be audited. see http://crbug.com/590369 for more details. |
| 217 const Position& oldSelectionStart = selection().start(); | 209 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 218 | 210 |
| 219 m_selectionEditor->setVisibleSelection(s, options); | 211 const Document& currentDocument = document(); |
| 220 m_frameCaret->setCaretRectNeedsUpdate(); | 212 // TODO(yosin): We should get rid of unsued |options| for |
| 213 // |Editor::respondToChangedSelection()|. |
| 214 // Note: Since, setting focus can modify DOM tree, we should use |
| 215 // |oldSelection| before setting focus |
| 216 m_frame->editor().respondToChangedSelection( |
| 217 createVisibleSelection(oldSelectionInDOMTree).start(), options); |
| 218 DCHECK_EQ(currentDocument, document()); |
| 221 | 219 |
| 222 if (!s.isNone() && !(options & DoNotSetFocus)) { | 220 if (!selection().isNone() && !(options & DoNotSetFocus)) { |
| 223 setFocusedNodeIfNeeded(); | 221 setFocusedNodeIfNeeded(); |
| 224 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and | 222 // |setFocusedNodeIfNeeded()| dispatches sync events "FocusOut" and |
| 225 // "FocusIn", |m_frame| may associate to another document. | 223 // "FocusIn", |m_frame| may associate to another document. |
| 226 if (!isAvailable() || document() != currentDocument) { | 224 if (!isAvailable() || document() != currentDocument) { |
| 227 // Once we get test case to reach here, we should change this | 225 // Once we get test case to reach here, we should change this |
| 228 // if-statement to |DCHECK()|. | 226 // if-statement to |DCHECK()|. |
| 229 NOTREACHED(); | 227 NOTREACHED(); |
| 230 return; | 228 return; |
| 231 } | 229 } |
| 232 } | 230 } |
| 233 | 231 updateAppearance(); |
| 234 if (!(options & DoNotUpdateAppearance)) { | |
| 235 // Hits in | |
| 236 // compositing/overflow/do-not-paint-outline-into-composited-scrolling-conte
nts.html | |
| 237 DisableCompositingQueryAsserts disabler; | |
| 238 m_frameCaret->stopCaretBlinkTimer(); | |
| 239 updateAppearance(); | |
| 240 } | |
| 241 | 232 |
| 242 // Always clear the x position used for vertical arrow navigation. | 233 // Always clear the x position used for vertical arrow navigation. |
| 243 // It will be restored by the vertical arrow navigation code if necessary. | 234 // It will be restored by the vertical arrow navigation code if necessary. |
| 244 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); | 235 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); |
| 236 // TODO(yosin): Can we move this to at end of this function? |
| 245 // This may dispatch a synchronous focus-related events. | 237 // This may dispatch a synchronous focus-related events. |
| 246 selectFrameElementInParentIfFullySelected(); | 238 selectFrameElementInParentIfFullySelected(); |
| 247 if (!isAvailable() || document() != currentDocument) { | 239 if (!isAvailable() || document() != currentDocument) { |
| 248 // editing/selection/selectallchildren-crash.html and | 240 // editing/selection/selectallchildren-crash.html and |
| 249 // editing/selection/longpress-selection-in-iframe-removed-crash.html | 241 // editing/selection/longpress-selection-in-iframe-removed-crash.html |
| 250 // reach here. | 242 // reach here. |
| 251 return; | 243 return; |
| 252 } | 244 } |
| 245 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); |
| 253 notifyLayoutObjectOfSelectionChange(userTriggered); | 246 notifyLayoutObjectOfSelectionChange(userTriggered); |
| 254 // If the selections are same in the DOM tree but not in the flat tree, | |
| 255 // don't fire events. For example, if the selection crosses shadow tree | |
| 256 // boundary, selection for the DOM tree is shrunk while that for the | |
| 257 // flat tree is not. Additionally, this case occurs in some edge cases. | |
| 258 // See also: editing/pasteboard/4076267-3.html | |
| 259 if (oldSelection == m_selectionEditor->visibleSelection<Strategy>()) { | |
| 260 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid(); | |
| 261 return; | |
| 262 } | |
| 263 m_frame->editor().respondToChangedSelection(oldSelectionStart, options); | |
| 264 if (userTriggered == UserTriggered) { | 247 if (userTriggered == UserTriggered) { |
| 265 ScrollAlignment alignment; | 248 ScrollAlignment alignment; |
| 266 | 249 |
| 267 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) | 250 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) |
| 268 alignment = (align == CursorAlignOnScroll::Always) | 251 alignment = (align == CursorAlignOnScroll::Always) |
| 269 ? ScrollAlignment::alignCenterAlways | 252 ? ScrollAlignment::alignCenterAlways |
| 270 : ScrollAlignment::alignCenterIfNeeded; | 253 : ScrollAlignment::alignCenterIfNeeded; |
| 271 else | 254 else |
| 272 alignment = (align == CursorAlignOnScroll::Always) | 255 alignment = (align == CursorAlignOnScroll::Always) |
| 273 ? ScrollAlignment::alignTopAlways | 256 ? ScrollAlignment::alignTopAlways |
| 274 : ScrollAlignment::alignToEdgeIfNeeded; | 257 : ScrollAlignment::alignToEdgeIfNeeded; |
| 275 | 258 |
| 259 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 260 // needs to be audited. See http://crbug.com/590369 for more details. |
| 261 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 262 |
| 276 revealSelection(alignment, RevealExtent); | 263 revealSelection(alignment, RevealExtent); |
| 277 } | 264 } |
| 278 | 265 |
| 279 notifyAccessibilityForSelectionChange(); | 266 notifyAccessibilityForSelectionChange(); |
| 280 notifyCompositorForSelectionChange(); | 267 notifyCompositorForSelectionChange(); |
| 281 notifyEventHandlerForSelectionChange(); | 268 notifyEventHandlerForSelectionChange(); |
| 282 m_frame->domWindow()->enqueueDocumentEvent( | 269 m_frame->domWindow()->enqueueDocumentEvent( |
| 283 Event::create(EventTypeNames::selectionchange)); | 270 Event::create(EventTypeNames::selectionchange)); |
| 284 } | 271 } |
| 285 | 272 |
| 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), options, align, | |
| 301 granularity); | |
| 302 } | |
| 303 | |
| 304 // TODO(yosin): We will make |selectionInFlatTree| version of |SetSelection()| | |
| 305 // as primary function instead of wrapper. | |
| 306 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, | 273 void FrameSelection::setSelection(const SelectionInFlatTree& newSelection, |
| 307 SetSelectionOptions options, | 274 SetSelectionOptions options, |
| 308 CursorAlignOnScroll align, | 275 CursorAlignOnScroll align, |
| 309 TextGranularity granularity) { | 276 TextGranularity granularity) { |
| 310 if (!newSelection.isNone()) { | 277 newSelection.assertValidFor(document()); |
| 311 // TODO(editing-dev): The use of | 278 SelectionInDOMTree::Builder builder; |
| 312 // updateStyleAndLayoutIgnorePendingStylesheets | 279 builder.setAffinity(newSelection.affinity()) |
| 313 // needs to be audited. See http://crbug.com/590369 for more details. | 280 .setBaseAndExtent(toPositionInDOMTree(newSelection.base()), |
| 314 newSelection.base() | 281 toPositionInDOMTree(newSelection.extent())) |
| 315 .document() | 282 .setGranularity(newSelection.granularity()) |
| 316 ->updateStyleAndLayoutIgnorePendingStylesheets(); | 283 .setIsDirectional(newSelection.isDirectional()) |
| 317 } | 284 .setHasTrailingWhitespace(newSelection.hasTrailingWhitespace()); |
| 318 setSelection(createVisibleSelection(newSelection), options, align, | 285 return setSelection(builder.build(), options, align, granularity); |
| 319 granularity); | |
| 320 } | 286 } |
| 321 | 287 |
| 322 void FrameSelection::setSelection(const VisibleSelection& newSelection, | 288 void FrameSelection::setSelection(const VisibleSelection& newSelection, |
| 323 SetSelectionOptions options, | 289 SetSelectionOptions options, |
| 324 CursorAlignOnScroll align, | 290 CursorAlignOnScroll align, |
| 325 TextGranularity granularity) { | 291 TextGranularity granularity) { |
| 326 setSelectionAlgorithm<EditingStrategy>(newSelection, options, align, | 292 setSelection(newSelection.asSelection(), options, align, granularity); |
| 327 granularity); | |
| 328 } | 293 } |
| 329 | 294 |
| 330 void FrameSelection::setSelection( | 295 void FrameSelection::setSelection( |
| 331 const VisibleSelectionInFlatTree& newSelection, | 296 const VisibleSelectionInFlatTree& newSelection, |
| 332 SetSelectionOptions options, | 297 SetSelectionOptions options, |
| 333 CursorAlignOnScroll align, | 298 CursorAlignOnScroll align, |
| 334 TextGranularity granularity) { | 299 TextGranularity granularity) { |
| 335 setSelectionAlgorithm<EditingInFlatTreeStrategy>(newSelection, options, align, | 300 setSelection(newSelection.asSelection(), options, align, granularity); |
| 336 granularity); | |
| 337 } | |
| 338 | |
| 339 static bool removingNodeRemovesPosition(Node& node, const Position& position) { | |
| 340 if (!position.anchorNode()) | |
| 341 return false; | |
| 342 | |
| 343 if (position.anchorNode() == node) | |
| 344 return true; | |
| 345 | |
| 346 if (!node.isElementNode()) | |
| 347 return false; | |
| 348 | |
| 349 Element& element = toElement(node); | |
| 350 return element.isShadowIncludingInclusiveAncestorOf(position.anchorNode()); | |
| 351 } | |
| 352 | |
| 353 static Position computePositionForChildrenRemoval(const Position& position, | |
| 354 ContainerNode& container) { | |
| 355 Node* node = position.computeContainerNode(); | |
| 356 if (container.containsIncludingHostElements(*node)) | |
| 357 return Position::firstPositionInNode(&container); | |
| 358 return position; | |
| 359 } | 301 } |
| 360 | 302 |
| 361 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { | 303 void FrameSelection::nodeChildrenWillBeRemoved(ContainerNode& container) { |
| 362 if (isNone() || !container.inActiveDocument()) | 304 if (!container.inActiveDocument()) |
| 363 return; | 305 return; |
| 364 const Position& oldStart = selection().start(); | |
| 365 const Position& newStart = | |
| 366 computePositionForChildrenRemoval(oldStart, container); | |
| 367 const Position& oldEnd = selection().end(); | |
| 368 const Position& newEnd = computePositionForChildrenRemoval(oldEnd, container); | |
| 369 const Position& oldBase = selection().base(); | |
| 370 const Position& newBase = | |
| 371 computePositionForChildrenRemoval(oldBase, container); | |
| 372 const Position& oldExtent = selection().extent(); | |
| 373 const Position& newExtent = | |
| 374 computePositionForChildrenRemoval(oldExtent, container); | |
| 375 if (newStart == oldStart && newEnd == oldEnd && newBase == oldBase && | |
| 376 newExtent == oldExtent) | |
| 377 return; | |
| 378 if (selection().isBaseFirst()) | |
| 379 m_selectionEditor->setWithoutValidation(newStart, newEnd); | |
| 380 else | |
| 381 m_selectionEditor->setWithoutValidation(newEnd, newStart); | |
| 382 m_frameCaret->setCaretRectNeedsUpdate(); | |
| 383 if (document().isRunningExecCommand()) | |
| 384 return; | |
| 385 TypingCommand::closeTyping(m_frame); | |
| 386 } | |
| 387 | |
| 388 void FrameSelection::nodeWillBeRemoved(Node& node) { | |
| 389 // There can't be a selection inside a fragment, so if a fragment's node is | |
| 390 // being removed, the selection in the document that created the fragment | |
| 391 // needs no adjustment. | |
| 392 if (isNone() || !node.inActiveDocument()) | |
| 393 return; | |
| 394 | |
| 395 respondToNodeModification( | |
| 396 node, removingNodeRemovesPosition(node, selection().base()), | |
| 397 removingNodeRemovesPosition(node, selection().extent()), | |
| 398 removingNodeRemovesPosition(node, selection().start()), | |
| 399 removingNodeRemovesPosition(node, selection().end())); | |
| 400 | |
| 401 m_frameCaret->nodeWillBeRemoved(node); | |
| 402 } | |
| 403 | |
| 404 static SelectionState selectionStateOf(const Node& node) { | |
| 405 const LayoutObject* layoutObject = node.layoutObject(); | |
| 406 if (!layoutObject) | |
| 407 return SelectionNone; | |
| 408 return layoutObject->getSelectionState(); | |
| 409 } | |
| 410 | |
| 411 void FrameSelection::respondToNodeModification(Node& node, | |
| 412 bool baseRemoved, | |
| 413 bool extentRemoved, | |
| 414 bool startRemoved, | |
| 415 bool endRemoved) { | |
| 416 DCHECK(node.document().isActive()) << node; | |
| 417 | |
| 418 bool clearLayoutTreeSelection = false; | |
| 419 bool clearDOMTreeSelection = false; | |
| 420 | |
| 421 if (startRemoved || endRemoved) { | |
| 422 Position start = selection().start(); | |
| 423 Position end = selection().end(); | |
| 424 if (startRemoved) | |
| 425 updatePositionForNodeRemoval(start, node); | |
| 426 if (endRemoved) | |
| 427 updatePositionForNodeRemoval(end, node); | |
| 428 | |
| 429 if (Position::commonAncestorTreeScope(start, end) && start.isNotNull() && | |
| 430 end.isNotNull()) { | |
| 431 if (selection().isBaseFirst()) | |
| 432 m_selectionEditor->setWithoutValidation(start, end); | |
| 433 else | |
| 434 m_selectionEditor->setWithoutValidation(end, start); | |
| 435 } else { | |
| 436 clearDOMTreeSelection = true; | |
| 437 } | |
| 438 | |
| 439 clearLayoutTreeSelection = true; | |
| 440 } else if (baseRemoved || extentRemoved) { | |
| 441 // The base and/or extent are about to be removed, but the start and end | |
| 442 // aren't. Change the base and extent to the start and end, but don't | |
| 443 // re-validate the selection, since doing so could move the start and end | |
| 444 // into the node that is about to be removed. | |
| 445 if (selection().isBaseFirst()) | |
| 446 m_selectionEditor->setWithoutValidation(selection().start(), | |
| 447 selection().end()); | |
| 448 else | |
| 449 m_selectionEditor->setWithoutValidation(selection().end(), | |
| 450 selection().start()); | |
| 451 } else if (selectionStateOf(node) != SelectionNone) { | |
| 452 // When node to be removed is part of selection, we invalidate | |
| 453 // selection to paint again. | |
| 454 // TODO(yosin): We should paint changed area only rather than whole | |
| 455 // selected range. | |
| 456 clearLayoutTreeSelection = true; | |
| 457 } | |
| 458 | |
| 459 if (clearLayoutTreeSelection) | |
| 460 selection().start().document()->layoutViewItem().clearSelection(); | |
| 461 | |
| 462 if (clearDOMTreeSelection) | |
| 463 setSelection(SelectionInDOMTree(), DoNotSetFocus); | |
| 464 m_frameCaret->setCaretRectNeedsUpdate(); | |
| 465 | |
| 466 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 306 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
| 467 // |Editor| class. | 307 // |Editor| class. |
| 468 if (!document().isRunningExecCommand()) | 308 if (!document().isRunningExecCommand()) |
| 469 TypingCommand::closeTyping(m_frame); | 309 TypingCommand::closeTyping(m_frame); |
| 470 } | 310 } |
| 471 | 311 |
| 472 static Position updatePositionAfterAdoptingTextReplacement( | 312 void FrameSelection::nodeWillBeRemoved(Node& node) { |
| 473 const Position& position, | 313 // There can't be a selection inside a fragment, so if a fragment's node is |
| 474 CharacterData* node, | 314 // being removed, |
| 475 unsigned offset, | 315 // the selection in the document that created the fragment needs no |
| 476 unsigned oldLength, | 316 // adjustment. |
| 477 unsigned newLength) { | 317 if (!node.inActiveDocument()) |
| 478 if (!position.anchorNode() || position.anchorNode() != node || | |
| 479 !position.isOffsetInAnchor()) | |
| 480 return position; | |
| 481 | |
| 482 // See: | |
| 483 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | |
| 484 DCHECK_GE(position.offsetInContainerNode(), 0); | |
| 485 unsigned positionOffset = | |
| 486 static_cast<unsigned>(position.offsetInContainerNode()); | |
| 487 // Replacing text can be viewed as a deletion followed by insertion. | |
| 488 if (positionOffset >= offset && positionOffset <= offset + oldLength) | |
| 489 positionOffset = offset; | |
| 490 | |
| 491 // Adjust the offset if the position is after the end of the deleted contents | |
| 492 // (positionOffset > offset + oldLength) to avoid having a stale offset. | |
| 493 if (positionOffset > offset + oldLength) | |
| 494 positionOffset = positionOffset - oldLength + newLength; | |
| 495 | |
| 496 // Due to case folding | |
| 497 // (http://unicode.org/Public/UCD/latest/ucd/CaseFolding.txt), LayoutText | |
| 498 // length may be different from Text length. A correct implementation would | |
| 499 // translate the LayoutText offset to a Text offset; this is just a safety | |
| 500 // precaution to avoid offset values that run off the end of the Text. | |
| 501 if (positionOffset > node->length()) | |
| 502 positionOffset = node->length(); | |
| 503 | |
| 504 // CharacterNode in VisibleSelection must be Text node, because Comment | |
| 505 // and ProcessingInstruction node aren't visible. | |
| 506 return Position(toText(node), positionOffset); | |
| 507 } | |
| 508 | |
| 509 void FrameSelection::didUpdateCharacterData(CharacterData* node, | |
| 510 unsigned offset, | |
| 511 unsigned oldLength, | |
| 512 unsigned newLength) { | |
| 513 // The fragment check is a performance optimization. See | |
| 514 // http://trac.webkit.org/changeset/30062. | |
| 515 if (isNone() || !node || !node->isConnected()) | |
| 516 return; | |
| 517 | |
| 518 Position base = updatePositionAfterAdoptingTextReplacement( | |
| 519 selection().base(), node, offset, oldLength, newLength); | |
| 520 Position extent = updatePositionAfterAdoptingTextReplacement( | |
| 521 selection().extent(), node, offset, oldLength, newLength); | |
| 522 Position start = updatePositionAfterAdoptingTextReplacement( | |
| 523 selection().start(), node, offset, oldLength, newLength); | |
| 524 Position end = updatePositionAfterAdoptingTextReplacement( | |
| 525 selection().end(), node, offset, oldLength, newLength); | |
| 526 updateSelectionIfNeeded(base, extent, start, end); | |
| 527 } | |
| 528 | |
| 529 static Position updatePostionAfterAdoptingTextNodesMerged( | |
| 530 const Position& position, | |
| 531 const Text& oldNode, | |
| 532 unsigned offset) { | |
| 533 if (!position.anchorNode() || !position.isOffsetInAnchor()) | |
| 534 return position; | |
| 535 | |
| 536 DCHECK_GE(position.offsetInContainerNode(), 0); | |
| 537 unsigned positionOffset = | |
| 538 static_cast<unsigned>(position.offsetInContainerNode()); | |
| 539 | |
| 540 if (position.anchorNode() == &oldNode) | |
| 541 return Position(toText(oldNode.previousSibling()), positionOffset + offset); | |
| 542 | |
| 543 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset) | |
| 544 return Position(toText(oldNode.previousSibling()), offset); | |
| 545 | |
| 546 return position; | |
| 547 } | |
| 548 | |
| 549 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset) { | |
| 550 if (isNone() || !oldNode.isConnected()) | |
| 551 return; | |
| 552 Position base = updatePostionAfterAdoptingTextNodesMerged(selection().base(), | |
| 553 oldNode, offset); | |
| 554 Position extent = updatePostionAfterAdoptingTextNodesMerged( | |
| 555 selection().extent(), oldNode, offset); | |
| 556 Position start = updatePostionAfterAdoptingTextNodesMerged( | |
| 557 selection().start(), oldNode, offset); | |
| 558 Position end = updatePostionAfterAdoptingTextNodesMerged(selection().end(), | |
| 559 oldNode, offset); | |
| 560 updateSelectionIfNeeded(base, extent, start, end); | |
| 561 } | |
| 562 | |
| 563 static Position updatePostionAfterAdoptingTextNodeSplit( | |
| 564 const Position& position, | |
| 565 const Text& oldNode) { | |
| 566 if (!position.anchorNode() || position.anchorNode() != &oldNode || | |
| 567 !position.isOffsetInAnchor()) | |
| 568 return position; | |
| 569 // See: | |
| 570 // http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-
Mutation | |
| 571 DCHECK_GE(position.offsetInContainerNode(), 0); | |
| 572 unsigned positionOffset = | |
| 573 static_cast<unsigned>(position.offsetInContainerNode()); | |
| 574 unsigned oldLength = oldNode.length(); | |
| 575 if (positionOffset <= oldLength) | |
| 576 return position; | |
| 577 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); | |
| 578 } | |
| 579 | |
| 580 void FrameSelection::didSplitTextNode(const Text& oldNode) { | |
| 581 if (isNone() || !oldNode.isConnected()) | |
| 582 return; | |
| 583 Position base = | |
| 584 updatePostionAfterAdoptingTextNodeSplit(selection().base(), oldNode); | |
| 585 Position extent = | |
| 586 updatePostionAfterAdoptingTextNodeSplit(selection().extent(), oldNode); | |
| 587 Position start = | |
| 588 updatePostionAfterAdoptingTextNodeSplit(selection().start(), oldNode); | |
| 589 Position end = | |
| 590 updatePostionAfterAdoptingTextNodeSplit(selection().end(), oldNode); | |
| 591 updateSelectionIfNeeded(base, extent, start, end); | |
| 592 } | |
| 593 | |
| 594 void FrameSelection::updateSelectionIfNeeded(const Position& base, | |
| 595 const Position& extent, | |
| 596 const Position& start, | |
| 597 const Position& end) { | |
| 598 if (base == selection().base() && extent == selection().extent() && | |
| 599 start == selection().start() && end == selection().end()) | |
| 600 return; | 318 return; |
| 601 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to | 319 // TODO(yosin): We should move to call |TypingCommand::closeTyping()| to |
| 602 // |Editor| class. | 320 // |Editor| class. |
| 603 if (!document().isRunningExecCommand()) | 321 if (!document().isRunningExecCommand()) |
| 604 TypingCommand::closeTyping(m_frame); | 322 TypingCommand::closeTyping(m_frame); |
| 605 VisibleSelection newSelection; | |
| 606 if (selection().isBaseFirst()) | |
| 607 newSelection.setWithoutValidation(start, end); | |
| 608 else | |
| 609 newSelection.setWithoutValidation(end, start); | |
| 610 setSelection(newSelection, DoNotSetFocus); | |
| 611 } | 323 } |
| 612 | 324 |
| 613 void FrameSelection::didChangeFocus() { | 325 void FrameSelection::didChangeFocus() { |
| 614 // Hits in | 326 // Hits in |
| 615 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html | 327 // virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disable
d.html |
| 616 DisableCompositingQueryAsserts disabler; | 328 DisableCompositingQueryAsserts disabler; |
| 617 updateAppearance(); | 329 updateAppearance(); |
| 618 } | 330 } |
| 619 | 331 |
| 620 static DispatchEventResult dispatchSelectStart( | 332 static DispatchEventResult dispatchSelectStart( |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 689 | 401 |
| 690 m_granularity = CharacterGranularity; | 402 m_granularity = CharacterGranularity; |
| 691 | 403 |
| 692 return true; | 404 return true; |
| 693 } | 405 } |
| 694 | 406 |
| 695 void FrameSelection::clear() { | 407 void FrameSelection::clear() { |
| 696 m_granularity = CharacterGranularity; | 408 m_granularity = CharacterGranularity; |
| 697 if (m_granularityStrategy) | 409 if (m_granularityStrategy) |
| 698 m_granularityStrategy->Clear(); | 410 m_granularityStrategy->Clear(); |
| 699 setSelection(SelectionInDOMTree()); | 411 setSelection(VisibleSelection()); |
| 700 } | 412 } |
| 701 | 413 |
| 702 void FrameSelection::documentAttached(Document* document) { | 414 void FrameSelection::documentAttached(Document* document) { |
| 703 DCHECK(document); | 415 DCHECK(document); |
| 704 DCHECK(!m_document) << "FrameSelection is already attached to " << m_document; | 416 m_frameCaret->documentAttached(document); |
| 705 m_document = document; | |
| 706 m_useSecureKeyboardEntryWhenActive = false; | |
| 707 m_selectionEditor->documentAttached(document); | 417 m_selectionEditor->documentAttached(document); |
| 418 setContext(document); |
| 708 } | 419 } |
| 709 | 420 |
| 710 void FrameSelection::documentDetached(const Document& document) { | 421 void FrameSelection::contextDestroyed(Document*) { |
| 711 DCHECK_EQ(m_document, document); | |
| 712 m_document = nullptr; | |
| 713 m_granularity = CharacterGranularity; | 422 m_granularity = CharacterGranularity; |
| 714 | 423 |
| 715 LayoutViewItem view = m_frame->contentLayoutItem(); | 424 LayoutViewItem view = m_frame->contentLayoutItem(); |
| 716 if (!view.isNull()) | 425 if (!view.isNull()) |
| 717 view.clearSelection(); | 426 view.clearSelection(); |
| 718 | 427 |
| 719 clearTypingStyle(); | 428 clearTypingStyle(); |
| 720 m_selectionEditor->documentDetached(document); | |
| 721 m_frameCaret->documentDetached(); | |
| 722 } | 429 } |
| 723 | 430 |
| 724 LayoutBlock* FrameSelection::caretLayoutObject() const { | 431 LayoutBlock* FrameSelection::caretLayoutObject() const { |
| 725 DCHECK(selection().isValidFor(document())); | 432 DCHECK(isAvailable()); |
| 726 if (!isCaret()) | 433 const VisibleSelection& selection = computeVisibleSelectionInDOMTree(); |
| 434 if (!selection.isCaret()) |
| 727 return nullptr; | 435 return nullptr; |
| 728 return CaretBase::caretLayoutObject(selection().start().anchorNode()); | 436 return CaretBase::caretLayoutObject(selection.start().anchorNode()); |
| 729 } | 437 } |
| 730 | 438 |
| 731 IntRect FrameSelection::absoluteCaretBounds() { | 439 IntRect FrameSelection::absoluteCaretBounds() { |
| 732 DCHECK(selection().isValidFor(*m_frame->document())); | 440 DCHECK(isAvailable()); |
| 733 return m_frameCaret->absoluteCaretBounds(); | 441 return m_frameCaret->absoluteCaretBounds(); |
| 734 } | 442 } |
| 735 | 443 |
| 736 void FrameSelection::invalidateCaretRect(bool forceInvalidation) { | 444 void FrameSelection::invalidateCaretRect(bool forceInvalidation) { |
| 737 m_frameCaret->invalidateCaretRect(forceInvalidation); | 445 m_frameCaret->invalidateCaretRect(forceInvalidation); |
| 738 } | 446 } |
| 739 | 447 |
| 740 void FrameSelection::dataWillChange(const CharacterData& node) { | |
| 741 m_frameCaret->dataWillChange(node); | |
| 742 } | |
| 743 | |
| 744 void FrameSelection::paintCaret(GraphicsContext& context, | 448 void FrameSelection::paintCaret(GraphicsContext& context, |
| 745 const LayoutPoint& paintOffset) { | 449 const LayoutPoint& paintOffset) { |
| 746 m_frameCaret->paintCaret(context, paintOffset); | 450 m_frameCaret->paintCaret(context, paintOffset); |
| 747 } | 451 } |
| 748 | 452 |
| 749 bool FrameSelection::contains(const LayoutPoint& point) { | 453 bool FrameSelection::contains(const LayoutPoint& point) { |
| 750 if (document().layoutViewItem().isNull()) | 454 if (!isAvailable()) |
| 751 return false; | 455 return false; |
| 752 | 456 |
| 753 // Treat a collapsed selection like no selection. | 457 // Treat a collapsed selection like no selection. |
| 754 const VisibleSelectionInFlatTree& visibleSelection = | 458 const VisibleSelectionInFlatTree& visibleSelection = |
| 755 this->visibleSelection<EditingInFlatTreeStrategy>(); | 459 computeVisibleSelectionInFlatTree(); |
| 756 if (!visibleSelection.isRange()) | 460 if (!visibleSelection.isRange()) |
| 757 return false; | 461 return false; |
| 758 | 462 |
| 759 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); | 463 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active); |
| 760 HitTestResult result(request, point); | 464 HitTestResult result(request, point); |
| 761 document().layoutViewItem().hitTest(result); | 465 document().layoutViewItem().hitTest(result); |
| 762 Node* innerNode = result.innerNode(); | 466 Node* innerNode = result.innerNode(); |
| 763 if (!innerNode || !innerNode->layoutObject()) | 467 if (!innerNode || !innerNode->layoutObject()) |
| 764 return false; | 468 return false; |
| 765 | 469 |
| (...skipping 10 matching lines...) Expand all Loading... |
| 776 return false; | 480 return false; |
| 777 | 481 |
| 778 const PositionInFlatTree& start = visibleStart.deepEquivalent(); | 482 const PositionInFlatTree& start = visibleStart.deepEquivalent(); |
| 779 const PositionInFlatTree& end = visibleEnd.deepEquivalent(); | 483 const PositionInFlatTree& end = visibleEnd.deepEquivalent(); |
| 780 const PositionInFlatTree& pos = visiblePos.deepEquivalent(); | 484 const PositionInFlatTree& pos = visiblePos.deepEquivalent(); |
| 781 return start.compareTo(pos) <= 0 && pos.compareTo(end) <= 0; | 485 return start.compareTo(pos) <= 0 && pos.compareTo(end) <= 0; |
| 782 } | 486 } |
| 783 | 487 |
| 784 // Workaround for the fact that it's hard to delete a frame. | 488 // Workaround for the fact that it's hard to delete a frame. |
| 785 // Call this after doing user-triggered selections to make it easy to delete the | 489 // Call this after doing user-triggered selections to make it easy to delete the |
| 786 // frame you entirely selected. Can't do this implicitly as part of every | 490 // frame you entirely selected. |
| 787 // setSelection call because in some contexts it might not be good for the focus | 491 // Can't do this implicitly as part of every setSelection call because in some |
| 788 // to move to another frame. So instead we call it from places where we are | 492 // contexts it might not be good |
| 789 // selecting with the mouse or the keyboard after setting the selection. | 493 // for the focus to move to another frame. So instead we call it from places |
| 494 // where we are selecting with the |
| 495 // mouse or the keyboard after setting the selection. |
| 790 void FrameSelection::selectFrameElementInParentIfFullySelected() { | 496 void FrameSelection::selectFrameElementInParentIfFullySelected() { |
| 791 // Find the parent frame; if there is none, then we have nothing to do. | 497 // Find the parent frame; if there is none, then we have nothing to do. |
| 792 Frame* parent = m_frame->tree().parent(); | 498 Frame* parent = m_frame->tree().parent(); |
| 793 if (!parent) | 499 if (!parent) |
| 794 return; | 500 return; |
| 795 Page* page = m_frame->page(); | 501 Page* page = m_frame->page(); |
| 796 if (!page) | 502 if (!page) |
| 797 return; | 503 return; |
| 798 | 504 |
| 799 // Check if the selection contains the entire frame contents; if not, then | 505 // Check if the selection contains the entire frame contents; if not, then |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 905 // |root| may be detached due to selectstart event. | 611 // |root| may be detached due to selectstart event. |
| 906 if (!root->isConnected() || expectedDocument != root->document()) | 612 if (!root->isConnected() || expectedDocument != root->document()) |
| 907 return; | 613 return; |
| 908 } | 614 } |
| 909 | 615 |
| 910 setSelection(SelectionInDOMTree::Builder().selectAllChildren(*root).build()); | 616 setSelection(SelectionInDOMTree::Builder().selectAllChildren(*root).build()); |
| 911 selectFrameElementInParentIfFullySelected(); | 617 selectFrameElementInParentIfFullySelected(); |
| 912 notifyLayoutObjectOfSelectionChange(UserTriggered); | 618 notifyLayoutObjectOfSelectionChange(UserTriggered); |
| 913 } | 619 } |
| 914 | 620 |
| 621 bool FrameSelection::setSelectedRange(Range* range, |
| 622 TextAffinity affinity, |
| 623 SelectionDirectionalMode directional, |
| 624 SetSelectionOptions options) { |
| 625 if (!range || !range->isConnected()) |
| 626 return false; |
| 627 DCHECK_EQ(range->startContainer()->document(), |
| 628 range->endContainer()->document()); |
| 629 return setSelectedRange(EphemeralRange(range), affinity, directional, |
| 630 options); |
| 631 } |
| 632 |
| 915 bool FrameSelection::setSelectedRange(const EphemeralRange& range, | 633 bool FrameSelection::setSelectedRange(const EphemeralRange& range, |
| 916 TextAffinity affinity, | 634 TextAffinity affinity, |
| 917 SelectionDirectionalMode directional, | 635 SelectionDirectionalMode directional, |
| 918 SetSelectionOptions options) { | 636 SetSelectionOptions options) { |
| 919 if (range.isNull()) | 637 if (range.isNull()) |
| 920 return false; | 638 return false; |
| 921 m_selectionEditor->resetLogicalRange(); | 639 m_selectionEditor->resetLogicalRange(); |
| 922 // Since |FrameSeleciton::setSelection()| dispatches events and DOM tree | 640 // Since |FrameSeleciton::setSelection()| dispatches events and DOM tree |
| 923 // can be modified by event handlers, we should create |Range| object before | 641 // can be modified by event handlers, we should create |Range| object before |
| 924 // calling it. | 642 // calling it. |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 994 // Secure keyboard entry is set by the active frame. | 712 // Secure keyboard entry is set by the active frame. |
| 995 if (m_useSecureKeyboardEntryWhenActive) | 713 if (m_useSecureKeyboardEntryWhenActive) |
| 996 setUseSecureKeyboardEntry(activeAndFocused); | 714 setUseSecureKeyboardEntry(activeAndFocused); |
| 997 } | 715 } |
| 998 | 716 |
| 999 void FrameSelection::pageActivationChanged() { | 717 void FrameSelection::pageActivationChanged() { |
| 1000 focusedOrActiveStateChanged(); | 718 focusedOrActiveStateChanged(); |
| 1001 } | 719 } |
| 1002 | 720 |
| 1003 void FrameSelection::updateSecureKeyboardEntryIfActive() { | 721 void FrameSelection::updateSecureKeyboardEntryIfActive() { |
| 1004 if (!isFocusedAndActive()) | 722 if (isFocusedAndActive()) |
| 1005 return; | 723 setUseSecureKeyboardEntry(m_useSecureKeyboardEntryWhenActive); |
| 1006 setUseSecureKeyboardEntry(m_useSecureKeyboardEntryWhenActive); | |
| 1007 } | 724 } |
| 1008 | 725 |
| 1009 void FrameSelection::setUseSecureKeyboardEntryWhenActive( | 726 void FrameSelection::setUseSecureKeyboardEntryWhenActive( |
| 1010 bool usesSecureKeyboard) { | 727 bool usesSecureKeyboard) { |
| 1011 if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard) | 728 if (m_useSecureKeyboardEntryWhenActive == usesSecureKeyboard) |
| 1012 return; | 729 return; |
| 1013 m_useSecureKeyboardEntryWhenActive = usesSecureKeyboard; | 730 m_useSecureKeyboardEntryWhenActive = usesSecureKeyboard; |
| 1014 updateSecureKeyboardEntryIfActive(); | 731 updateSecureKeyboardEntryIfActive(); |
| 1015 } | 732 } |
| 1016 | 733 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 1035 } | 752 } |
| 1036 | 753 |
| 1037 bool FrameSelection::isAppearanceDirty() const { | 754 bool FrameSelection::isAppearanceDirty() const { |
| 1038 return m_pendingSelection->hasPendingSelection(); | 755 return m_pendingSelection->hasPendingSelection(); |
| 1039 } | 756 } |
| 1040 | 757 |
| 1041 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { | 758 void FrameSelection::commitAppearanceIfNeeded(LayoutView& layoutView) { |
| 1042 return m_pendingSelection->commit(layoutView); | 759 return m_pendingSelection->commit(layoutView); |
| 1043 } | 760 } |
| 1044 | 761 |
| 762 void FrameSelection::didLayout() { |
| 763 DCHECK_GE(document().lifecycle().state(), |
| 764 DocumentLifecycle::AfterPerformLayout); |
| 765 updateAppearance(); |
| 766 } |
| 767 |
| 1045 void FrameSelection::updateAppearance() { | 768 void FrameSelection::updateAppearance() { |
| 1046 m_frameCaret->updateAppearance(); | 769 DCHECK(!m_frame->contentLayoutItem().isNull()); |
| 1047 | 770 m_frameCaret->setCaretRectNeedsUpdate(); |
| 1048 if (m_frame->contentLayoutItem().isNull()) | |
| 1049 return; | |
| 1050 m_pendingSelection->setHasPendingSelection(); | 771 m_pendingSelection->setHasPendingSelection(); |
| 1051 } | 772 } |
| 1052 | 773 |
| 1053 void FrameSelection::notifyLayoutObjectOfSelectionChange( | 774 void FrameSelection::notifyLayoutObjectOfSelectionChange( |
| 1054 EUserTriggered userTriggered) { | 775 EUserTriggered userTriggered) { |
| 1055 if (TextControlElement* textControl = enclosingTextControl(start())) | 776 if (TextControlElement* textControl = enclosingTextControl(start())) |
| 1056 textControl->selectionChanged(userTriggered == UserTriggered); | 777 textControl->selectionChanged(userTriggered == UserTriggered); |
| 1057 } | 778 } |
| 1058 | 779 |
| 1059 // Helper function that tells whether a particular node is an element that has | 780 // Helper function that tells whether a particular node is an element that has |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1086 } | 807 } |
| 1087 target = target->parentOrShadowHostElement(); | 808 target = target->parentOrShadowHostElement(); |
| 1088 } | 809 } |
| 1089 document().clearFocusedElement(); | 810 document().clearFocusedElement(); |
| 1090 } | 811 } |
| 1091 } | 812 } |
| 1092 | 813 |
| 1093 static String extractSelectedText(const FrameSelection& selection, | 814 static String extractSelectedText(const FrameSelection& selection, |
| 1094 TextIteratorBehavior behavior) { | 815 TextIteratorBehavior behavior) { |
| 1095 const VisibleSelectionInFlatTree& visibleSelection = | 816 const VisibleSelectionInFlatTree& visibleSelection = |
| 1096 selection.visibleSelection<EditingInFlatTreeStrategy>(); | 817 selection.computeVisibleSelectionInFlatTree(); |
| 1097 const EphemeralRangeInFlatTree& range = | 818 const EphemeralRangeInFlatTree& range = |
| 1098 visibleSelection.toNormalizedEphemeralRange(); | 819 visibleSelection.toNormalizedEphemeralRange(); |
| 1099 // We remove '\0' characters because they are not visibly rendered to the | 820 // We remove '\0' characters because they are not visibly rendered to the |
| 1100 // user. | 821 // user. |
| 1101 return plainText(range, behavior).replace(0, ""); | 822 return plainText(range, behavior).replace(0, ""); |
| 1102 } | 823 } |
| 1103 | 824 |
| 1104 String FrameSelection::selectedHTMLForClipboard() const { | 825 String FrameSelection::selectedHTMLForClipboard() const { |
| 1105 const VisibleSelectionInFlatTree& visibleSelection = | 826 const VisibleSelectionInFlatTree& visibleSelection = |
| 1106 this->visibleSelection<EditingInFlatTreeStrategy>(); | 827 computeVisibleSelectionInFlatTree(); |
| 1107 const EphemeralRangeInFlatTree& range = | 828 const EphemeralRangeInFlatTree& range = |
| 1108 visibleSelection.toNormalizedEphemeralRange(); | 829 visibleSelection.toNormalizedEphemeralRange(); |
| 1109 return createMarkup(range.startPosition(), range.endPosition(), | 830 return createMarkup(range.startPosition(), range.endPosition(), |
| 1110 AnnotateForInterchange, | 831 AnnotateForInterchange, |
| 1111 ConvertBlocksToInlines::NotConvert, ResolveNonLocalURLs); | 832 ConvertBlocksToInlines::NotConvert, ResolveNonLocalURLs); |
| 1112 } | 833 } |
| 1113 | 834 |
| 1114 String FrameSelection::selectedText(TextIteratorBehavior behavior) const { | 835 String FrameSelection::selectedText(TextIteratorBehavior behavior) const { |
| 1115 return extractSelectedText(*this, behavior); | 836 return extractSelectedText(*this, behavior); |
| 1116 } | 837 } |
| (...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1235 | 956 |
| 1236 Document* document = m_frame->document(); | 957 Document* document = m_frame->document(); |
| 1237 if (!isNone() || !(blink::hasEditableStyle(*document))) | 958 if (!isNone() || !(blink::hasEditableStyle(*document))) |
| 1238 return; | 959 return; |
| 1239 | 960 |
| 1240 Element* documentElement = document->documentElement(); | 961 Element* documentElement = document->documentElement(); |
| 1241 if (!documentElement) | 962 if (!documentElement) |
| 1242 return; | 963 return; |
| 1243 if (HTMLBodyElement* body = | 964 if (HTMLBodyElement* body = |
| 1244 Traversal<HTMLBodyElement>::firstChild(*documentElement)) { | 965 Traversal<HTMLBodyElement>::firstChild(*documentElement)) { |
| 1245 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | |
| 1246 // needs to be audited. See http://crbug.com/590369 for more details. | |
| 1247 document->updateStyleAndLayoutIgnorePendingStylesheets(); | |
| 1248 | |
| 1249 setSelection(SelectionInDOMTree::Builder() | 966 setSelection(SelectionInDOMTree::Builder() |
| 1250 .collapse(firstPositionInOrBeforeNode(body)) | 967 .collapse(firstPositionInOrBeforeNode(body)) |
| 1251 .build()); | 968 .build()); |
| 1252 } | 969 } |
| 1253 } | 970 } |
| 1254 | 971 |
| 1255 // TODO(yoichio): We should have LocalFrame having FrameCaret, | 972 // TODO(yoichio): We should have LocalFrame having FrameCaret, |
| 1256 // Editor and PendingSelection using FrameCaret directly | 973 // Editor and PendingSelection using FrameCaret directly |
| 1257 // and get rid of this. | 974 // and get rid of this. |
| 1258 bool FrameSelection::shouldShowBlockCursor() const { | 975 bool FrameSelection::shouldShowBlockCursor() const { |
| 1259 return m_frameCaret->shouldShowBlockCursor(); | 976 return m_frameCaret->shouldShowBlockCursor(); |
| 1260 } | 977 } |
| 1261 | 978 |
| 1262 // TODO(yoichio): We should have LocalFrame having FrameCaret, | 979 // TODO(yoichio): We should have LocalFrame having FrameCaret, |
| 1263 // Editor and PendingSelection using FrameCaret directly | 980 // Editor and PendingSelection using FrameCaret directly |
| 1264 // and get rid of this. | 981 // and get rid of this. |
| 1265 // TODO(yoichio): We should use "caret-shape" in "CSS Basic User Interface | 982 // TODO(yoichio): We should use "caret-shape" in "CSS Basic User Interface |
| 1266 // Module Level 4" https://drafts.csswg.org/css-ui-4/ | 983 // Module Level 4" https://drafts.csswg.org/css-ui-4/ |
| 1267 // To use "caret-shape", we need to expose inserting mode information to CSS; | 984 // To use "caret-shape", we need to expose inserting mode information to CSS; |
| 1268 // https://github.com/w3c/csswg-drafts/issues/133 | 985 // https://github.com/w3c/csswg-drafts/issues/133 |
| 1269 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor) { | 986 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor) { |
| 1270 m_frameCaret->setShouldShowBlockCursor(shouldShowBlockCursor); | 987 m_frameCaret->setShouldShowBlockCursor(shouldShowBlockCursor); |
| 1271 } | 988 } |
| 1272 | 989 |
| 990 template <typename Strategy> |
| 991 VisibleSelectionTemplate<Strategy> FrameSelection::validateSelection( |
| 992 const VisibleSelectionTemplate<Strategy>& selection) { |
| 993 if (selection.isNone()) |
| 994 return selection; |
| 995 |
| 996 const PositionTemplate<Strategy> base = selection.base(); |
| 997 const PositionTemplate<Strategy> extent = selection.extent(); |
| 998 bool isBaseValid = base.document() == document(); |
| 999 bool isExtentValid = extent.document() == document(); |
| 1000 |
| 1001 if (isBaseValid && isExtentValid) |
| 1002 return selection; |
| 1003 |
| 1004 VisibleSelectionTemplate<Strategy> newSelection; |
| 1005 if (isBaseValid) { |
| 1006 newSelection.setWithoutValidation(base, base); |
| 1007 } else if (isExtentValid) { |
| 1008 newSelection.setWithoutValidation(extent, extent); |
| 1009 } |
| 1010 return newSelection; |
| 1011 } |
| 1012 |
| 1273 #ifndef NDEBUG | 1013 #ifndef NDEBUG |
| 1274 | 1014 |
| 1275 void FrameSelection::showTreeForThis() const { | 1015 void FrameSelection::showTreeForThis() const { |
| 1276 selection().showTreeForThis(); | 1016 selection().showTreeForThis(); |
| 1277 } | 1017 } |
| 1278 | 1018 |
| 1279 #endif | 1019 #endif |
| 1280 | 1020 |
| 1281 DEFINE_TRACE(FrameSelection) { | 1021 DEFINE_TRACE(FrameSelection) { |
| 1282 visitor->trace(m_document); | |
| 1283 visitor->trace(m_frame); | 1022 visitor->trace(m_frame); |
| 1284 visitor->trace(m_pendingSelection); | 1023 visitor->trace(m_pendingSelection); |
| 1285 visitor->trace(m_selectionEditor); | 1024 visitor->trace(m_selectionEditor); |
| 1286 visitor->trace(m_typingStyle); | 1025 visitor->trace(m_typingStyle); |
| 1287 visitor->trace(m_frameCaret); | 1026 visitor->trace(m_frameCaret); |
| 1027 SynchronousMutationObserver::trace(visitor); |
| 1288 } | 1028 } |
| 1289 | 1029 |
| 1290 void FrameSelection::scheduleVisualUpdate() const { | 1030 void FrameSelection::scheduleVisualUpdate() const { |
| 1291 if (Page* page = m_frame->page()) | 1031 if (Page* page = m_frame->page()) |
| 1292 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot()); | 1032 page->animator().scheduleVisualUpdate(m_frame->localFrameRoot()); |
| 1293 } | 1033 } |
| 1294 | 1034 |
| 1295 bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position) { | 1035 bool FrameSelection::selectWordAroundPosition(const VisiblePosition& position) { |
| 1296 static const EWordSide wordSideList[2] = {RightWordIfOnBoundary, | 1036 static const EWordSide wordSideList[2] = {RightWordIfOnBoundary, |
| 1297 LeftWordIfOnBoundary}; | 1037 LeftWordIfOnBoundary}; |
| 1298 for (EWordSide wordSide : wordSideList) { | 1038 for (EWordSide wordSide : wordSideList) { |
| 1299 VisiblePosition start = startOfWord(position, wordSide); | 1039 VisiblePosition start = startOfWord(position, wordSide); |
| 1300 VisiblePosition end = endOfWord(position, wordSide); | 1040 VisiblePosition end = endOfWord(position, wordSide); |
| 1301 String text = | 1041 String text = |
| 1302 plainText(EphemeralRange(start.deepEquivalent(), end.deepEquivalent())); | 1042 plainText(EphemeralRange(start.deepEquivalent(), end.deepEquivalent())); |
| 1303 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { | 1043 if (!text.isEmpty() && !isSeparator(text.characterStartingAt(0))) { |
| 1304 setSelection(SelectionInDOMTree::Builder() | 1044 setSelection(SelectionInDOMTree::Builder() |
| 1305 .collapse(start.toPositionWithAffinity()) | 1045 .collapse(start.toPositionWithAffinity()) |
| 1306 .extend(end.deepEquivalent()) | 1046 .extend(end.deepEquivalent()) |
| 1307 .build(), | 1047 .build(), |
| 1308 CloseTyping | ClearTypingStyle, | 1048 WordGranularity); |
| 1309 CursorAlignOnScroll::IfNeeded, WordGranularity); | |
| 1310 return true; | 1049 return true; |
| 1311 } | 1050 } |
| 1312 } | 1051 } |
| 1313 | 1052 |
| 1314 return false; | 1053 return false; |
| 1315 } | 1054 } |
| 1316 | 1055 |
| 1317 GranularityStrategy* FrameSelection::granularityStrategy() { | 1056 GranularityStrategy* FrameSelection::granularityStrategy() { |
| 1318 // We do lazy initalization for m_granularityStrategy, because if we | 1057 // We do lazy initalization for m_granularityStrategy, because if we |
| 1319 // initialize it right in the constructor - the correct settings may not be | 1058 // initialize it right in the constructor - the correct settings may not be |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1356 SelectionInDOMTree::Builder() | 1095 SelectionInDOMTree::Builder() |
| 1357 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), | 1096 .setBaseAndExtentDeprecated(basePosition.deepEquivalent(), |
| 1358 extentPosition.deepEquivalent()) | 1097 extentPosition.deepEquivalent()) |
| 1359 .setAffinity(basePosition.affinity()) | 1098 .setAffinity(basePosition.affinity()) |
| 1360 .setGranularity(granularity) | 1099 .setGranularity(granularity) |
| 1361 .build()); | 1100 .build()); |
| 1362 | 1101 |
| 1363 if (newSelection.isNone()) | 1102 if (newSelection.isNone()) |
| 1364 return; | 1103 return; |
| 1365 | 1104 |
| 1366 setSelection(newSelection, CloseTyping | ClearTypingStyle, | 1105 setSelection(newSelection, granularity); |
| 1367 CursorAlignOnScroll::IfNeeded, granularity); | |
| 1368 } | 1106 } |
| 1369 | 1107 |
| 1370 void FrameSelection::updateIfNeeded() { | 1108 void FrameSelection::updateIfNeeded() { |
| 1371 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); | 1109 DCHECK(!m_frame->document()->needsLayoutTreeUpdate()); |
| 1372 m_selectionEditor->updateIfNeeded(); | 1110 m_selectionEditor->updateIfNeeded(); |
| 1373 } | 1111 } |
| 1374 | 1112 |
| 1375 void FrameSelection::setCaretVisible(bool caretIsVisible) { | 1113 void FrameSelection::setCaretVisible(bool caretIsVisible) { |
| 1376 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible | 1114 m_frameCaret->setCaretVisibility(caretIsVisible ? CaretVisibility::Visible |
| 1377 : CaretVisibility::Hidden); | 1115 : CaretVisibility::Hidden); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1410 } | 1148 } |
| 1411 | 1149 |
| 1412 void showTree(const blink::FrameSelection* sel) { | 1150 void showTree(const blink::FrameSelection* sel) { |
| 1413 if (sel) | 1151 if (sel) |
| 1414 sel->showTreeForThis(); | 1152 sel->showTreeForThis(); |
| 1415 else | 1153 else |
| 1416 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; | 1154 LOG(INFO) << "Cannot showTree for <null> FrameSelection."; |
| 1417 } | 1155 } |
| 1418 | 1156 |
| 1419 #endif | 1157 #endif |
| OLD | NEW |