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

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

Issue 1113323002: [Reland] Refactor the selection code in EventHandler (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Created 5 years, 6 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
« no previous file with comments | « Source/core/editing/SelectionController.h ('k') | Source/core/html/HTMLLabelElement.cpp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserv ed.
3 * Copyright (C) 2006 Alexey Proskuryakov (ap@webkit.org)
4 * Copyright (C) 2012 Digia Plc. and/or its subsidiary(-ies)
5 * Copyright (C) 2015 Google Inc. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "config.h"
30 #include "core/editing/SelectionController.h"
31
32 #include "core/HTMLNames.h"
33 #include "core/dom/Document.h"
34 #include "core/dom/DocumentMarkerController.h"
35 #include "core/editing/Editor.h"
36 #include "core/editing/FrameSelection.h"
37 #include "core/editing/htmlediting.h"
38 #include "core/editing/iterators/TextIterator.h"
39 #include "core/events/Event.h"
40 #include "core/frame/FrameView.h"
41 #include "core/frame/LocalFrame.h"
42 #include "core/frame/Settings.h"
43 #include "core/layout/LayoutView.h"
44 #include "core/page/FocusController.h"
45 #include "core/page/Page.h"
46 #include "platform/RuntimeEnabledFeatures.h"
47
48 namespace blink {
49
50 namespace {
51
52 void setSelectionIfNeeded(FrameSelection& selection, const VisibleSelection& new Selection)
53 {
54 if (selection.selection() == newSelection)
tkent 2015/06/04 03:34:18 Please don't change code structure when you move t
Miyoung Shin(g) 2015/06/04 08:53:03 'early return' is required by yosin@. I think I ne
55 return;
56 selection.setSelection(newSelection);
57 }
58
59 bool dispatchSelectStart(Node* node)
60 {
61 if (!node || !node->layoutObject())
62 return true;
63
64 return node->dispatchEvent(Event::createCancelableBubble(EventTypeNames::sel ectstart));
65 }
66
67 VisibleSelection expandSelectionToRespectUserSelectAll(Node* targetNode, const V isibleSelection& selection)
68 {
69 Node* rootUserSelectAll = Position::rootUserSelectAllForNode(targetNode);
70 if (!rootUserSelectAll)
71 return selection;
72
73 VisibleSelection newSelection(selection);
74 newSelection.setBase(positionBeforeNode(rootUserSelectAll).upstream(CanCross EditingBoundary));
75 newSelection.setExtent(positionAfterNode(rootUserSelectAll).downstream(CanCr ossEditingBoundary));
76
77 return newSelection;
78 }
79
80 bool canMouseDownStartSelect(Node* node)
81 {
82 if (!node || !node->layoutObject())
83 return true;
84
85 if (!node->canStartSelection())
86 return false;
87
88 return true;
89 }
90
91 int textDistance(const Position& start, const Position& end)
92 {
93 RefPtrWillBeRawPtr<Range> range = Range::create(*start.document(), start, en d);
94 return TextIterator::rangeLength(range->startPosition(), range->endPosition( ), true);
95 }
96
97 } // anonymous namespace
98
99 PassOwnPtrWillBeRawPtr<SelectionController> SelectionController::create(LocalFra me& frame)
100 {
101 return adoptPtrWillBeNoop(new SelectionController(frame));
102 }
103
104 SelectionController::SelectionController(LocalFrame& frame)
105 : m_frame(&frame)
106 , m_mouseDownMayStartSelect(false)
107 , m_singleClickInSelection(false)
108 , m_selectionState(SelectionState::HaveNotStartedSelection)
109 {
110 }
111
112 DEFINE_TRACE(SelectionController)
113 {
114 visitor->trace(m_frame);
115 }
116
117 bool SelectionController::updateSelectionForMouseDownDispatchingSelectStart(Node * targetNode, const VisibleSelection& visibleSelection, TextGranularity granular ity)
118 {
119 if (Position::nodeIsUserSelectNone(targetNode))
120 return false;
121
122 if (!dispatchSelectStart(targetNode))
123 return false;
124
125 if (visibleSelection.isRange()) {
126 m_selectionState = SelectionState::ExtendedSelection;
127 } else {
128 granularity = CharacterGranularity;
129 m_selectionState = SelectionState::PlacedCaret;
130 }
131
132 selection().setNonDirectionalSelectionIfNeeded(visibleSelection, granularity );
133
134 return true;
135 }
136
137 void SelectionController::selectClosestWordFromHitTestResult(const HitTestResult & result, AppendTrailingWhitespace appendTrailingWhitespace)
138 {
139 Node* innerNode = result.innerNode();
140 VisibleSelection newSelection;
141
142 if (innerNode && innerNode->layoutObject()) {
143 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.l ocalPoint()));
144 if (pos.isNotNull()) {
145 newSelection = VisibleSelection(pos);
146 newSelection.expandUsingGranularity(WordGranularity);
147 }
148
149 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend & & newSelection.isRange())
150 newSelection.appendTrailingWhitespace();
151
152 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelec tionToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
153 }
154 }
155
156 void SelectionController::selectClosestMisspellingFromHitTestResult(const HitTes tResult& result, AppendTrailingWhitespace appendTrailingWhitespace)
157 {
158 Node* innerNode = result.innerNode();
159 VisibleSelection newSelection;
160
161 if (!innerNode || !innerNode->layoutObject())
162 return;
163
164 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.local Point()));
165 Position start = pos.deepEquivalent();
166 Position end = pos.deepEquivalent();
167 if (pos.isNotNull()) {
168 DocumentMarkerVector markers = innerNode->document().markers().markersIn Range(makeRange(pos, pos).get(), DocumentMarker::MisspellingMarkers());
169 if (markers.size() == 1) {
170 start.moveToOffset(markers[0]->startOffset());
171 end.moveToOffset(markers[0]->endOffset());
172 newSelection = VisibleSelection(start, end);
173 }
174 }
175
176 if (appendTrailingWhitespace == AppendTrailingWhitespace::ShouldAppend && ne wSelection.isRange())
177 newSelection.appendTrailingWhitespace();
178
179 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection ToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
180 }
181
182 void SelectionController::selectClosestWordFromMouseEvent(const MouseEventWithHi tTestResults& result)
183 {
184 if (!m_mouseDownMayStartSelect)
185 return;
186
187 selectClosestWordFromHitTestResult(result.hitTestResult(),
188 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingW hitespaceEnabled()) ? AppendTrailingWhitespace::ShouldAppend : AppendTrailingWhi tespace::DontAppend);
189 }
190
191 void SelectionController::selectClosestMisspellingFromMouseEvent(const MouseEven tWithHitTestResults& result)
192 {
193 if (!m_mouseDownMayStartSelect)
194 return;
195
196 selectClosestMisspellingFromHitTestResult(result.hitTestResult(),
197 (result.event().clickCount() == 2 && m_frame->editor().isSelectTrailingW hitespaceEnabled()) ? AppendTrailingWhitespace::ShouldAppend : AppendTrailingWhi tespace::DontAppend);
198 }
199
200 void SelectionController::selectClosestWordOrLinkFromMouseEvent(const MouseEvent WithHitTestResults& result)
201 {
202 if (!result.hitTestResult().isLiveLink())
203 return selectClosestWordFromMouseEvent(result);
204
205 Node* innerNode = result.innerNode();
206
207 if (!innerNode || !innerNode->layoutObject() || !m_mouseDownMayStartSelect)
208 return;
209
210 VisibleSelection newSelection;
211 Element* URLElement = result.hitTestResult().URLElement();
212 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(result.local Point()));
213 if (pos.isNotNull() && pos.deepEquivalent().deprecatedNode()->isDescendantOf (URLElement))
214 newSelection = VisibleSelection::selectionFromContentsOfNode(URLElement) ;
215
216 updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSelection ToRespectUserSelectAll(innerNode, newSelection), WordGranularity);
217 }
218
219 void SelectionController::handleMousePressEvent(const MouseEventWithHitTestResul ts& event)
220 {
221 // If we got the event back, that must mean it wasn't prevented,
222 // so it's allowed to start a drag or selection if it wasn't in a scrollbar.
223 m_mouseDownMayStartSelect = canMouseDownStartSelect(event.innerNode()) && !e vent.scrollbar();
224 m_singleClickInSelection = false;
225 }
226
227 bool SelectionController::handleMousePressEventDoubleClick(const MouseEventWithH itTestResults& event)
228 {
229 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventDoubleClick ");
230
231 if (event.event().button() != LeftButton)
232 return false;
233
234 if (selection().isRange()) {
235 // A double-click when range is already selected
236 // should not change the selection. So, do not call
237 // selectClosestWordFromMouseEvent, but do set
238 // m_beganSelectingText to prevent handleMouseReleaseEvent
239 // from setting caret selection.
240 m_selectionState = SelectionState::ExtendedSelection;
241 } else {
242 selectClosestWordFromMouseEvent(event);
243 }
244 return true;
245 }
246
247 bool SelectionController::handleMousePressEventTripleClick(const MouseEventWithH itTestResults& event)
248 {
249 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventTripleClick ");
250
251 if (event.event().button() != LeftButton)
252 return false;
253
254 Node* innerNode = event.innerNode();
255 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect))
256 return false;
257
258 VisibleSelection newSelection;
259 VisiblePosition pos(innerNode->layoutObject()->positionForPoint(event.localP oint()));
260 if (pos.isNotNull()) {
261 newSelection = VisibleSelection(pos);
262 newSelection.expandUsingGranularity(ParagraphGranularity);
263 }
264
265 return updateSelectionForMouseDownDispatchingSelectStart(innerNode, expandSe lectionToRespectUserSelectAll(innerNode, newSelection), ParagraphGranularity);
266 }
267
268 bool SelectionController::handleMousePressEventSingleClick(const MouseEventWithH itTestResults& event)
269 {
270 TRACE_EVENT0("blink", "SelectionController::handleMousePressEventSingleClick ");
271
272 m_frame->document()->updateLayoutIgnorePendingStylesheets();
273 Node* innerNode = event.innerNode();
274 if (!(innerNode && innerNode->layoutObject() && m_mouseDownMayStartSelect))
275 return false;
276
277 // Extend the selection if the Shift key is down, unless the click is in a l ink.
278 bool extendSelection = event.event().shiftKey() && !event.isOverLink();
279
280 // Don't restart the selection when the mouse is pressed on an
281 // existing selection so we can allow for text dragging.
282 if (FrameView* view = m_frame->view()) {
283 LayoutPoint vPoint = view->rootFrameToContents(event.event().position()) ;
284 if (!extendSelection && selection().contains(vPoint)) {
285 m_singleClickInSelection = true;
286 return false;
287 }
288 }
289
290 VisiblePosition visiblePos(innerNode->layoutObject()->positionForPoint(event .localPoint()));
291 if (visiblePos.isNull())
292 visiblePos = VisiblePosition(firstPositionInOrBeforeNode(innerNode), DOW NSTREAM);
293 Position pos = visiblePos.deepEquivalent();
294
295 VisibleSelection newSelection = selection().selection();
296 TextGranularity granularity = CharacterGranularity;
297
298 if (extendSelection && newSelection.isCaretOrRange()) {
299 VisibleSelection selectionInUserSelectAll(expandSelectionToRespectUserSe lectAll(innerNode, VisibleSelection(VisiblePosition(pos))));
300 if (selectionInUserSelectAll.isRange()) {
301 if (comparePositions(selectionInUserSelectAll.start(), newSelection. start()) < 0)
302 pos = selectionInUserSelectAll.start();
303 else if (comparePositions(newSelection.end(), selectionInUserSelectA ll.end()) < 0)
304 pos = selectionInUserSelectAll.end();
305 }
306
307 if (!m_frame->editor().behavior().shouldConsiderSelectionAsDirectional() ) {
308 if (pos.isNotNull()) {
309 // See <rdar://problem/3668157> REGRESSION (Mail): shift-click d eselects when selection
310 // was created right-to-left
311 Position start = newSelection.start();
312 Position end = newSelection.end();
313 int distanceToStart = textDistance(start, pos);
314 int distanceToEnd = textDistance(pos, end);
315 if (distanceToStart <= distanceToEnd)
316 newSelection = VisibleSelection(end, pos);
317 else
318 newSelection = VisibleSelection(start, pos);
319 }
320 } else {
321 newSelection.setExtent(pos);
322 }
323
324 if (selection().granularity() != CharacterGranularity) {
325 granularity = selection().granularity();
326 newSelection.expandUsingGranularity(selection().granularity());
327 }
328 } else if (m_selectionState != SelectionState::ExtendedSelection) {
329 newSelection = expandSelectionToRespectUserSelectAll(innerNode, VisibleS election(visiblePos));
330 }
331
332 // Updating the selection is considered side-effect of the event and so it d oesn't impact the handled state.
333 updateSelectionForMouseDownDispatchingSelectStart(innerNode, newSelection, g ranularity);
334 return false;
335 }
336
337 void SelectionController::handleMouseDraggedEvent(const MouseEventWithHitTestRes ults& event, const IntPoint& mouseDownPos, const LayoutPoint& dragStartPos, Node * mousePressNode, const IntPoint& lastKnownMousePosition)
338 {
339 if (m_selectionState != SelectionState::ExtendedSelection) {
340 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active );
341 HitTestResult result(request, mouseDownPos);
342 m_frame->document()->layoutView()->hitTest(result);
343
344 updateSelectionForMouseDrag(result, mousePressNode, dragStartPos, lastKn ownMousePosition);
345 }
346 updateSelectionForMouseDrag(event.hitTestResult(), mousePressNode, dragStart Pos, lastKnownMousePosition);
347 }
348
349 bool SelectionController::handleMouseReleaseEvent(const MouseEventWithHitTestRes ults& event, const LayoutPoint& dragStartPos)
350 {
351 bool handled = false;
352 m_mouseDownMayStartSelect = false;
353 // Clear the selection if the mouse didn't move after the last mouse
354 // press and it's not a context menu click. We do this so when clicking
355 // on the selection, the selection goes away. However, if we are
356 // editing, place the caret.
357 if (m_singleClickInSelection && m_selectionState != SelectionState::Extended Selection
358 && dragStartPos == event.event().position()
359 && selection().isRange()
360 && event.event().button() != RightButton) {
361 VisibleSelection newSelection;
362 Node* node = event.innerNode();
363 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBr owsingEnabled();
364 if (node && node->layoutObject() && (caretBrowsing || node->hasEditableS tyle())) {
365 VisiblePosition pos = VisiblePosition(node->layoutObject()->position ForPoint(event.localPoint()));
366 newSelection = VisibleSelection(pos);
367 }
368
369 setSelectionIfNeeded(selection(), newSelection);
370
371 handled = true;
372 }
373
374 selection().notifyLayoutObjectOfSelectionChange(UserTriggered);
375
376 selection().selectFrameElementInParentIfFullySelected();
377
378 if (event.event().button() == MiddleButton && !event.isOverLink()) {
379 // Ignore handled, since we want to paste to where the caret was placed anyway.
380 handled = handlePasteGlobalSelection(event.event()) || handled;
381 }
382
383 return handled;
384 }
385
386 bool SelectionController::handlePasteGlobalSelection(const PlatformMouseEvent& m ouseEvent)
387 {
388 // If the event was a middle click, attempt to copy global selection in afte r
389 // the newly set caret position.
390 //
391 // This code is called from either the mouse up or mouse down handling. Ther e
392 // is some debate about when the global selection is pasted:
393 // xterm: pastes on up.
394 // GTK: pastes on down.
395 // Qt: pastes on up.
396 // Firefox: pastes on up.
397 // Chromium: pastes on up.
398 //
399 // There is something of a webcompat angle to this well, as highlighted by
400 // crbug.com/14608. Pages can clear text boxes 'onclick' and, if we paste on
401 // down then the text is pasted just before the onclick handler runs and
402 // clears the text box. So it's important this happens after the event
403 // handlers have been fired.
404 if (mouseEvent.type() != PlatformEvent::MouseReleased)
405 return false;
406
407 if (!m_frame->page())
408 return false;
409 Frame* focusFrame = m_frame->page()->focusController().focusedOrMainFrame();
410 // Do not paste here if the focus was moved somewhere else.
411 if (m_frame == focusFrame && m_frame->editor().behavior().supportsGlobalSele ction())
412 return m_frame->editor().command("PasteGlobalSelection").execute();
413
414 return false;
415 }
416
417 bool SelectionController::handleGestureLongPress(const PlatformGestureEvent& ges tureEvent, const HitTestResult& hitTestResult)
418 {
419 #if OS(ANDROID)
420 bool shouldLongPressSelectWord = true;
421 #else
422 bool shouldLongPressSelectWord = m_frame->settings() && m_frame->settings()- >touchEditingEnabled();
423 #endif
424 if (!shouldLongPressSelectWord)
425 return false;
426
427 Node* innerNode = hitTestResult.innerNode();
428 if (!hitTestResult.isLiveLink() && innerNode && (innerNode->isContentEditabl e() || innerNode->isTextNode()
429 #if OS(ANDROID)
430 || innerNode->canStartSelection()
431 #endif
432 )) {
433 selectClosestWordFromHitTestResult(hitTestResult, AppendTrailingWhitespa ce::DontAppend);
434 if (m_frame->selection().isRange()) {
435 Page* page = m_frame->page();
436 if (page)
437 page->focusController().focusDocumentView(m_frame);
438
439 return true;
440 }
441 }
442 return false;
443 }
444
445 void SelectionController::updateSelectionForMouseDrag(Node* mousePressNode, cons t LayoutPoint& dragStartPos, const IntPoint& lastKnownMousePosition)
446 {
447 FrameView* view = m_frame->view();
448 if (!view)
449 return;
450 LayoutView* layoutObject = m_frame->contentLayoutObject();
451 if (!layoutObject)
452 return;
453
454 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | H itTestRequest::Move);
455 HitTestResult result(request, view->rootFrameToContents(lastKnownMousePositi on));
456 layoutObject->hitTest(result);
457 updateSelectionForMouseDrag(result, mousePressNode, dragStartPos, lastKnownM ousePosition);
458 }
459
460 void SelectionController::updateSelectionForMouseDrag(const HitTestResult& hitTe stResult, Node* mousePressNode, const LayoutPoint& dragStartPos, const IntPoint& lastKnownMousePosition)
461 {
462 if (!m_mouseDownMayStartSelect)
463 return;
464
465 Node* target = hitTestResult.innerNode();
466 if (!target)
467 return;
468
469 VisiblePosition targetPosition = selection().selection().visiblePositionResp ectingEditingBoundary(hitTestResult.localPoint(), target);
470 // Don't modify the selection if we're not on a node.
471 if (targetPosition.isNull())
472 return;
473
474 // Restart the selection if this is the first mouse move. This work is usual ly
475 // done in handleMousePressEvent, but not if the mouse press was on an exist ing selection.
476 VisibleSelection newSelection = selection().selection();
477
478 // Special case to limit selection to the containing block for SVG text.
479 // FIXME: Isn't there a better non-SVG-specific way to do this?
480 if (Node* selectionBaseNode = newSelection.base().deprecatedNode()) {
481 if (LayoutObject* selectionBaseLayoutObject = selectionBaseNode->layoutO bject()) {
482 if (selectionBaseLayoutObject->isSVGText()) {
483 if (target->layoutObject()->containingBlock() != selectionBaseLa youtObject->containingBlock())
484 return;
485 }
486 }
487 }
488
489 if (m_selectionState == SelectionState::HaveNotStartedSelection && !dispatch SelectStart(target))
490 return;
491
492 if (m_selectionState != SelectionState::ExtendedSelection) {
493 // Always extend selection here because it's caused by a mouse drag
494 m_selectionState = SelectionState::ExtendedSelection;
495 newSelection = VisibleSelection(targetPosition);
496 }
497
498 if (RuntimeEnabledFeatures::userSelectAllEnabled()) {
499 Node* rootUserSelectAllForMousePressNode = Position::rootUserSelectAllFo rNode(mousePressNode);
500 if (rootUserSelectAllForMousePressNode && rootUserSelectAllForMousePress Node == Position::rootUserSelectAllForNode(target)) {
501 newSelection.setBase(positionBeforeNode(rootUserSelectAllForMousePre ssNode).upstream(CanCrossEditingBoundary));
502 newSelection.setExtent(positionAfterNode(rootUserSelectAllForMousePr essNode).downstream(CanCrossEditingBoundary));
503 } else {
504 // Reset base for user select all when base is inside user-select-al l area and extent < base.
505 if (rootUserSelectAllForMousePressNode && comparePositions(target->l ayoutObject()->positionForPoint(hitTestResult.localPoint()), mousePressNode->lay outObject()->positionForPoint(dragStartPos)) < 0)
506 newSelection.setBase(positionAfterNode(rootUserSelectAllForMouse PressNode).downstream(CanCrossEditingBoundary));
507
508 Node* rootUserSelectAllForTarget = Position::rootUserSelectAllForNod e(target);
509 if (rootUserSelectAllForTarget && mousePressNode->layoutObject() && comparePositions(target->layoutObject()->positionForPoint(hitTestResult.localPoi nt()), mousePressNode->layoutObject()->positionForPoint(dragStartPos)) < 0)
510 newSelection.setExtent(positionBeforeNode(rootUserSelectAllForTa rget).upstream(CanCrossEditingBoundary));
511 else if (rootUserSelectAllForTarget && mousePressNode->layoutObject( ))
512 newSelection.setExtent(positionAfterNode(rootUserSelectAllForTar get).downstream(CanCrossEditingBoundary));
513 else
514 newSelection.setExtent(targetPosition);
515 }
516 } else {
517 newSelection.setExtent(targetPosition);
518 }
519
520 if (selection().granularity() != CharacterGranularity)
521 newSelection.expandUsingGranularity(selection().granularity());
522
523 selection().setNonDirectionalSelectionIfNeeded(newSelection, selection().gra nularity(),
524 FrameSelection::AdjustEndpointsAtBidiBoundary);
525 }
526
527
528 void SelectionController::prepareForContextMenu(const MouseEventWithHitTestResul ts& mev, const LayoutPoint& position)
529 {
530 if (selection().contains(position)
531 || mev.scrollbar()
532 // FIXME: In the editable case, word selection sometimes selects content that isn't underneath the mouse.
533 // If the selection is non-editable, we do word selection to make it eas ier to use the contextual menu items
534 // available for text selections. But only if we're above text.
535 || !(selection().isContentEditable() || (mev.innerNode() && mev.innerNod e()->isTextNode())))
536 return;
537
538 m_mouseDownMayStartSelect = true; // context menu events are always allowed to perform a selection
539
540 if (mev.hitTestResult().isMisspelled()) {
541 selectClosestMisspellingFromMouseEvent(mev);
542 return;
543 }
544
545 if (m_frame->editor().behavior().shouldSelectOnContextualMenuClick())
546 selectClosestWordOrLinkFromMouseEvent(mev);
547 }
548
549 void SelectionController::preparePassMousePressEventToSubframe(MouseEventWithHit TestResults& mev)
550 {
551 // If we're clicking into a frame that is selected, the frame will appear
552 // greyed out even though we're clicking on the selection. This looks
553 // really strange (having the whole frame be greyed out), so we deselect the
554 // selection.
555 IntPoint p = m_frame->view()->rootFrameToContents(mev.event().position());
556 if (!selection().contains(p))
557 return;
558
559 VisiblePosition visiblePos(
560 mev.innerNode()->layoutObject()->positionForPoint(mev.localPoint()));
561 VisibleSelection newSelection(visiblePos);
562 selection().setSelection(newSelection);
563 }
564
565 void SelectionController::initializeSelectionState()
566 {
567 m_selectionState = SelectionState::HaveNotStartedSelection;
568 }
569
570 void SelectionController::setMouseDownMayStartSelect(bool mayStartSelect)
571 {
572 m_mouseDownMayStartSelect = mayStartSelect;
573 }
574
575 bool SelectionController::mouseDownMayStartSelect() const
576 {
577 return m_mouseDownMayStartSelect;
578 }
579
580 bool SelectionController::singleClickInSelection() const
581 {
582 return m_singleClickInSelection;
583 }
584
585 FrameSelection& SelectionController::selection() const
586 {
587 return m_frame->selection();
588 }
589
590 } // namespace blink
OLDNEW
« no previous file with comments | « Source/core/editing/SelectionController.h ('k') | Source/core/html/HTMLLabelElement.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698