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

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

Powered by Google App Engine
This is Rietveld 408576698