Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(80)

Side by Side Diff: third_party/WebKit/Source/core/editing/SelectionController.cpp

Issue 2201853002: Blink handle selection handle visibility (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: layout test doesnt make sense because tap causes selections handles to be shown Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698