| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights | 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights |
| 3 * reserved. | 3 * reserved. |
| 4 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) | 4 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
| 5 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) | 5 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) |
| 6 * Copyright (C) 2015 Google Inc. All rights reserved. | 6 * Copyright (C) 2015 Google Inc. All rights reserved. |
| 7 * | 7 * |
| 8 * Redistribution and use in source and binary forms, with or without | 8 * Redistribution and use in source and binary forms, with or without |
| 9 * modification, are permitted provided that the following conditions | 9 * modification, are permitted provided that the following conditions |
| 10 * are met: | 10 * are met: |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 132 Node* innerNode = event.innerNode(); | 132 Node* innerNode = event.innerNode(); |
| 133 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect)) | 133 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect)) |
| 134 return false; | 134 return false; |
| 135 | 135 |
| 136 // Extend the selection if the Shift key is down, unless the click is in a | 136 // Extend the selection if the Shift key is down, unless the click is in a |
| 137 // link or image. | 137 // link or image. |
| 138 bool extendSelection = isExtendingSelection(event); | 138 bool extendSelection = isExtendingSelection(event); |
| 139 | 139 |
| 140 // Don't restart the selection when the mouse is pressed on an | 140 // Don't restart the selection when the mouse is pressed on an |
| 141 // existing selection so we can allow for text dragging. | 141 // existing selection so we can allow for text dragging. |
| 142 if (FrameView* view = m_frame->view()) { | 142 if (!extendSelection && selection().contains(event.hitTestResult())) { |
| 143 LayoutPoint vPoint = view->rootFrameToContents(event.event().position()); | 143 m_mouseDownWasSingleClickInSelection = true; |
| 144 if (!extendSelection && selection().contains(vPoint)) { | 144 if (!event.event().fromTouch()) |
| 145 m_mouseDownWasSingleClickInSelection = true; | |
| 146 return false; | 145 return false; |
| 147 } | |
| 148 } | 146 } |
| 149 | 147 |
| 150 VisiblePositionInFlatTree visiblePos = | 148 VisiblePositionInFlatTree visiblePos = |
| 151 visiblePositionOfHitTestResult(event.hitTestResult()); | 149 visiblePositionOfHitTestResult(event.hitTestResult()); |
| 152 if (visiblePos.isNull()) | 150 if (visiblePos.isNull()) |
| 153 visiblePos = createVisiblePosition( | 151 visiblePos = createVisiblePosition( |
| 154 PositionInFlatTree::firstPositionInOrBeforeNode(innerNode)); | 152 PositionInFlatTree::firstPositionInOrBeforeNode(innerNode)); |
| 155 PositionInFlatTree pos = visiblePos.deepEquivalent(); | 153 PositionInFlatTree pos = visiblePos.deepEquivalent(); |
| 156 | 154 |
| 157 VisibleSelectionInFlatTree newSelection = | 155 VisibleSelectionInFlatTree newSelection = |
| 158 selection().visibleSelection<EditingInFlatTreeStrategy>(); | 156 selection().visibleSelection<EditingInFlatTreeStrategy>(); |
| 159 TextGranularity granularity = CharacterGranularity; | 157 TextGranularity granularity = CharacterGranularity; |
| 160 | 158 bool isHandleVisible = false; |
| 161 if (extendSelection && !newSelection.isNone()) { | 159 if (mouseDownWasSingleClickInSelection() && !selection().isHandleVisible()) { |
| 160 isHandleVisible = true; |
| 161 } else if (extendSelection && !newSelection.isNone()) { |
| 162 const VisibleSelectionInFlatTree selectionInUserSelectAll( | 162 const VisibleSelectionInFlatTree selectionInUserSelectAll( |
| 163 expandSelectionToRespectUserSelectAll(innerNode, | 163 expandSelectionToRespectUserSelectAll(innerNode, |
| 164 createVisibleSelection(pos))); | 164 createVisibleSelection(pos))); |
| 165 if (selectionInUserSelectAll.isRange()) { | 165 if (selectionInUserSelectAll.isRange()) { |
| 166 if (selectionInUserSelectAll.start().compareTo(newSelection.start()) < 0) | 166 if (selectionInUserSelectAll.start().compareTo(newSelection.start()) < 0) |
| 167 pos = selectionInUserSelectAll.start(); | 167 pos = selectionInUserSelectAll.start(); |
| 168 else if (newSelection.end().compareTo(selectionInUserSelectAll.end()) < 0) | 168 else if (newSelection.end().compareTo(selectionInUserSelectAll.end()) < 0) |
| 169 pos = selectionInUserSelectAll.end(); | 169 pos = selectionInUserSelectAll.end(); |
| 170 } | 170 } |
| 171 | 171 |
| (...skipping 14 matching lines...) Expand all Loading... |
| 186 newSelection.setExtent(pos); | 186 newSelection.setExtent(pos); |
| 187 } | 187 } |
| 188 | 188 |
| 189 if (selection().granularity() != CharacterGranularity) { | 189 if (selection().granularity() != CharacterGranularity) { |
| 190 granularity = selection().granularity(); | 190 granularity = selection().granularity(); |
| 191 newSelection.expandUsingGranularity(selection().granularity()); | 191 newSelection.expandUsingGranularity(selection().granularity()); |
| 192 } | 192 } |
| 193 } else if (m_selectionState != SelectionState::ExtendedSelection) { | 193 } else if (m_selectionState != SelectionState::ExtendedSelection) { |
| 194 newSelection = expandSelectionToRespectUserSelectAll( | 194 newSelection = expandSelectionToRespectUserSelectAll( |
| 195 innerNode, createVisibleSelection(visiblePos)); | 195 innerNode, createVisibleSelection(visiblePos)); |
| 196 if (newSelection.isContentEditable()) { |
| 197 bool isTextBoxEmpty = |
| 198 VisibleSelection::selectionFromContentsOfNode(innerNode).isCaret(); |
| 199 bool notLeftClick = event.event().pointerProperties().button != |
| 200 WebPointerProperties::Button::Left; |
| 201 if (!isTextBoxEmpty || notLeftClick) |
| 202 isHandleVisible = event.event().fromTouch(); |
| 203 } |
| 196 } | 204 } |
| 197 | 205 |
| 198 // Updating the selection is considered side-effect of the event and so it | 206 // Updating the selection is considered side-effect of the event and so it |
| 199 // doesn't impact the handled state. | 207 // doesn't impact the handled state. |
| 200 updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, | 208 updateSelectionForMouseDownDispatchingSelectStart( |
| 201 granularity); | 209 innerNode, newSelection, granularity, isHandleVisible); |
| 202 return false; | 210 return false; |
| 203 } | 211 } |
| 204 | 212 |
| 205 void SelectionController::updateSelectionForMouseDrag( | 213 void SelectionController::updateSelectionForMouseDrag( |
| 206 const HitTestResult& hitTestResult, | 214 const HitTestResult& hitTestResult, |
| 207 Node* mousePressNode, | 215 Node* mousePressNode, |
| 208 const LayoutPoint& dragStartPos, | 216 const LayoutPoint& dragStartPos, |
| 209 const IntPoint& lastKnownMousePosition) { | 217 const IntPoint& lastKnownMousePosition) { |
| 210 if (!m_mouseDownMayStartSelect) | 218 if (!m_mouseDownMayStartSelect) |
| 211 return; | 219 return; |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 319 newSelection.expandUsingGranularity(selection().granularity()); | 327 newSelection.expandUsingGranularity(selection().granularity()); |
| 320 | 328 |
| 321 selection().setNonDirectionalSelectionIfNeeded( | 329 selection().setNonDirectionalSelectionIfNeeded( |
| 322 newSelection, selection().granularity(), | 330 newSelection, selection().granularity(), |
| 323 FrameSelection::AdjustEndpointsAtBidiBoundary); | 331 FrameSelection::AdjustEndpointsAtBidiBoundary); |
| 324 } | 332 } |
| 325 | 333 |
| 326 bool SelectionController::updateSelectionForMouseDownDispatchingSelectStart( | 334 bool SelectionController::updateSelectionForMouseDownDispatchingSelectStart( |
| 327 Node* targetNode, | 335 Node* targetNode, |
| 328 const VisibleSelectionInFlatTree& selection, | 336 const VisibleSelectionInFlatTree& selection, |
| 329 TextGranularity granularity) { | 337 TextGranularity granularity, |
| 338 bool isHandleVisible) { |
| 330 if (targetNode && targetNode->layoutObject() && | 339 if (targetNode && targetNode->layoutObject() && |
| 331 !targetNode->layoutObject()->isSelectable()) | 340 !targetNode->layoutObject()->isSelectable()) |
| 332 return false; | 341 return false; |
| 333 | 342 |
| 334 if (dispatchSelectStart(targetNode) != DispatchEventResult::NotCanceled) | 343 if (dispatchSelectStart(targetNode) != DispatchEventResult::NotCanceled) |
| 335 return false; | 344 return false; |
| 336 | 345 |
| 337 // |dispatchSelectStart()| can change document hosted by |m_frame|. | 346 // |dispatchSelectStart()| can change document hosted by |m_frame|. |
| 338 if (!this->selection().isAvailable()) | 347 if (!this->selection().isAvailable()) |
| 339 return false; | 348 return false; |
| 340 | 349 |
| 341 if (!selection.isValidFor(this->selection().document())) | 350 if (!selection.isValidFor(this->selection().document())) |
| 342 return false; | 351 return false; |
| 343 | 352 |
| 344 if (selection.isRange()) { | 353 if (selection.isRange()) { |
| 345 m_selectionState = SelectionState::ExtendedSelection; | 354 m_selectionState = SelectionState::ExtendedSelection; |
| 346 } else { | 355 } else { |
| 347 granularity = CharacterGranularity; | 356 granularity = CharacterGranularity; |
| 348 m_selectionState = SelectionState::PlacedCaret; | 357 m_selectionState = SelectionState::PlacedCaret; |
| 349 } | 358 } |
| 350 | 359 |
| 351 this->selection().setNonDirectionalSelectionIfNeeded(selection, granularity); | 360 this->selection().setNonDirectionalSelectionIfNeeded( |
| 361 selection, granularity, FrameSelection::DoNotAdjustEndpoints, |
| 362 isHandleVisible); |
| 352 | 363 |
| 353 return true; | 364 return true; |
| 354 } | 365 } |
| 355 | 366 |
| 356 void SelectionController::selectClosestWordFromHitTestResult( | 367 void SelectionController::selectClosestWordFromHitTestResult( |
| 357 const HitTestResult& result, | 368 const HitTestResult& result, |
| 358 AppendTrailingWhitespace appendTrailingWhitespace, | 369 AppendTrailingWhitespace appendTrailingWhitespace, |
| 359 SelectInputEventType selectInputEventType) { | 370 SelectInputEventType selectInputEventType) { |
| 360 Node* innerNode = result.innerNode(); | 371 Node* innerNode = result.innerNode(); |
| 361 VisibleSelectionInFlatTree newSelection; | 372 VisibleSelectionInFlatTree newSelection; |
| (...skipping 10 matching lines...) Expand all Loading... |
| 372 adjustedHitTestResult.setNodeAndPosition(result.innerNode(), | 383 adjustedHitTestResult.setNodeAndPosition(result.innerNode(), |
| 373 LayoutPoint(0, 0)); | 384 LayoutPoint(0, 0)); |
| 374 | 385 |
| 375 const VisiblePositionInFlatTree& pos = | 386 const VisiblePositionInFlatTree& pos = |
| 376 visiblePositionOfHitTestResult(adjustedHitTestResult); | 387 visiblePositionOfHitTestResult(adjustedHitTestResult); |
| 377 if (pos.isNotNull()) { | 388 if (pos.isNotNull()) { |
| 378 newSelection = createVisibleSelection(pos); | 389 newSelection = createVisibleSelection(pos); |
| 379 newSelection.expandUsingGranularity(WordGranularity); | 390 newSelection.expandUsingGranularity(WordGranularity); |
| 380 } | 391 } |
| 381 | 392 |
| 393 bool isHandleVisible = false; |
| 382 if (selectInputEventType == SelectInputEventType::Touch) { | 394 if (selectInputEventType == SelectInputEventType::Touch) { |
| 383 // If node doesn't have text except space, tab or line break, do not | 395 // If node doesn't have text except space, tab or line break, do not |
| 384 // select that 'empty' area. | 396 // select that 'empty' area. |
| 385 EphemeralRangeInFlatTree range(newSelection.start(), newSelection.end()); | 397 EphemeralRangeInFlatTree range(newSelection.start(), newSelection.end()); |
| 386 const String& str = | 398 const String& str = |
| 387 plainText(range, hasEditableStyle(*innerNode) | 399 plainText(range, hasEditableStyle(*innerNode) |
| 388 ? TextIteratorEmitsObjectReplacementCharacter | 400 ? TextIteratorEmitsObjectReplacementCharacter |
| 389 : TextIteratorDefaultBehavior); | 401 : TextIteratorDefaultBehavior); |
| 390 if (str.isEmpty() || str.simplifyWhiteSpace().containsOnlyWhitespace()) | 402 if (str.isEmpty() || str.simplifyWhiteSpace().containsOnlyWhitespace()) |
| 391 return; | 403 return; |
| 392 | 404 |
| 393 if (newSelection.rootEditableElement() && | 405 if (newSelection.rootEditableElement() && |
| 394 pos.deepEquivalent() == | 406 pos.deepEquivalent() == |
| 395 VisiblePositionInFlatTree::lastPositionInNode( | 407 VisiblePositionInFlatTree::lastPositionInNode( |
| 396 newSelection.rootEditableElement()) | 408 newSelection.rootEditableElement()) |
| 397 .deepEquivalent()) | 409 .deepEquivalent()) |
| 398 return; | 410 return; |
| 411 isHandleVisible = true; |
| 399 } | 412 } |
| 400 | 413 |
| 401 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && | 414 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && |
| 402 newSelection.isRange()) | 415 newSelection.isRange()) |
| 403 newSelection.appendTrailingWhitespace(); | 416 newSelection.appendTrailingWhitespace(); |
| 404 | 417 |
| 405 updateSelectionForMouseDownDispatchingSelectStart( | 418 updateSelectionForMouseDownDispatchingSelectStart( |
| 406 innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), | 419 innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), |
| 407 WordGranularity); | 420 WordGranularity, isHandleVisible); |
| 408 } | 421 } |
| 409 | 422 |
| 410 void SelectionController::selectClosestMisspellingFromHitTestResult( | 423 void SelectionController::selectClosestMisspellingFromHitTestResult( |
| 411 const HitTestResult& result, | 424 const HitTestResult& result, |
| 412 AppendTrailingWhitespace appendTrailingWhitespace) { | 425 AppendTrailingWhitespace appendTrailingWhitespace) { |
| 413 Node* innerNode = result.innerNode(); | 426 Node* innerNode = result.innerNode(); |
| 414 VisibleSelectionInFlatTree newSelection; | 427 VisibleSelectionInFlatTree newSelection; |
| 415 | 428 |
| 416 if (!innerNode || !innerNode->layoutObject()) | 429 if (!innerNode || !innerNode->layoutObject()) |
| 417 return; | 430 return; |
| (...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 547 return false; | 560 return false; |
| 548 | 561 |
| 549 VisibleSelectionInFlatTree newSelection; | 562 VisibleSelectionInFlatTree newSelection; |
| 550 const VisiblePositionInFlatTree& pos = | 563 const VisiblePositionInFlatTree& pos = |
| 551 visiblePositionOfHitTestResult(event.hitTestResult()); | 564 visiblePositionOfHitTestResult(event.hitTestResult()); |
| 552 if (pos.isNotNull()) { | 565 if (pos.isNotNull()) { |
| 553 newSelection = createVisibleSelection(pos); | 566 newSelection = createVisibleSelection(pos); |
| 554 newSelection.expandUsingGranularity(ParagraphGranularity); | 567 newSelection.expandUsingGranularity(ParagraphGranularity); |
| 555 } | 568 } |
| 556 | 569 |
| 570 bool isHandleVisible = event.event().fromTouch() && newSelection.isRange(); |
| 571 |
| 557 return updateSelectionForMouseDownDispatchingSelectStart( | 572 return updateSelectionForMouseDownDispatchingSelectStart( |
| 558 innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), | 573 innerNode, expandSelectionToRespectUserSelectAll(innerNode, newSelection), |
| 559 ParagraphGranularity); | 574 ParagraphGranularity, isHandleVisible); |
| 560 } | 575 } |
| 561 | 576 |
| 562 void SelectionController::handleMousePressEvent( | 577 void SelectionController::handleMousePressEvent( |
| 563 const MouseEventWithHitTestResults& event) { | 578 const MouseEventWithHitTestResults& event) { |
| 564 // If we got the event back, that must mean it wasn't prevented, | 579 // If we got the event back, that must mean it wasn't prevented, |
| 565 // so it's allowed to start a drag or selection if it wasn't in a scrollbar. | 580 // so it's allowed to start a drag or selection if it wasn't in a scrollbar. |
| 566 m_mouseDownMayStartSelect = | 581 m_mouseDownMayStartSelect = |
| 567 (canMouseDownStartSelect(event.innerNode()) || isLinkSelection(event)) && | 582 (canMouseDownStartSelect(event.innerNode()) || isLinkSelection(event)) && |
| 568 !event.scrollbar(); | 583 !event.scrollbar(); |
| 569 m_mouseDownWasSingleClickInSelection = false; | 584 m_mouseDownWasSingleClickInSelection = false; |
| (...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 743 pos.deepEquivalent().parentAnchoredEquivalent()), | 758 pos.deepEquivalent().parentAnchoredEquivalent()), |
| 744 DocumentMarker::MisspellingMarkers()) | 759 DocumentMarker::MisspellingMarkers()) |
| 745 .size() > 0; | 760 .size() > 0; |
| 746 } | 761 } |
| 747 | 762 |
| 748 void SelectionController::sendContextMenuEvent( | 763 void SelectionController::sendContextMenuEvent( |
| 749 const MouseEventWithHitTestResults& mev, | 764 const MouseEventWithHitTestResults& mev, |
| 750 const LayoutPoint& position) { | 765 const LayoutPoint& position) { |
| 751 if (!selection().isAvailable()) | 766 if (!selection().isAvailable()) |
| 752 return; | 767 return; |
| 753 if (selection().contains(position) || mev.scrollbar() || | 768 if (selection().contains(mev.hitTestResult()) || mev.scrollbar() || |
| 754 // FIXME: In the editable case, word selection sometimes selects content | 769 // FIXME: In the editable case, word selection sometimes selects content |
| 755 // that isn't underneath the mouse. | 770 // that isn't underneath the mouse. |
| 756 // If the selection is non-editable, we do word selection to make it | 771 // If the selection is non-editable, we do word selection to make it |
| 757 // easier to use the contextual menu items available for text selections. | 772 // easier to use the contextual menu items available for text selections. |
| 758 // But only if we're above text. | 773 // But only if we're above text. |
| 759 !(selection().isContentEditable() || | 774 !(selection().isContentEditable() || |
| 760 (mev.innerNode() && mev.innerNode()->isTextNode()))) | 775 (mev.innerNode() && mev.innerNode()->isTextNode()))) |
| 761 return; | 776 return; |
| 762 | 777 |
| 763 // Context menu events are always allowed to perform a selection. | 778 // Context menu events are always allowed to perform a selection. |
| 764 AutoReset<bool> mouseDownMayStartSelectChange(&m_mouseDownMayStartSelect, | 779 AutoReset<bool> mouseDownMayStartSelectChange(&m_mouseDownMayStartSelect, |
| 765 true); | 780 true); |
| 766 | 781 |
| 767 if (hitTestResultIsMisspelled(mev.hitTestResult())) | 782 if (hitTestResultIsMisspelled(mev.hitTestResult())) |
| 768 return selectClosestMisspellingFromMouseEvent(mev); | 783 return selectClosestMisspellingFromMouseEvent(mev); |
| 769 | 784 |
| 770 if (!m_frame->editor().behavior().shouldSelectOnContextualMenuClick()) | 785 if (!m_frame->editor().behavior().shouldSelectOnContextualMenuClick()) |
| 771 return; | 786 return; |
| 772 | 787 |
| 773 selectClosestWordOrLinkFromMouseEvent(mev); | 788 selectClosestWordOrLinkFromMouseEvent(mev); |
| 774 } | 789 } |
| 775 | 790 |
| 776 void SelectionController::passMousePressEventToSubframe( | 791 void SelectionController::passMousePressEventToSubframe( |
| 777 const MouseEventWithHitTestResults& mev) { | 792 const MouseEventWithHitTestResults& mev) { |
| 778 // If we're clicking into a frame that is selected, the frame will appear | 793 // If we're clicking into a frame that is selected, the frame will appear |
| 779 // greyed out even though we're clicking on the selection. This looks | 794 // greyed out even though we're clicking on the selection. This looks |
| 780 // really strange (having the whole frame be greyed out), so we deselect the | 795 // really strange (having the whole frame be greyed out), so we deselect the |
| 781 // selection. | 796 // selection. |
| 782 IntPoint p = m_frame->view()->rootFrameToContents(mev.event().position()); | 797 if (!selection().contains(mev.hitTestResult())) |
| 783 if (!selection().contains(p)) | |
| 784 return; | 798 return; |
| 785 | 799 |
| 786 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets | 800 // TODO(xiaochengh): The use of updateStyleAndLayoutIgnorePendingStylesheets |
| 787 // needs to be audited. See http://crbug.com/590369 for more details. | 801 // needs to be audited. See http://crbug.com/590369 for more details. |
| 788 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); | 802 m_frame->document()->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 789 | 803 |
| 790 const VisiblePositionInFlatTree& visiblePos = | 804 const VisiblePositionInFlatTree& visiblePos = |
| 791 visiblePositionOfHitTestResult(mev.hitTestResult()); | 805 visiblePositionOfHitTestResult(mev.hitTestResult()); |
| 792 VisibleSelectionInFlatTree newSelection = createVisibleSelection(visiblePos); | 806 VisibleSelectionInFlatTree newSelection = createVisibleSelection(visiblePos); |
| 793 selection().setSelection(newSelection); | 807 selection().setSelection(newSelection); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 826 return event.event().altKey() && event.isOverLink(); | 840 return event.event().altKey() && event.isOverLink(); |
| 827 } | 841 } |
| 828 | 842 |
| 829 bool isExtendingSelection(const MouseEventWithHitTestResults& event) { | 843 bool isExtendingSelection(const MouseEventWithHitTestResults& event) { |
| 830 bool isMouseDownOnLinkOrImage = | 844 bool isMouseDownOnLinkOrImage = |
| 831 event.isOverLink() || event.hitTestResult().image(); | 845 event.isOverLink() || event.hitTestResult().image(); |
| 832 return event.event().shiftKey() && !isMouseDownOnLinkOrImage; | 846 return event.event().shiftKey() && !isMouseDownOnLinkOrImage; |
| 833 } | 847 } |
| 834 | 848 |
| 835 } // namespace blink | 849 } // namespace blink |
| OLD | NEW |