OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. | 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv
ed. |
3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) | 3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org) |
4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) | 4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies) |
5 * Copyright (C) 2015 Google Inc. All rights reserved. | 5 * Copyright (C) 2015 Google Inc. All rights reserved. |
6 * | 6 * |
7 * Redistribution and use in source and binary forms, with or without | 7 * Redistribution and use in source and binary forms, with or without |
8 * modification, are permitted provided that the following conditions | 8 * modification, are permitted provided that the following conditions |
9 * are met: | 9 * are met: |
10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
59 { | 59 { |
60 } | 60 } |
61 | 61 |
62 DEFINE_TRACE(SelectionController) | 62 DEFINE_TRACE(SelectionController) |
63 { | 63 { |
64 visitor->trace(m_frame); | 64 visitor->trace(m_frame); |
65 } | 65 } |
66 | 66 |
67 namespace { | 67 namespace { |
68 | 68 |
69 void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& new
Selection) | 69 template <typename Strategy> |
| 70 void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelectionTempl
ate<Strategy>& newSelection) |
70 { | 71 { |
71 if (equalSelectionsInDOMTree(selection.selection(), newSelection)) | 72 if (selection.visibleSelection<Strategy>() == newSelection) |
72 return; | 73 return; |
73 selection.setSelection(newSelection); | 74 selection.setSelection(newSelection); |
74 } | 75 } |
75 | 76 |
76 bool dispatchSelectStart(Node* node) | 77 bool dispatchSelectStart(Node* node) |
77 { | 78 { |
78 if (!node || !node->layoutObject()) | 79 if (!node || !node->layoutObject()) |
79 return true; | 80 return true; |
80 | 81 |
81 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::sel
ectstart)); | 82 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::sel
ectstart)); |
82 } | 83 } |
83 | 84 |
84 template <typename Strategy> | 85 template <typename Strategy> |
85 VisibleSelectionTemplate<Strategy> expandSelectionToRespectUserSelectAll(Node* t
argetNode, const VisibleSelectionTemplate<Strategy>& selection) | 86 VisibleSelectionTemplate<Strategy> expandSelectionToRespectUserSelectAll(Node* t
argetNode, const VisibleSelectionTemplate<Strategy>& selection) |
86 { | 87 { |
87 Node* rootUserSelectAll = Strategy::rootUserSelectAllForNode(targetNode); | 88 Node* rootUserSelectAll = Strategy::rootUserSelectAllForNode(targetNode); |
88 if (!rootUserSelectAll) | 89 if (!rootUserSelectAll) |
89 return selection; | 90 return selection; |
90 | 91 |
91 VisibleSelectionTemplate<Strategy> newSelection(selection); | 92 VisibleSelectionTemplate<Strategy> newSelection(selection); |
92 newSelection.setBase(mostBackwardCaretPosition(PositionAlgorithm<Strategy>::
beforeNode(rootUserSelectAll), CanCrossEditingBoundary)); | 93 newSelection.setBase(mostBackwardCaretPosition(PositionAlgorithm<Strategy>::
beforeNode(rootUserSelectAll), CanCrossEditingBoundary)); |
93 newSelection.setExtent(mostForwardCaretPosition(PositionAlgorithm<Strategy>:
:afterNode(rootUserSelectAll), CanCrossEditingBoundary)); | 94 newSelection.setExtent(mostForwardCaretPosition(PositionAlgorithm<Strategy>:
:afterNode(rootUserSelectAll), CanCrossEditingBoundary)); |
94 | 95 |
95 return newSelection; | 96 return newSelection; |
96 } | 97 } |
97 | 98 |
98 // TODO(yosin) |expandSelectionToRespectUserSelectAll()| with |VisibleSelection| | |
99 // should be removed once we have full version of |VisibleSelectionTemplate|. | |
100 static VisibleSelectionTemplate<EditingStrategy> expandSelectionToRespectUserSel
ectAll(Node* targetNode, const VisibleSelection& selection) | |
101 { | |
102 return expandSelectionToRespectUserSelectAll(targetNode, VisibleSelectionTem
plate<EditingStrategy>(selection)); | |
103 } | |
104 | |
105 bool expandSelectionUsingGranularity(VisibleSelection& selection, TextGranularit
y granularity) | |
106 { | |
107 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) | |
108 return selection.expandUsingGranularityInComposedTree(granularity); | |
109 return selection.expandUsingGranularity(granularity); | |
110 } | |
111 | |
112 template <typename Strategy> | 99 template <typename Strategy> |
113 static int textDistance(const PositionAlgorithm<Strategy>& start, const Position
Algorithm<Strategy>& end) | 100 static int textDistance(const PositionAlgorithm<Strategy>& start, const Position
Algorithm<Strategy>& end) |
114 { | 101 { |
115 return TextIteratorAlgorithm<Strategy>::rangeLength(start, end, true); | 102 return TextIteratorAlgorithm<Strategy>::rangeLength(start, end, true); |
116 } | 103 } |
117 | 104 |
118 bool canMouseDownStartSelect(Node* node) | 105 bool canMouseDownStartSelect(Node* node) |
119 { | 106 { |
120 if (!node || !node->layoutObject()) | 107 if (!node || !node->layoutObject()) |
121 return true; | 108 return true; |
(...skipping 23 matching lines...) Expand all Loading... |
145 // existing selection so we can allow for text dragging. | 132 // existing selection so we can allow for text dragging. |
146 if (FrameView* view = m_frame->view()) { | 133 if (FrameView* view = m_frame->view()) { |
147 LayoutPoint vPoint = view->rootFrameToContents(event.event().position())
; | 134 LayoutPoint vPoint = view->rootFrameToContents(event.event().position())
; |
148 if (!extendSelection && selection().contains(vPoint)) { | 135 if (!extendSelection && selection().contains(vPoint)) { |
149 m_mouseDownWasSingleClickInSelection = true; | 136 m_mouseDownWasSingleClickInSelection = true; |
150 return false; | 137 return false; |
151 } | 138 } |
152 } | 139 } |
153 | 140 |
154 const PositionWithAffinity eventPos = innerNode->layoutObject()->positionFor
Point(event.localPoint()); | 141 const PositionWithAffinity eventPos = innerNode->layoutObject()->positionFor
Point(event.localPoint()); |
155 VisiblePositionTemplate<Strategy> visiblePos = createVisiblePosition(fromPos
itionInDOMTree<Strategy>(eventPos.position()), eventPos.affinity()); | 142 VisiblePositionTemplate<Strategy> visiblePos = createVisiblePosition(fromPos
itionInDOMTree<Strategy>(eventPos)); |
156 if (visiblePos.isNull()) | 143 if (visiblePos.isNull()) |
157 visiblePos = createVisiblePosition(PositionAlgorithm<Strategy>::firstPos
itionInOrBeforeNode(innerNode)); | 144 visiblePos = createVisiblePosition(PositionAlgorithm<Strategy>::firstPos
itionInOrBeforeNode(innerNode)); |
158 PositionAlgorithm<Strategy> pos = visiblePos.deepEquivalent(); | 145 PositionAlgorithm<Strategy> pos = visiblePos.deepEquivalent(); |
159 | 146 |
160 VisibleSelectionTemplate<Strategy> newSelection(selection().selection()); | 147 VisibleSelectionTemplate<Strategy> newSelection = selection().visibleSelecti
on<Strategy>(); |
161 TextGranularity granularity = CharacterGranularity; | 148 TextGranularity granularity = CharacterGranularity; |
162 | 149 |
163 if (extendSelection && newSelection.isCaretOrRange()) { | 150 if (extendSelection && newSelection.isCaretOrRange()) { |
164 const VisibleSelectionTemplate<Strategy> selectionInUserSelectAll(expand
SelectionToRespectUserSelectAll(innerNode, VisibleSelectionTemplate<Strategy>(cr
eateVisiblePosition(pos)))); | 151 const VisibleSelectionTemplate<Strategy> selectionInUserSelectAll(expand
SelectionToRespectUserSelectAll(innerNode, VisibleSelectionTemplate<Strategy>(cr
eateVisiblePosition(pos)))); |
165 if (selectionInUserSelectAll.isRange()) { | 152 if (selectionInUserSelectAll.isRange()) { |
166 if (selectionInUserSelectAll.start().compareTo(newSelection.start())
< 0) | 153 if (selectionInUserSelectAll.start().compareTo(newSelection.start())
< 0) |
167 pos = selectionInUserSelectAll.start(); | 154 pos = selectionInUserSelectAll.start(); |
168 else if (newSelection.end().compareTo(selectionInUserSelectAll.end()
) < 0) | 155 else if (newSelection.end().compareTo(selectionInUserSelectAll.end()
) < 0) |
169 pos = selectionInUserSelectAll.end(); | 156 pos = selectionInUserSelectAll.end(); |
170 } | 157 } |
171 | 158 |
172 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional()
) { | 159 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional()
) { |
173 if (pos.isNotNull()) { | 160 if (pos.isNotNull()) { |
174 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click d
eselects when selection | 161 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click |
175 // was created right-to-left | 162 // deselects when selection was created right-to-left |
176 const PositionAlgorithm<Strategy> start = newSelection.start(); | 163 const PositionAlgorithm<Strategy> start = newSelection.start(); |
177 const PositionAlgorithm<Strategy> end = newSelection.end(); | 164 const PositionAlgorithm<Strategy> end = newSelection.end(); |
178 int distanceToStart = textDistance(start, pos); | 165 int distanceToStart = textDistance(start, pos); |
179 int distanceToEnd = textDistance(pos, end); | 166 int distanceToEnd = textDistance(pos, end); |
180 if (distanceToStart <= distanceToEnd) | 167 if (distanceToStart <= distanceToEnd) |
181 newSelection = VisibleSelectionTemplate<Strategy>(end, pos); | 168 newSelection = VisibleSelectionTemplate<Strategy>(end, pos); |
182 else | 169 else |
183 newSelection = VisibleSelectionTemplate<Strategy>(start, pos
); | 170 newSelection = VisibleSelectionTemplate<Strategy>(start, pos
); |
184 } | 171 } |
185 } else { | 172 } else { |
(...skipping 16 matching lines...) Expand all Loading... |
202 template <typename Strategy> | 189 template <typename Strategy> |
203 void SelectionController::updateSelectionForMouseDragAlgorithm(const HitTestResu
lt& hitTestResult, Node* mousePressNode, const LayoutPoint& dragStartPos, const
IntPoint& lastKnownMousePosition) | 190 void SelectionController::updateSelectionForMouseDragAlgorithm(const HitTestResu
lt& hitTestResult, Node* mousePressNode, const LayoutPoint& dragStartPos, const
IntPoint& lastKnownMousePosition) |
204 { | 191 { |
205 if (!m_mouseDownMayStartSelect) | 192 if (!m_mouseDownMayStartSelect) |
206 return; | 193 return; |
207 | 194 |
208 Node* target = hitTestResult.innerNode(); | 195 Node* target = hitTestResult.innerNode(); |
209 if (!target) | 196 if (!target) |
210 return; | 197 return; |
211 | 198 |
212 const PositionWithAffinity rawTargetPosition = selection().selection().posit
ionRespectingEditingBoundary(hitTestResult.localPoint(), target); | 199 PositionWithAffinity rawTargetPosition = selection().selection().positionRes
pectingEditingBoundary(hitTestResult.localPoint(), target); |
213 const VisiblePositionTemplate<Strategy> targetPosition = createVisiblePositi
on(fromPositionInDOMTree<Strategy>(rawTargetPosition.position()), rawTargetPosit
ion.affinity()); | 200 VisiblePositionTemplate<Strategy> targetPosition = createVisiblePosition(fro
mPositionInDOMTree<Strategy>(rawTargetPosition)); |
214 // Don't modify the selection if we're not on a node. | 201 // Don't modify the selection if we're not on a node. |
215 if (targetPosition.isNull()) | 202 if (targetPosition.isNull()) |
216 return; | 203 return; |
217 | 204 |
218 // Restart the selection if this is the first mouse move. This work is usual
ly | 205 // Restart the selection if this is the first mouse move. This work is usual
ly |
219 // done in handleMousePressEvent, but not if the mouse press was on an exist
ing selection. | 206 // done in handleMousePressEvent, but not if the mouse press was on an exist
ing selection. |
220 VisibleSelectionTemplate<Strategy> newSelection(selection().selection()); | 207 VisibleSelectionTemplate<Strategy> newSelection = selection().visibleSelecti
on<Strategy>(); |
221 | 208 |
222 // Special case to limit selection to the containing block for SVG text. | 209 // Special case to limit selection to the containing block for SVG text. |
223 // FIXME: Isn't there a better non-SVG-specific way to do this? | 210 // FIXME: Isn't there a better non-SVG-specific way to do this? |
224 if (Node* selectionBaseNode = newSelection.base().anchorNode()) { | 211 if (Node* selectionBaseNode = newSelection.base().anchorNode()) { |
225 if (LayoutObject* selectionBaseLayoutObject = selectionBaseNode->layoutO
bject()) { | 212 if (LayoutObject* selectionBaseLayoutObject = selectionBaseNode->layoutO
bject()) { |
226 if (selectionBaseLayoutObject->isSVGText()) { | 213 if (selectionBaseLayoutObject->isSVGText()) { |
227 if (target->layoutObject()->containingBlock() != selectionBaseLa
youtObject->containingBlock()) | 214 if (target->layoutObject()->containingBlock() != selectionBaseLa
youtObject->containingBlock()) |
228 return; | 215 return; |
229 } | 216 } |
230 } | 217 } |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 } else { | 281 } else { |
295 granularity = CharacterGranularity; | 282 granularity = CharacterGranularity; |
296 m_selectionState = SelectionState::PlacedCaret; | 283 m_selectionState = SelectionState::PlacedCaret; |
297 } | 284 } |
298 | 285 |
299 this->selection().setNonDirectionalSelectionIfNeeded(selection, granularity)
; | 286 this->selection().setNonDirectionalSelectionIfNeeded(selection, granularity)
; |
300 | 287 |
301 return true; | 288 return true; |
302 } | 289 } |
303 | 290 |
| 291 template <typename Strategy> |
304 void SelectionController::selectClosestWordFromHitTestResult(const HitTestResult
& result, AppendTrailingWhitespace appendTrailingWhitespace) | 292 void SelectionController::selectClosestWordFromHitTestResult(const HitTestResult
& result, AppendTrailingWhitespace appendTrailingWhitespace) |
305 { | 293 { |
306 Node* innerNode = result.innerNode(); | 294 Node* innerNode = result.innerNode(); |
307 VisibleSelection newSelection; | 295 VisibleSelectionTemplate<Strategy> newSelection; |
308 | 296 |
309 if (!innerNode || !innerNode->layoutObject()) | 297 if (!innerNode || !innerNode->layoutObject()) |
310 return; | 298 return; |
311 | 299 |
312 VisiblePosition pos = createVisiblePosition(innerNode->layoutObject()->posit
ionForPoint(result.localPoint())); | 300 const VisiblePositionTemplate<Strategy> pos = createVisiblePosition(fromPosi
tionInDOMTree<Strategy>(innerNode->layoutObject()->positionForPoint(result.local
Point()))); |
313 if (pos.isNotNull()) { | 301 if (pos.isNotNull()) { |
314 newSelection = VisibleSelection(pos); | 302 newSelection = VisibleSelectionTemplate<Strategy>(pos); |
315 expandSelectionUsingGranularity(newSelection, WordGranularity); | 303 newSelection.expandUsingGranularity(WordGranularity); |
316 } | 304 } |
317 | 305 |
318 #if OS(ANDROID) | 306 #if OS(ANDROID) |
319 // If node is not editable and doesn't have text except space, tab or | 307 // If node is not editable and doesn't have text except space, tab or |
320 // line break, do not select that 'empty' area. | 308 // line break, do not select that 'empty' area. |
321 if (!innerNode->hasEditableStyle()) { | 309 if (!innerNode->hasEditableStyle()) { |
322 const EphemeralRangeInComposedTree range = EphemeralRangeInComposedTree(
newSelection.startInComposedTree(), newSelection.endInComposedTree()); | 310 EphemeralRangeTemplate<Strategy> range = EphemeralRangeTemplate<Strategy
>(newSelection.start(), newSelection.end()); |
323 String str = plainText(range, TextIteratorDefaultBehavior); | 311 String str = plainText(range, TextIteratorDefaultBehavior); |
324 if (str.isEmpty() || str.simplifyWhiteSpace().containsOnlyWhitespace()) | 312 if (str.isEmpty() || str.simplifyWhiteSpace().containsOnlyWhitespace()) |
325 return; | 313 return; |
326 } | 314 } |
327 #endif | 315 #endif |
328 | 316 |
329 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && ne
wSelection.isRange()) | 317 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && ne
wSelection.isRange()) |
330 newSelection.appendTrailingWhitespace(); | 318 newSelection.appendTrailingWhitespace(); |
331 | 319 |
332 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); | 320 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); |
333 } | 321 } |
334 | 322 |
| 323 template <typename Strategy> |
335 void SelectionController::selectClosestMisspellingFromHitTestResult(const HitTes
tResult& result, AppendTrailingWhitespace appendTrailingWhitespace) | 324 void SelectionController::selectClosestMisspellingFromHitTestResult(const HitTes
tResult& result, AppendTrailingWhitespace appendTrailingWhitespace) |
336 { | 325 { |
337 Node* innerNode = result.innerNode(); | 326 Node* innerNode = result.innerNode(); |
338 VisibleSelection newSelection; | 327 VisibleSelectionTemplate<Strategy> newSelection; |
339 | 328 |
340 if (!innerNode || !innerNode->layoutObject()) | 329 if (!innerNode || !innerNode->layoutObject()) |
341 return; | 330 return; |
342 | 331 |
343 VisiblePosition pos = createVisiblePosition(innerNode->layoutObject()->posit
ionForPoint(result.localPoint())); | 332 VisiblePositionTemplate<Strategy> pos = createVisiblePosition(fromPositionIn
DOMTree<Strategy>(innerNode->layoutObject()->positionForPoint(result.localPoint(
)))); |
344 if (pos.isNotNull()) { | 333 if (pos.isNotNull()) { |
345 const Position markerPosition = pos.deepEquivalent().parentAnchoredEquiv
alent(); | 334 const PositionAlgorithm<Strategy> markerPosition = pos.deepEquivalent().
parentAnchoredEquivalent(); |
346 DocumentMarkerVector markers = innerNode->document().markers().markersIn
Range(EphemeralRange(markerPosition), DocumentMarker::MisspellingMarkers()); | 335 DocumentMarkerVector markers = innerNode->document().markers().markersIn
Range(EphemeralRange(toPositionInDOMTree(markerPosition)), DocumentMarker::Missp
ellingMarkers()); |
347 if (markers.size() == 1) { | 336 if (markers.size() == 1) { |
348 Node* containerNode = markerPosition.computeContainerNode(); | 337 Node* containerNode = markerPosition.computeContainerNode(); |
349 const Position start(containerNode, markers[0]->startOffset()); | 338 const PositionAlgorithm<Strategy> start(containerNode, markers[0]->s
tartOffset()); |
350 const Position end(containerNode, markers[0]->endOffset()); | 339 const PositionAlgorithm<Strategy> end(containerNode, markers[0]->end
Offset()); |
351 newSelection = VisibleSelection(start, end); | 340 newSelection = VisibleSelectionTemplate<Strategy>(start, end); |
352 } | 341 } |
353 } | 342 } |
354 | 343 |
355 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && ne
wSelection.isRange()) | 344 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && ne
wSelection.isRange()) |
356 newSelection.appendTrailingWhitespace(); | 345 newSelection.appendTrailingWhitespace(); |
357 | 346 |
358 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); | 347 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); |
359 } | 348 } |
360 | 349 |
361 void SelectionController::selectClosestWordFromMouseEvent(const MouseEventWithHi
tTestResults& result) | 350 void SelectionController::selectClosestWordFromMouseEvent(const MouseEventWithHi
tTestResults& result) |
362 { | 351 { |
363 if (!m_mouseDownMayStartSelect) | 352 if (!m_mouseDownMayStartSelect) |
364 return; | 353 return; |
365 | 354 |
366 selectClosestWordFromHitTestResult(result.hitTestResult(), | 355 AppendTrailingWhitespace appendTrailingWhitespace = (result.event().clickCou
nt() == 2 && m_frame->editor().isSelectTrailingWhitespaceEnabled()) ? AppendTrai
lingWhitespace::ShouldAppend : AppendTrailingWhitespace::DontAppend; |
367 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingW
hitespaceEnabled()) ? AppendTrailingWhitespace::ShouldAppend : AppendTrailingWhi
tespace::DontAppend); | 356 |
| 357 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 358 return selectClosestWordFromHitTestResult<EditingInComposedTreeStrategy>
(result.hitTestResult(), appendTrailingWhitespace); |
| 359 selectClosestWordFromHitTestResult<EditingStrategy>(result.hitTestResult(),
appendTrailingWhitespace); |
368 } | 360 } |
369 | 361 |
| 362 template <typename Strategy> |
370 void SelectionController::selectClosestMisspellingFromMouseEvent(const MouseEven
tWithHitTestResults& result) | 363 void SelectionController::selectClosestMisspellingFromMouseEvent(const MouseEven
tWithHitTestResults& result) |
371 { | 364 { |
372 if (!m_mouseDownMayStartSelect) | 365 if (!m_mouseDownMayStartSelect) |
373 return; | 366 return; |
374 | 367 |
375 selectClosestMisspellingFromHitTestResult(result.hitTestResult(), | 368 selectClosestMisspellingFromHitTestResult<Strategy>(result.hitTestResult(), |
376 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingW
hitespaceEnabled()) ? AppendTrailingWhitespace::ShouldAppend : AppendTrailingWhi
tespace::DontAppend); | 369 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingW
hitespaceEnabled()) ? AppendTrailingWhitespace::ShouldAppend : AppendTrailingWhi
tespace::DontAppend); |
377 | 370 |
378 } | 371 } |
379 | 372 |
| 373 template <typename Strategy> |
380 void SelectionController::selectClosestWordOrLinkFromMouseEvent(const MouseEvent
WithHitTestResults& result) | 374 void SelectionController::selectClosestWordOrLinkFromMouseEvent(const MouseEvent
WithHitTestResults& result) |
381 { | 375 { |
382 if (!result.hitTestResult().isLiveLink()) | 376 if (!result.hitTestResult().isLiveLink()) |
383 return selectClosestWordFromMouseEvent(result); | 377 return selectClosestWordFromMouseEvent(result); |
384 | 378 |
385 Node* innerNode = result.innerNode(); | 379 Node* innerNode = result.innerNode(); |
386 | 380 |
387 if (!innerNode || !innerNode->layoutObject() || !m_mouseDownMayStartSelect) | 381 if (!innerNode || !innerNode->layoutObject() || !m_mouseDownMayStartSelect) |
388 return; | 382 return; |
389 | 383 |
390 VisibleSelection newSelection; | 384 VisibleSelectionTemplate<Strategy> newSelection; |
391 Element* URLElement = result.hitTestResult().URLElement(); | 385 Element* URLElement = result.hitTestResult().URLElement(); |
392 VisiblePosition pos = createVisiblePosition(innerNode->layoutObject()->posit
ionForPoint(result.localPoint())); | 386 const VisiblePositionTemplate<Strategy> pos = createVisiblePosition(fromPosi
tionInDOMTree<Strategy>(innerNode->layoutObject()->positionForPoint(result.local
Point()))); |
393 if (pos.isNotNull() && pos.deepEquivalent().anchorNode()->isDescendantOf(URL
Element)) | 387 if (pos.isNotNull() && pos.deepEquivalent().anchorNode()->isDescendantOf(URL
Element)) |
394 newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement)
; | 388 newSelection = VisibleSelectionTemplate<Strategy>::selectionFromContents
OfNode(URLElement); |
395 | 389 |
396 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); | 390 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection
ToRespectUserSelectAll(innerNode, newSelection), WordGranularity); |
397 } | 391 } |
398 | 392 |
399 bool SelectionController::handleMousePressEventDoubleClick(const MouseEventWithH
itTestResults& event) | 393 bool SelectionController::handleMousePressEventDoubleClick(const MouseEventWithH
itTestResults& event) |
400 { | 394 { |
401 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventDoubleClick
"); | 395 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventDoubleClick
"); |
402 | 396 |
403 if (event.event().button() != LeftButton) | 397 if (event.event().button() != LeftButton) |
404 return false; | 398 return false; |
405 | 399 |
406 if (selection().isRange()) { | 400 if (selection().isRange()) { |
407 // A double-click when range is already selected | 401 // A double-click when range is already selected |
408 // should not change the selection. So, do not call | 402 // should not change the selection. So, do not call |
409 // selectClosestWordFromMouseEvent, but do set | 403 // selectClosestWordFromMouseEvent, but do set |
410 // m_beganSelectingText to prevent handleMouseReleaseEvent | 404 // m_beganSelectingText to prevent handleMouseReleaseEvent |
411 // from setting caret selection. | 405 // from setting caret selection. |
412 m_selectionState = SelectionState::ExtendedSelection; | 406 m_selectionState = SelectionState::ExtendedSelection; |
413 } else { | 407 } else { |
414 selectClosestWordFromMouseEvent(event); | 408 selectClosestWordFromMouseEvent(event); |
415 } | 409 } |
416 return true; | 410 return true; |
417 } | 411 } |
418 | 412 |
419 bool SelectionController::handleMousePressEventTripleClick(const MouseEventWithH
itTestResults& event) | 413 template <typename Strategy> |
| 414 bool SelectionController::handleMousePressEventTripleClickAlgorithm(const MouseE
ventWithHitTestResults& event) |
420 { | 415 { |
421 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventTripleClick
"); | 416 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventTripleClick
"); |
422 | 417 |
423 if (event.event().button() != LeftButton) | 418 if (event.event().button() != LeftButton) |
424 return false; | 419 return false; |
425 | 420 |
426 Node* innerNode = event.innerNode(); | 421 Node* innerNode = event.innerNode(); |
427 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect)) | 422 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect)) |
428 return false; | 423 return false; |
429 | 424 |
430 VisibleSelection newSelection; | 425 VisibleSelectionTemplate<Strategy> newSelection; |
431 VisiblePosition pos = createVisiblePosition(innerNode->layoutObject()->posit
ionForPoint(event.localPoint())); | 426 const VisiblePositionTemplate<Strategy> pos = createVisiblePosition(fromPosi
tionInDOMTree<Strategy>(innerNode->layoutObject()->positionForPoint(event.localP
oint()))); |
432 if (pos.isNotNull()) { | 427 if (pos.isNotNull()) { |
433 newSelection = VisibleSelection(pos); | 428 newSelection = VisibleSelectionTemplate<Strategy>(pos); |
434 expandSelectionUsingGranularity(newSelection, ParagraphGranularity); | 429 newSelection.expandUsingGranularity(ParagraphGranularity); |
435 } | 430 } |
436 | 431 |
437 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSe
lectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity); | 432 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSe
lectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity); |
438 } | 433 } |
439 | 434 |
| 435 bool SelectionController::handleMousePressEventTripleClick(const MouseEventWithH
itTestResults& event) |
| 436 { |
| 437 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 438 return handleMousePressEventTripleClickAlgorithm<EditingInComposedTreeSt
rategy>(event); |
| 439 return handleMousePressEventTripleClickAlgorithm<EditingStrategy>(event); |
| 440 } |
| 441 |
440 bool SelectionController::handleMousePressEventSingleClick(const MouseEventWithH
itTestResults& event) | 442 bool SelectionController::handleMousePressEventSingleClick(const MouseEventWithH
itTestResults& event) |
441 { | 443 { |
442 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) | 444 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
443 return handleMousePressEventSingleClickAlgorithm<EditingInComposedTreeSt
rategy>(event); | 445 return handleMousePressEventSingleClickAlgorithm<EditingInComposedTreeSt
rategy>(event); |
444 return handleMousePressEventSingleClickAlgorithm<EditingStrategy>(event); | 446 return handleMousePressEventSingleClickAlgorithm<EditingStrategy>(event); |
445 } | 447 } |
446 | 448 |
447 void SelectionController::handleMousePressEvent(const MouseEventWithHitTestResul
ts& event) | 449 void SelectionController::handleMousePressEvent(const MouseEventWithHitTestResul
ts& event) |
448 { | 450 { |
449 // If we got the event back, that must mean it wasn't prevented, | 451 // If we got the event back, that must mean it wasn't prevented, |
(...skipping 29 matching lines...) Expand all Loading... |
479 updateSelectionForMouseDrag(result, mousePressNode, dragStartPos, lastKnownM
ousePosition); | 481 updateSelectionForMouseDrag(result, mousePressNode, dragStartPos, lastKnownM
ousePosition); |
480 } | 482 } |
481 | 483 |
482 void SelectionController::updateSelectionForMouseDrag(const HitTestResult& hitTe
stResult, Node* mousePressNode, const LayoutPoint& dragStartPos, const IntPoint&
lastKnownMousePosition) | 484 void SelectionController::updateSelectionForMouseDrag(const HitTestResult& hitTe
stResult, Node* mousePressNode, const LayoutPoint& dragStartPos, const IntPoint&
lastKnownMousePosition) |
483 { | 485 { |
484 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) | 486 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
485 return updateSelectionForMouseDragAlgorithm<EditingInComposedTreeStrateg
y>(hitTestResult, mousePressNode, dragStartPos, lastKnownMousePosition); | 487 return updateSelectionForMouseDragAlgorithm<EditingInComposedTreeStrateg
y>(hitTestResult, mousePressNode, dragStartPos, lastKnownMousePosition); |
486 updateSelectionForMouseDragAlgorithm<EditingStrategy>(hitTestResult, mousePr
essNode, dragStartPos, lastKnownMousePosition); | 488 updateSelectionForMouseDragAlgorithm<EditingStrategy>(hitTestResult, mousePr
essNode, dragStartPos, lastKnownMousePosition); |
487 } | 489 } |
488 | 490 |
489 bool SelectionController::handleMouseReleaseEvent(const MouseEventWithHitTestRes
ults& event, const LayoutPoint& dragStartPos) | 491 template <typename Strategy> |
| 492 bool SelectionController::handleMouseReleaseEventAlgorithm(const MouseEventWithH
itTestResults& event, const LayoutPoint& dragStartPos) |
490 { | 493 { |
491 bool handled = false; | 494 bool handled = false; |
492 m_mouseDownMayStartSelect = false; | 495 m_mouseDownMayStartSelect = false; |
493 // Clear the selection if the mouse didn't move after the last mouse | 496 // Clear the selection if the mouse didn't move after the last mouse |
494 // press and it's not a context menu click. We do this so when clicking | 497 // press and it's not a context menu click. We do this so when clicking |
495 // on the selection, the selection goes away. However, if we are | 498 // on the selection, the selection goes away. However, if we are |
496 // editing, place the caret. | 499 // editing, place the caret. |
497 if (m_mouseDownWasSingleClickInSelection && m_selectionState != SelectionSta
te::ExtendedSelection | 500 if (m_mouseDownWasSingleClickInSelection && m_selectionState != SelectionSta
te::ExtendedSelection |
498 && dragStartPos == event.event().position() | 501 && dragStartPos == event.event().position() |
499 && selection().isRange() | 502 && selection().isRange() |
500 && event.event().button() != RightButton) { | 503 && event.event().button() != RightButton) { |
501 VisibleSelection newSelection; | 504 VisibleSelectionTemplate<Strategy> newSelection; |
502 Node* node = event.innerNode(); | 505 Node* node = event.innerNode(); |
503 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBr
owsingEnabled(); | 506 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBr
owsingEnabled(); |
504 if (node && node->layoutObject() && (caretBrowsing || node->hasEditableS
tyle())) { | 507 if (node && node->layoutObject() && (caretBrowsing || node->hasEditableS
tyle())) { |
505 VisiblePosition pos = createVisiblePosition(node->layoutObject()->po
sitionForPoint(event.localPoint())); | 508 const VisiblePositionTemplate<Strategy> pos = createVisiblePosition(
fromPositionInDOMTree<Strategy>(node->layoutObject()->positionForPoint(event.loc
alPoint()))); |
506 newSelection = VisibleSelection(pos); | 509 newSelection = VisibleSelectionTemplate<Strategy>(pos); |
507 } | 510 } |
508 | 511 |
509 setSelectionIfNeeded(selection(), newSelection); | 512 setSelectionIfNeeded(selection(), newSelection); |
510 | 513 |
511 handled = true; | 514 handled = true; |
512 } | 515 } |
513 | 516 |
514 selection().notifyLayoutObjectOfSelectionChange(UserTriggered); | 517 selection().notifyLayoutObjectOfSelectionChange(UserTriggered); |
515 | 518 |
516 selection().selectFrameElementInParentIfFullySelected(); | 519 selection().selectFrameElementInParentIfFullySelected(); |
517 | 520 |
518 if (event.event().button() == MiddleButton && !event.isOverLink()) { | 521 if (event.event().button() == MiddleButton && !event.isOverLink()) { |
519 // Ignore handled, since we want to paste to where the caret was placed
anyway. | 522 // Ignore handled, since we want to paste to where the caret was placed
anyway. |
520 handled = handlePasteGlobalSelection(event.event()) || handled; | 523 handled = handlePasteGlobalSelection(event.event()) || handled; |
521 } | 524 } |
522 | 525 |
523 return handled; | 526 return handled; |
524 } | 527 } |
525 | 528 |
| 529 bool SelectionController::handleMouseReleaseEvent(const MouseEventWithHitTestRes
ults& event, const LayoutPoint& dragStartPos) |
| 530 { |
| 531 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 532 return handleMouseReleaseEventAlgorithm<EditingInComposedTreeStrategy>(e
vent, dragStartPos); |
| 533 return handleMouseReleaseEventAlgorithm<EditingStrategy>(event, dragStartPos
); |
| 534 } |
526 | 535 |
527 bool SelectionController::handlePasteGlobalSelection(const PlatformMouseEvent& m
ouseEvent) | 536 bool SelectionController::handlePasteGlobalSelection(const PlatformMouseEvent& m
ouseEvent) |
528 { | 537 { |
529 // If the event was a middle click, attempt to copy global selection in afte
r | 538 // If the event was a middle click, attempt to copy global selection in afte
r |
530 // the newly set caret position. | 539 // the newly set caret position. |
531 // | 540 // |
532 // This code is called from either the mouse up or mouse down handling. Ther
e | 541 // This code is called from either the mouse up or mouse down handling. Ther
e |
533 // is some debate about when the global selection is pasted: | 542 // is some debate about when the global selection is pasted: |
534 // xterm: pastes on up. | 543 // xterm: pastes on up. |
535 // GTK: pastes on down. | 544 // GTK: pastes on down. |
(...skipping 12 matching lines...) Expand all Loading... |
548 if (!m_frame->page()) | 557 if (!m_frame->page()) |
549 return false; | 558 return false; |
550 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame(); | 559 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame(); |
551 // Do not paste here if the focus was moved somewhere else. | 560 // Do not paste here if the focus was moved somewhere else. |
552 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSele
ction()) | 561 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSele
ction()) |
553 return m_frame->editor().command("PasteGlobalSelection").execute(); | 562 return m_frame->editor().command("PasteGlobalSelection").execute(); |
554 | 563 |
555 return false; | 564 return false; |
556 } | 565 } |
557 | 566 |
558 bool SelectionController::handleGestureLongPress(const PlatformGestureEvent& ges
tureEvent, const HitTestResult& hitTestResult) | 567 template <typename Strategy> |
| 568 bool SelectionController::handleGestureLongPressAlgorithm(const PlatformGestureE
vent& gestureEvent, const HitTestResult& hitTestResult) |
559 { | 569 { |
560 if (hitTestResult.isLiveLink()) | 570 if (hitTestResult.isLiveLink()) |
561 return false; | 571 return false; |
562 | 572 |
563 #if OS(ANDROID) | 573 #if OS(ANDROID) |
564 bool shouldLongPressSelectWord = true; | 574 bool shouldLongPressSelectWord = true; |
565 #else | 575 #else |
566 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()-
>touchEditingEnabled(); | 576 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()-
>touchEditingEnabled(); |
567 #endif | 577 #endif |
568 if (!shouldLongPressSelectWord) | 578 if (!shouldLongPressSelectWord) |
569 return false; | 579 return false; |
570 | 580 |
571 Node* innerNode = hitTestResult.innerNode(); | 581 Node* innerNode = hitTestResult.innerNode(); |
572 #if OS(ANDROID) | 582 #if OS(ANDROID) |
573 bool innerNodeIsSelectable = innerNode && (innerNode->isContentEditable() ||
innerNode->isTextNode() || innerNode->canStartSelection()); | 583 bool innerNodeIsSelectable = innerNode && (innerNode->isContentEditable() ||
innerNode->isTextNode() || innerNode->canStartSelection()); |
574 #else | 584 #else |
575 bool innerNodeIsSelectable = innerNode && (innerNode->isContentEditable() ||
innerNode->isTextNode()); | 585 bool innerNodeIsSelectable = innerNode && (innerNode->isContentEditable() ||
innerNode->isTextNode()); |
576 #endif | 586 #endif |
577 if (!innerNodeIsSelectable) | 587 if (!innerNodeIsSelectable) |
578 return false; | 588 return false; |
579 | 589 |
580 selectClosestWordFromHitTestResult(hitTestResult, AppendTrailingWhitespace::
DontAppend); | 590 selectClosestWordFromHitTestResult<Strategy>(hitTestResult, AppendTrailingWh
itespace::DontAppend); |
581 if (!selection().isRange()) | 591 return selection().isRange(); |
582 return false; | 592 } |
583 return true; | 593 |
| 594 bool SelectionController::handleGestureLongPress(const PlatformGestureEvent& ges
tureEvent, const HitTestResult& hitTestResult) |
| 595 { |
| 596 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 597 return handleGestureLongPressAlgorithm<EditingInComposedTreeStrategy>(ge
stureEvent, hitTestResult); |
| 598 return handleGestureLongPressAlgorithm<EditingStrategy>(gestureEvent, hitTes
tResult); |
584 } | 599 } |
585 | 600 |
586 void SelectionController::sendContextMenuEvent(const MouseEventWithHitTestResult
s& mev, const LayoutPoint& position) | 601 void SelectionController::sendContextMenuEvent(const MouseEventWithHitTestResult
s& mev, const LayoutPoint& position) |
587 { | 602 { |
588 if (selection().contains(position) | 603 if (selection().contains(position) |
589 || mev.scrollbar() | 604 || mev.scrollbar() |
590 // FIXME: In the editable case, word selection sometimes selects content
that isn't underneath the mouse. | 605 // FIXME: In the editable case, word selection sometimes selects content
that isn't underneath the mouse. |
591 // If the selection is non-editable, we do word selection to make it eas
ier to use the contextual menu items | 606 // If the selection is non-editable, we do word selection to make it eas
ier to use the contextual menu items |
592 // available for text selections. But only if we're above text. | 607 // available for text selections. But only if we're above text. |
593 || !(selection().isContentEditable() || (mev.innerNode() && mev.innerNod
e()->isTextNode()))) | 608 || !(selection().isContentEditable() || (mev.innerNode() && mev.innerNod
e()->isTextNode()))) |
594 return; | 609 return; |
595 | 610 |
596 m_mouseDownMayStartSelect = true; // context menu events are always allowed
to perform a selection | 611 m_mouseDownMayStartSelect = true; // context menu events are always allowed
to perform a selection |
597 | 612 |
598 if (mev.hitTestResult().isMisspelled()) | 613 if (mev.hitTestResult().isMisspelled()) { |
599 selectClosestMisspellingFromMouseEvent(mev); | 614 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
600 else if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick()) | 615 return selectClosestMisspellingFromMouseEvent<EditingInComposedTreeS
trategy>(mev); |
601 selectClosestWordOrLinkFromMouseEvent(mev); | 616 return selectClosestMisspellingFromMouseEvent<EditingStrategy>(mev); |
| 617 } |
| 618 |
| 619 if (!m_frame->editor().behavior().shouldSelectOnContextualMenuClick()) |
| 620 return; |
| 621 |
| 622 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 623 return selectClosestWordOrLinkFromMouseEvent<EditingInComposedTreeStrate
gy>(mev); |
| 624 selectClosestWordOrLinkFromMouseEvent<EditingStrategy>(mev); |
602 } | 625 } |
603 | 626 |
604 void SelectionController::passMousePressEventToSubframe(const MouseEventWithHitT
estResults& mev) | 627 template <typename Strategy> |
| 628 void SelectionController::passMousePressEventToSubframeAlgorithm(const MouseEven
tWithHitTestResults& mev) |
605 { | 629 { |
606 // If we're clicking into a frame that is selected, the frame will appear | 630 // If we're clicking into a frame that is selected, the frame will appear |
607 // greyed out even though we're clicking on the selection. This looks | 631 // greyed out even though we're clicking on the selection. This looks |
608 // really strange (having the whole frame be greyed out), so we deselect the | 632 // really strange (having the whole frame be greyed out), so we deselect the |
609 // selection. | 633 // selection. |
610 IntPoint p = m_frame->view()->rootFrameToContents(mev.event().position()); | 634 IntPoint p = m_frame->view()->rootFrameToContents(mev.event().position()); |
611 if (!selection().contains(p)) | 635 if (!selection().contains(p)) |
612 return; | 636 return; |
613 | 637 |
614 VisiblePosition visiblePos = createVisiblePosition( | 638 const VisiblePositionTemplate<Strategy> visiblePos = createVisiblePosition( |
615 mev.innerNode()->layoutObject()->positionForPoint(mev.localPoint())); | 639 fromPositionInDOMTree<Strategy>(mev.innerNode()->layoutObject()->positio
nForPoint(mev.localPoint()))); |
616 VisibleSelection newSelection(visiblePos); | 640 VisibleSelectionTemplate<Strategy> newSelection(visiblePos); |
617 selection().setSelection(newSelection); | 641 selection().setSelection(newSelection); |
618 } | 642 } |
619 | 643 |
| 644 void SelectionController::passMousePressEventToSubframe(const MouseEventWithHitT
estResults& mev) |
| 645 { |
| 646 if (RuntimeEnabledFeatures::selectionForComposedTreeEnabled()) |
| 647 return passMousePressEventToSubframeAlgorithm<EditingInComposedTreeStrat
egy>(mev); |
| 648 passMousePressEventToSubframeAlgorithm<EditingStrategy>(mev); |
| 649 } |
| 650 |
620 void SelectionController::initializeSelectionState() | 651 void SelectionController::initializeSelectionState() |
621 { | 652 { |
622 m_selectionState = SelectionState::HaveNotStartedSelection; | 653 m_selectionState = SelectionState::HaveNotStartedSelection; |
623 } | 654 } |
624 | 655 |
625 void SelectionController::setMouseDownMayStartSelect(bool mayStartSelect) | 656 void SelectionController::setMouseDownMayStartSelect(bool mayStartSelect) |
626 { | 657 { |
627 m_mouseDownMayStartSelect = mayStartSelect; | 658 m_mouseDownMayStartSelect = mayStartSelect; |
628 } | 659 } |
629 | 660 |
(...skipping 16 matching lines...) Expand all Loading... |
646 else | 677 else |
647 m_selectionState = SelectionState::HaveNotStartedSelection; | 678 m_selectionState = SelectionState::HaveNotStartedSelection; |
648 } | 679 } |
649 | 680 |
650 FrameSelection& SelectionController::selection() const | 681 FrameSelection& SelectionController::selection() const |
651 { | 682 { |
652 return m_frame->selection(); | 683 return m_frame->selection(); |
653 } | 684 } |
654 | 685 |
655 } // namespace blink | 686 } // namespace blink |
OLD | NEW |