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