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