1/* 2 * Copyright (C) 2004, 2008, 2009, 2010, 2014 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "FrameSelection.h" 28 29#include "CharacterData.h" 30#include "DeleteSelectionCommand.h" 31#include "Document.h" 32#include "Editor.h" 33#include "EditorClient.h" 34#include "Element.h" 35#include "ElementIterator.h" 36#include "EventHandler.h" 37#include "ExceptionCode.h" 38#include "FloatQuad.h" 39#include "FocusController.h" 40#include "Frame.h" 41#include "FrameTree.h" 42#include "FrameView.h" 43#include "GraphicsContext.h" 44#include "HTMLFormElement.h" 45#include "HTMLFrameElementBase.h" 46#include "HTMLInputElement.h" 47#include "HTMLSelectElement.h" 48#include "HTMLNames.h" 49#include "HitTestRequest.h" 50#include "HitTestResult.h" 51#include "InlineTextBox.h" 52#include "Page.h" 53#include "RenderText.h" 54#include "RenderTextControl.h" 55#include "RenderTheme.h" 56#include "RenderView.h" 57#include "RenderWidget.h" 58#include "RenderedPosition.h" 59#include "Settings.h" 60#include "SpatialNavigation.h" 61#include "StyleProperties.h" 62#include "TypingCommand.h" 63#include "VisibleUnits.h" 64#include "htmlediting.h" 65#include <stdio.h> 66#include <wtf/text/CString.h> 67 68#if PLATFORM(IOS) 69#include "Chrome.h" 70#include "ChromeClient.h" 71#include "Color.h" 72#include "RenderLayer.h" 73#include "RenderObject.h" 74#include "RenderStyle.h" 75#endif 76 77#define EDIT_DEBUG 0 78 79namespace WebCore { 80 81using namespace HTMLNames; 82 83static inline LayoutUnit NoXPosForVerticalArrowNavigation() 84{ 85 return LayoutUnit::min(); 86} 87 88CaretBase::CaretBase(CaretVisibility visibility) 89 : m_caretRectNeedsUpdate(true) 90 , m_caretVisibility(visibility) 91{ 92} 93 94DragCaretController::DragCaretController() 95 : CaretBase(Visible) 96{ 97} 98 99bool DragCaretController::isContentRichlyEditable() const 100{ 101 return isRichlyEditablePosition(m_position.deepEquivalent()); 102} 103 104static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame) 105{ 106 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional(); 107} 108 109FrameSelection::FrameSelection(Frame* frame) 110 : m_frame(frame) 111 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) 112 , m_granularity(CharacterGranularity) 113 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) 114 , m_absCaretBoundsDirty(true) 115 , m_caretPaint(true) 116 , m_isCaretBlinkingSuspended(false) 117 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame) 118 , m_shouldShowBlockCursor(false) 119 , m_pendingSelectionUpdate(false) 120 , m_shouldRevealSelection(false) 121 , m_alwaysAlignCursorOnScrollWhenRevealingSelection(false) 122#if PLATFORM(IOS) 123 , m_updateAppearanceEnabled(false) 124 , m_caretBlinks(true) 125 , m_scrollingSuppressCount(0) 126#endif 127{ 128 if (shouldAlwaysUseDirectionalSelection(m_frame)) 129 m_selection.setIsDirectional(true); 130} 131 132Element* FrameSelection::rootEditableElementOrDocumentElement() const 133{ 134 Element* selectionRoot = m_selection.rootEditableElement(); 135 return selectionRoot ? selectionRoot : m_frame->document()->documentElement(); 136} 137 138void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align) 139{ 140 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), 141 defaultSetSelectionOptions(userTriggered), align); 142} 143 144void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered) 145{ 146 const bool selectionHasDirection = true; 147 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 148} 149 150void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered) 151{ 152 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), defaultSetSelectionOptions(userTriggered)); 153} 154 155void FrameSelection::moveTo(const Range* range) 156{ 157 VisibleSelection selection = range ? VisibleSelection(range->startPosition(), range->endPosition()) : VisibleSelection(); 158 setSelection(selection); 159} 160 161void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered) 162{ 163 const bool selectionHasDirection = true; 164 setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 165} 166 167void FrameSelection::moveWithoutValidationTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus) 168{ 169 VisibleSelection newSelection; 170 newSelection.setWithoutValidation(base, extent); 171 newSelection.setIsDirectional(selectionHasDirection); 172 setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus)); 173} 174 175void DragCaretController::setCaretPosition(const VisiblePosition& position) 176{ 177 if (Node* node = m_position.deepEquivalent().deprecatedNode()) 178 invalidateCaretRect(node); 179 m_position = position; 180 setCaretRectNeedsUpdate(); 181 Document* document = 0; 182 if (Node* node = m_position.deepEquivalent().deprecatedNode()) { 183 invalidateCaretRect(node); 184 document = &node->document(); 185 } 186 if (m_position.isNull() || m_position.isOrphan()) 187 clearCaretRect(); 188 else 189 updateCaretRect(document, m_position); 190} 191 192static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent) 193{ 194 RenderedPosition base(visibleBase); 195 RenderedPosition extent(visibleExtent); 196 197 if (base.isNull() || extent.isNull() || base.isEquivalent(extent)) 198 return; 199 200 if (base.atLeftBoundaryOfBidiRun()) { 201 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight()) 202 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) { 203 visibleBase = base.positionAtLeftBoundaryOfBiDiRun(); 204 return; 205 } 206 return; 207 } 208 209 if (base.atRightBoundaryOfBidiRun()) { 210 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft()) 211 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) { 212 visibleBase = base.positionAtRightBoundaryOfBiDiRun(); 213 return; 214 } 215 return; 216 } 217 218 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) { 219 visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun(); 220 return; 221 } 222 223 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) { 224 visibleExtent = extent.positionAtRightBoundaryOfBiDiRun(); 225 return; 226 } 227} 228 229void FrameSelection::setSelectionByMouseIfDifferent(const VisibleSelection& passedNewSelection, TextGranularity granularity, 230 EndPointsAdjustmentMode endpointsAdjustmentMode) 231{ 232 VisibleSelection newSelection = passedNewSelection; 233 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional(); 234 235 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase(); 236 VisiblePosition newBase = base; 237 VisiblePosition extent = newSelection.visibleExtent(); 238 VisiblePosition newExtent = extent; 239 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary) 240 adjustEndpointsAtBidiBoundary(newBase, newExtent); 241 242 if (newBase != base || newExtent != extent) { 243 m_originalBase = base; 244 newSelection.setBase(newBase); 245 newSelection.setExtent(newExtent); 246 } else if (m_originalBase.isNotNull()) { 247 if (m_selection.base() == newSelection.base()) 248 newSelection.setBase(m_originalBase); 249 m_originalBase.clear(); 250 } 251 252 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional 253 if (m_selection == newSelection || !shouldChangeSelection(newSelection)) 254 return; 255 256 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, AlignCursorOnScrollIfNeeded, granularity); 257} 258 259bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelection& newSelectionPossiblyWithoutDirection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity) 260{ 261 bool closeTyping = options & CloseTyping; 262 bool shouldClearTypingStyle = options & ClearTypingStyle; 263 264 VisibleSelection newSelection = newSelectionPossiblyWithoutDirection; 265 if (shouldAlwaysUseDirectionalSelection(m_frame)) 266 newSelection.setIsDirectional(true); 267 268 if (!m_frame) { 269 m_selection = newSelection; 270 return false; 271 } 272 273 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection 274 // if document->frame() == m_frame we can get into an infinite loop 275 if (Document* newSelectionDocument = newSelection.base().document()) { 276 if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) { 277 if (newSelectionFrame != m_frame && newSelectionDocument != m_frame->document()) { 278 newSelectionFrame->selection().setSelection(newSelection, options, align, granularity); 279 // It's possible that during the above set selection, this FrameSelection has been modified by 280 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since 281 // the frame is about to be destroyed. If this is the case, clear our selection. 282 if (newSelectionFrame->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange()) 283 clear(); 284 return false; 285 } 286 } 287 } 288 289 m_granularity = granularity; 290 291 if (closeTyping) 292 TypingCommand::closeTyping(m_frame); 293 294 if (shouldClearTypingStyle) 295 clearTypingStyle(); 296 297 VisibleSelection oldSelection = m_selection; 298 m_selection = newSelection; 299 300 // Selection offsets should increase when LF is inserted before the caret in InsertLineBreakCommand. See <https://webkit.org/b/56061>. 301 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(newSelection.start())) 302 textControl->selectionChanged(options & FireSelectEvent); 303 304 if (oldSelection == newSelection) 305 return false; 306 307 setCaretRectNeedsUpdate(); 308 309 if (!newSelection.isNone() && !(options & DoNotSetFocus)) 310 setFocusedElementIfNeeded(); 311 312 // Always clear the x position used for vertical arrow navigation. 313 // It will be restored by the vertical arrow navigation code if necessary. 314 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); 315 selectFrameElementInParentIfFullySelected(); 316 m_frame->editor().respondToChangedSelection(oldSelection, options); 317 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false)); 318 319 return true; 320} 321 322void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity) 323{ 324 if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity)) 325 return; 326 327 Document* document = m_frame->document(); 328 if (!document) 329 return; 330 331 m_shouldRevealSelection = options & RevealSelection; 332 m_alwaysAlignCursorOnScrollWhenRevealingSelection = align == AlignCursorOnScrollAlways; 333 334 m_pendingSelectionUpdate = true; 335 336 if (document->hasPendingStyleRecalc()) 337 return; 338 339 FrameView* frameView = document->view(); 340 if (frameView && frameView->layoutPending()) 341 return; 342 343 updateAndRevealSelection(); 344} 345 346static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame) 347{ 348#if ENABLE(TEXT_CARET) 349 frame.document()->updateLayoutIgnorePendingStylesheets(); 350#else 351 frame.document()->updateStyleIfNeeded(); 352#endif 353} 354 355void FrameSelection::setNeedsSelectionUpdate() 356{ 357 m_pendingSelectionUpdate = true; 358 if (RenderView* view = m_frame->contentRenderer()) 359 view->clearSelection(); 360} 361 362void FrameSelection::updateAndRevealSelection() 363{ 364 if (!m_pendingSelectionUpdate) 365 return; 366 367 m_pendingSelectionUpdate = false; 368 369 updateAppearance(); 370 371 if (m_shouldRevealSelection) { 372 ScrollAlignment alignment; 373 374 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) 375 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded; 376 else 377 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded; 378 379 revealSelection(alignment, RevealExtent); 380 } 381 382 notifyAccessibilityForSelectionChange(); 383} 384 385void FrameSelection::updateDataDetectorsForSelection() 386{ 387#if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS) 388 m_frame->editor().scanSelectionForTelephoneNumbers(); 389#endif 390} 391 392static bool removingNodeRemovesPosition(Node* node, const Position& position) 393{ 394 if (!position.anchorNode()) 395 return false; 396 397 if (position.anchorNode() == node) 398 return true; 399 400 if (!node->isElementNode()) 401 return false; 402 403 Element* element = toElement(node); 404 return element->containsIncludingShadowDOM(position.anchorNode()); 405} 406 407static void clearRenderViewSelection(Node& node) 408{ 409 Ref<Document> document(node.document()); 410 document->updateStyleIfNeeded(); 411 if (RenderView* view = document->renderView()) 412 view->clearSelection(); 413} 414 415void DragCaretController::nodeWillBeRemoved(Node* node) 416{ 417 if (!hasCaret() || (node && !node->inDocument())) 418 return; 419 420 if (!removingNodeRemovesPosition(node, m_position.deepEquivalent())) 421 return; 422 423 clearRenderViewSelection(*node); 424 clear(); 425} 426 427void FrameSelection::nodeWillBeRemoved(Node* node) 428{ 429 // There can't be a selection inside a fragment, so if a fragment's node is being removed, 430 // the selection in the document that created the fragment needs no adjustment. 431 if (isNone() || (node && !node->inDocument())) 432 return; 433 434 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()), 435 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end())); 436} 437 438void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved) 439{ 440 bool clearRenderTreeSelection = false; 441 bool clearDOMTreeSelection = false; 442 443 if (startRemoved || endRemoved) { 444 Position start = m_selection.start(); 445 Position end = m_selection.end(); 446 if (startRemoved) 447 updatePositionForNodeRemoval(start, node); 448 if (endRemoved) 449 updatePositionForNodeRemoval(end, node); 450 451 if (start.isNotNull() && end.isNotNull()) { 452 if (m_selection.isBaseFirst()) 453 m_selection.setWithoutValidation(start, end); 454 else 455 m_selection.setWithoutValidation(end, start); 456 } else 457 clearDOMTreeSelection = true; 458 459 clearRenderTreeSelection = true; 460 } else if (baseRemoved || extentRemoved) { 461 // The base and/or extent are about to be removed, but the start and end aren't. 462 // Change the base and extent to the start and end, but don't re-validate the 463 // selection, since doing so could move the start and end into the node 464 // that is about to be removed. 465 if (m_selection.isBaseFirst()) 466 m_selection.setWithoutValidation(m_selection.start(), m_selection.end()); 467 else 468 m_selection.setWithoutValidation(m_selection.end(), m_selection.start()); 469 } else if (RefPtr<Range> range = m_selection.firstRange()) { 470 ExceptionCode ec = 0; 471 Range::CompareResults compareResult = range->compareNode(node, ec); 472 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) { 473 // If we did nothing here, when this node's renderer was destroyed, the rect that it 474 // occupied would be invalidated, but, selection gaps that change as a result of 475 // the removal wouldn't be invalidated. 476 // FIXME: Don't do so much unnecessary invalidation. 477 clearRenderTreeSelection = true; 478 } 479 } 480 481 if (clearRenderTreeSelection) { 482 clearRenderViewSelection(*node); 483 484 // Trigger a selection update so the selection will be set again. 485 if (auto* renderView = node->document().renderView()) { 486 m_pendingSelectionUpdate = true; 487 renderView->setNeedsLayout(); 488 } 489 } 490 491 if (clearDOMTreeSelection) 492 setSelection(VisibleSelection(), DoNotSetFocus); 493} 494 495static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) 496{ 497 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor) 498 return; 499 500 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation 501 ASSERT(position.offsetInContainerNode() >= 0); 502 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode()); 503 // Replacing text can be viewed as a deletion followed by insertion. 504 if (positionOffset >= offset && positionOffset <= offset + oldLength) 505 position.moveToOffset(offset); 506 507 // Adjust the offset if the position is after the end of the deleted contents 508 // (positionOffset > offset + oldLength) to avoid having a stale offset. 509 if (positionOffset > offset + oldLength) 510 position.moveToOffset(positionOffset - oldLength + newLength); 511 512 ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length()); 513} 514 515void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) 516{ 517 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062. 518 if (isNone() || !node || !node->inDocument()) 519 return; 520 521 Position base = m_selection.base(); 522 Position extent = m_selection.extent(); 523 Position start = m_selection.start(); 524 Position end = m_selection.end(); 525 updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength); 526 updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength); 527 updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength); 528 updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength); 529 530 if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) { 531 VisibleSelection newSelection; 532 if (base != extent) 533 newSelection.setWithoutValidation(base, extent); 534 else if (m_selection.isDirectional() && !m_selection.isBaseFirst()) 535 newSelection.setWithoutValidation(end, start); 536 else 537 newSelection.setWithoutValidation(start, end); 538 539 setSelection(newSelection, DoNotSetFocus); 540 } 541} 542 543TextDirection FrameSelection::directionOfEnclosingBlock() 544{ 545 return WebCore::directionOfEnclosingBlock(m_selection.extent()); 546} 547 548TextDirection FrameSelection::directionOfSelection() 549{ 550 InlineBox* startBox = 0; 551 InlineBox* endBox = 0; 552 int unusedOffset; 553 // Cache the VisiblePositions because visibleStart() and visibleEnd() 554 // can cause layout, which has the potential to invalidate lineboxes. 555 VisiblePosition startPosition = m_selection.visibleStart(); 556 VisiblePosition endPosition = m_selection.visibleEnd(); 557 if (startPosition.isNotNull()) 558 startPosition.getInlineBoxAndOffset(startBox, unusedOffset); 559 if (endPosition.isNotNull()) 560 endPosition.getInlineBoxAndOffset(endBox, unusedOffset); 561 if (startBox && endBox && startBox->direction() == endBox->direction()) 562 return startBox->direction(); 563 564 return directionOfEnclosingBlock(); 565} 566 567void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction) 568{ 569 if (alter != AlterationExtend) 570 return; 571 572 Position start = m_selection.start(); 573 Position end = m_selection.end(); 574 575 bool baseIsStart = true; 576 577 if (m_selection.isDirectional()) { 578 // Make base and extent match start and end so we extend the user-visible selection. 579 // This only matters for cases where base and extend point to different positions than 580 // start and end (e.g. after a double-click to select a word). 581 if (m_selection.isBaseFirst()) 582 baseIsStart = true; 583 else 584 baseIsStart = false; 585 } else { 586 switch (direction) { 587 case DirectionRight: 588 if (directionOfSelection() == LTR) 589 baseIsStart = true; 590 else 591 baseIsStart = false; 592 break; 593 case DirectionForward: 594 baseIsStart = true; 595 break; 596 case DirectionLeft: 597 if (directionOfSelection() == LTR) 598 baseIsStart = false; 599 else 600 baseIsStart = true; 601 break; 602 case DirectionBackward: 603 baseIsStart = false; 604 break; 605 } 606 } 607 if (baseIsStart) { 608 m_selection.setBase(start); 609 m_selection.setExtent(end); 610 } else { 611 m_selection.setBase(end); 612 m_selection.setExtent(start); 613 } 614} 615 616VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const 617{ 618 if (m_frame && m_frame->settings().editingBehaviorType() == EditingMacBehavior) 619 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd(); 620 // Linux and Windows always extend selections from the extent endpoint. 621 // FIXME: VisibleSelection should be fixed to ensure as an invariant that 622 // base/extent always point to the same nodes as start/end, but which points 623 // to which depends on the value of isBaseFirst. Then this can be changed 624 // to just return m_sel.extent(). 625 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart(); 626} 627 628VisiblePosition FrameSelection::startForPlatform() const 629{ 630 return positionForPlatform(true); 631} 632 633VisiblePosition FrameSelection::endForPlatform() const 634{ 635 return positionForPlatform(false); 636} 637 638VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition) 639{ 640 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition); 641 642 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) { 643 // In order to skip spaces when moving right, we advance one 644 // word further and then move one word back. Given the 645 // semantics of previousWordPosition() this will put us at the 646 // beginning of the word following. 647 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); 648 if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord) 649 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); 650 651 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition)); 652 if (movingBackwardsMovedPositionToStartOfCurrentWord) 653 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; 654 } 655 return positionAfterCurrentWord; 656} 657 658#if ENABLE(USERSELECT_ALL) 659static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward) 660{ 661 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode())) 662 pos = isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary); 663} 664#endif 665 666VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity) 667{ 668 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 669 670 // The difference between modifyExtendingRight and modifyExtendingForward is: 671 // modifyExtendingForward always extends forward logically. 672 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word, 673 // it extends forward logically if the enclosing block is LTR direction, 674 // but it extends backward logically if the enclosing block is RTL direction. 675 switch (granularity) { 676 case CharacterGranularity: 677 if (directionOfEnclosingBlock() == LTR) 678 pos = pos.next(CannotCrossEditingBoundary); 679 else 680 pos = pos.previous(CannotCrossEditingBoundary); 681 break; 682 case WordGranularity: 683 if (directionOfEnclosingBlock() == LTR) 684 pos = nextWordPositionForPlatform(pos); 685 else 686 pos = previousWordPosition(pos); 687 break; 688 case LineBoundary: 689 if (directionOfEnclosingBlock() == LTR) 690 pos = modifyExtendingForward(granularity); 691 else 692 pos = modifyExtendingBackward(granularity); 693 break; 694 case SentenceGranularity: 695 case LineGranularity: 696 case ParagraphGranularity: 697 case SentenceBoundary: 698 case ParagraphBoundary: 699 case DocumentBoundary: 700 // FIXME: implement all of the above? 701 pos = modifyExtendingForward(granularity); 702 break; 703#if PLATFORM(IOS) 704 case DocumentGranularity: 705 ASSERT_NOT_REACHED(); 706 break; 707#endif 708 } 709#if ENABLE(USERSELECT_ALL) 710 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); 711#endif 712 return pos; 713} 714 715VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity) 716{ 717 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 718 switch (granularity) { 719 case CharacterGranularity: 720 pos = pos.next(CannotCrossEditingBoundary); 721 break; 722 case WordGranularity: 723 pos = nextWordPositionForPlatform(pos); 724 break; 725 case SentenceGranularity: 726 pos = nextSentencePosition(pos); 727 break; 728 case LineGranularity: 729 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 730 break; 731 case ParagraphGranularity: 732 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 733 break; 734#if PLATFORM(IOS) 735 case DocumentGranularity: 736 ASSERT_NOT_REACHED(); 737 break; 738#endif 739 case SentenceBoundary: 740 pos = endOfSentence(endForPlatform()); 741 break; 742 case LineBoundary: 743 pos = logicalEndOfLine(endForPlatform()); 744 break; 745 case ParagraphBoundary: 746 pos = endOfParagraph(endForPlatform()); 747 break; 748 case DocumentBoundary: 749 pos = endForPlatform(); 750 if (isEditablePosition(pos.deepEquivalent())) 751 pos = endOfEditableContent(pos); 752 else 753 pos = endOfDocument(pos); 754 break; 755 } 756#if ENABLE(USERSELECT_ALL) 757 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); 758#endif 759 return pos; 760} 761 762VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity) 763{ 764 VisiblePosition pos; 765 switch (granularity) { 766 case CharacterGranularity: 767 if (isRange()) { 768 if (directionOfSelection() == LTR) 769 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 770 else 771 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 772 } else 773 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true); 774 break; 775 case WordGranularity: { 776 // Visual word movement relies on isWordTextBreak which is not implemented in WinCE and QT. 777 // https://bugs.webkit.org/show_bug.cgi?id=81136. 778 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); 779 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); 780 break; 781 } 782 case SentenceGranularity: 783 case LineGranularity: 784 case ParagraphGranularity: 785 case SentenceBoundary: 786 case ParagraphBoundary: 787 case DocumentBoundary: 788 // FIXME: Implement all of the above. 789 pos = modifyMovingForward(granularity); 790 break; 791 case LineBoundary: 792 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); 793 break; 794#if PLATFORM(IOS) 795 case DocumentGranularity: 796 ASSERT_NOT_REACHED(); 797 break; 798#endif 799 } 800 return pos; 801} 802 803VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity) 804{ 805 VisiblePosition pos; 806 // FIXME: Stay in editable content for the less common granularities. 807 switch (granularity) { 808 case CharacterGranularity: 809 if (isRange()) 810 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 811 else 812 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary); 813 break; 814 case WordGranularity: 815 pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity())); 816 break; 817 case SentenceGranularity: 818 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 819 break; 820 case LineGranularity: { 821 // down-arrowing from a range selection that ends at the start of a line needs 822 // to leave the selection at that line start (no need to call nextLinePosition!) 823 pos = endForPlatform(); 824 if (!isRange() || !isStartOfLine(pos)) 825 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START)); 826 break; 827 } 828 case ParagraphGranularity: 829 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 830 break; 831#if PLATFORM(IOS) 832 case DocumentGranularity: 833 ASSERT_NOT_REACHED(); 834 break; 835#endif 836 case SentenceBoundary: 837 pos = endOfSentence(endForPlatform()); 838 break; 839 case LineBoundary: 840 pos = logicalEndOfLine(endForPlatform()); 841 break; 842 case ParagraphBoundary: 843 pos = endOfParagraph(endForPlatform()); 844 break; 845 case DocumentBoundary: 846 pos = endForPlatform(); 847 if (isEditablePosition(pos.deepEquivalent())) 848 pos = endOfEditableContent(pos); 849 else 850 pos = endOfDocument(pos); 851 break; 852 } 853 return pos; 854} 855 856VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity) 857{ 858 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 859 860 // The difference between modifyExtendingLeft and modifyExtendingBackward is: 861 // modifyExtendingBackward always extends backward logically. 862 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word, 863 // it extends backward logically if the enclosing block is LTR direction, 864 // but it extends forward logically if the enclosing block is RTL direction. 865 switch (granularity) { 866 case CharacterGranularity: 867 if (directionOfEnclosingBlock() == LTR) 868 pos = pos.previous(CannotCrossEditingBoundary); 869 else 870 pos = pos.next(CannotCrossEditingBoundary); 871 break; 872 case WordGranularity: 873 if (directionOfEnclosingBlock() == LTR) 874 pos = previousWordPosition(pos); 875 else 876 pos = nextWordPositionForPlatform(pos); 877 break; 878 case LineBoundary: 879 if (directionOfEnclosingBlock() == LTR) 880 pos = modifyExtendingBackward(granularity); 881 else 882 pos = modifyExtendingForward(granularity); 883 break; 884 case SentenceGranularity: 885 case LineGranularity: 886 case ParagraphGranularity: 887 case SentenceBoundary: 888 case ParagraphBoundary: 889 case DocumentBoundary: 890 pos = modifyExtendingBackward(granularity); 891 break; 892#if PLATFORM(IOS) 893 case DocumentGranularity: 894 ASSERT_NOT_REACHED(); 895 break; 896#endif 897 } 898#if ENABLE(USERSELECT_ALL) 899 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); 900#endif 901 return pos; 902} 903 904VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity) 905{ 906 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 907 908 // Extending a selection backward by word or character from just after a table selects 909 // the table. This "makes sense" from the user perspective, esp. when deleting. 910 // It was done here instead of in VisiblePosition because we want VPs to iterate 911 // over everything. 912 switch (granularity) { 913 case CharacterGranularity: 914 pos = pos.previous(CannotCrossEditingBoundary); 915 break; 916 case WordGranularity: 917 pos = previousWordPosition(pos); 918 break; 919 case SentenceGranularity: 920 pos = previousSentencePosition(pos); 921 break; 922 case LineGranularity: 923 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 924 break; 925 case ParagraphGranularity: 926 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 927 break; 928 case SentenceBoundary: 929 pos = startOfSentence(startForPlatform()); 930 break; 931 case LineBoundary: 932 pos = logicalStartOfLine(startForPlatform()); 933 break; 934 case ParagraphBoundary: 935 pos = startOfParagraph(startForPlatform()); 936 break; 937 case DocumentBoundary: 938 pos = startForPlatform(); 939 if (isEditablePosition(pos.deepEquivalent())) 940 pos = startOfEditableContent(pos); 941 else 942 pos = startOfDocument(pos); 943 break; 944#if PLATFORM(IOS) 945 case DocumentGranularity: 946 ASSERT_NOT_REACHED(); 947 break; 948#endif 949 } 950#if ENABLE(USERSELECT_ALL) 951 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); 952#endif 953 return pos; 954} 955 956VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity) 957{ 958 VisiblePosition pos; 959 switch (granularity) { 960 case CharacterGranularity: 961 if (isRange()) 962 if (directionOfSelection() == LTR) 963 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 964 else 965 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 966 else 967 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true); 968 break; 969 case WordGranularity: { 970 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); 971 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); 972 break; 973 } 974 case SentenceGranularity: 975 case LineGranularity: 976 case ParagraphGranularity: 977 case SentenceBoundary: 978 case ParagraphBoundary: 979 case DocumentBoundary: 980 // FIXME: Implement all of the above. 981 pos = modifyMovingBackward(granularity); 982 break; 983 case LineBoundary: 984 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); 985 break; 986#if PLATFORM(IOS) 987 case DocumentGranularity: 988 ASSERT_NOT_REACHED(); 989 break; 990#endif 991 } 992 return pos; 993} 994 995VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity) 996{ 997 VisiblePosition pos; 998 switch (granularity) { 999 case CharacterGranularity: 1000 if (isRange()) 1001 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 1002 else 1003 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary); 1004 break; 1005 case WordGranularity: 1006 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 1007 break; 1008 case SentenceGranularity: 1009 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 1010 break; 1011 case LineGranularity: 1012 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 1013 break; 1014 case ParagraphGranularity: 1015 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 1016 break; 1017 case SentenceBoundary: 1018 pos = startOfSentence(startForPlatform()); 1019 break; 1020 case LineBoundary: 1021 pos = logicalStartOfLine(startForPlatform()); 1022 break; 1023 case ParagraphBoundary: 1024 pos = startOfParagraph(startForPlatform()); 1025 break; 1026 case DocumentBoundary: 1027 pos = startForPlatform(); 1028 if (isEditablePosition(pos.deepEquivalent())) 1029 pos = startOfEditableContent(pos); 1030 else 1031 pos = startOfDocument(pos); 1032 break; 1033#if PLATFORM(IOS) 1034 case DocumentGranularity: 1035 ASSERT_NOT_REACHED(); 1036 break; 1037#endif 1038 } 1039 return pos; 1040} 1041 1042static bool isBoundary(TextGranularity granularity) 1043{ 1044 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary; 1045} 1046 1047bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered) 1048{ 1049 if (userTriggered == UserTriggered) { 1050 FrameSelection trialFrameSelection; 1051 trialFrameSelection.setSelection(m_selection); 1052 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered); 1053 1054 bool change = shouldChangeSelection(trialFrameSelection.selection()); 1055 if (!change) 1056 return false; 1057 1058 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart()) 1059 return false; 1060 } 1061 1062 willBeModified(alter, direction); 1063 1064 bool wasRange = m_selection.isRange(); 1065 Position originalStartPosition = m_selection.start(); 1066 VisiblePosition position; 1067 switch (direction) { 1068 case DirectionRight: 1069 if (alter == AlterationMove) 1070 position = modifyMovingRight(granularity); 1071 else 1072 position = modifyExtendingRight(granularity); 1073 break; 1074 case DirectionForward: 1075 if (alter == AlterationExtend) 1076 position = modifyExtendingForward(granularity); 1077 else 1078 position = modifyMovingForward(granularity); 1079 break; 1080 case DirectionLeft: 1081 if (alter == AlterationMove) 1082 position = modifyMovingLeft(granularity); 1083 else 1084 position = modifyExtendingLeft(granularity); 1085 break; 1086 case DirectionBackward: 1087 if (alter == AlterationExtend) 1088 position = modifyExtendingBackward(granularity); 1089 else 1090 position = modifyMovingBackward(granularity); 1091 break; 1092 } 1093 1094 if (position.isNull()) 1095 return false; 1096 1097 if (isSpatialNavigationEnabled(m_frame)) 1098 if (!wasRange && alter == AlterationMove && position == originalStartPosition) 1099 return false; 1100 1101 // Some of the above operations set an xPosForVerticalArrowNavigation. 1102 // Setting a selection will clear it, so save it to possibly restore later. 1103 // Note: the START position type is arbitrary because it is unused, it would be 1104 // the requested position type if there were no xPosForVerticalArrowNavigation set. 1105 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START); 1106 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); 1107 1108 switch (alter) { 1109 case AlterationMove: 1110 moveTo(position, userTriggered); 1111 break; 1112 case AlterationExtend: 1113 1114 if (!m_selection.isCaret() 1115 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity) 1116 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) { 1117 // Don't let the selection go across the base position directly. Needed to match mac 1118 // behavior when, for instance, word-selecting backwards starting with the caret in 1119 // the middle of a word and then word-selecting forward, leaving the caret in the 1120 // same place where it was, instead of directly selecting to the end of the word. 1121 VisibleSelection newSelection = m_selection; 1122 newSelection.setExtent(position); 1123 if (m_selection.isBaseFirst() != newSelection.isBaseFirst()) 1124 position = m_selection.base(); 1125 } 1126 1127 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the 1128 // base in place and moving the extent. Matches NSTextView. 1129 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) 1130 setExtent(position, userTriggered); 1131 else { 1132 TextDirection textDirection = directionOfEnclosingBlock(); 1133 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) 1134 setEnd(position, userTriggered); 1135 else 1136 setStart(position, userTriggered); 1137 } 1138 break; 1139 } 1140 1141 if (granularity == LineGranularity || granularity == ParagraphGranularity) 1142 m_xPosForVerticalArrowNavigation = x; 1143 1144 if (userTriggered == UserTriggered) 1145 m_granularity = CharacterGranularity; 1146 1147 setCaretRectNeedsUpdate(); 1148 1149 return true; 1150} 1151 1152// FIXME: Maybe baseline would be better? 1153static bool absoluteCaretY(const VisiblePosition &c, int &y) 1154{ 1155 IntRect rect = c.absoluteCaretBounds(); 1156 if (rect.isEmpty()) 1157 return false; 1158 y = rect.y() + rect.height() / 2; 1159 return true; 1160} 1161 1162bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align) 1163{ 1164 if (!verticalDistance) 1165 return false; 1166 1167 if (userTriggered == UserTriggered) { 1168 FrameSelection trialFrameSelection; 1169 trialFrameSelection.setSelection(m_selection); 1170 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered); 1171 1172 bool change = shouldChangeSelection(trialFrameSelection.selection()); 1173 if (!change) 1174 return false; 1175 } 1176 1177 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward); 1178 1179 VisiblePosition pos; 1180 LayoutUnit xPos = 0; 1181 switch (alter) { 1182 case AlterationMove: 1183 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity()); 1184 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END); 1185 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM); 1186 break; 1187 case AlterationExtend: 1188 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()); 1189 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT); 1190 m_selection.setAffinity(DOWNSTREAM); 1191 break; 1192 } 1193 1194 int startY; 1195 if (!absoluteCaretY(pos, startY)) 1196 return false; 1197 if (direction == DirectionUp) 1198 startY = -startY; 1199 int lastY = startY; 1200 1201 VisiblePosition result; 1202 VisiblePosition next; 1203 for (VisiblePosition p = pos; ; p = next) { 1204 if (direction == DirectionUp) 1205 next = previousLinePosition(p, xPos); 1206 else 1207 next = nextLinePosition(p, xPos); 1208 1209 if (next.isNull() || next == p) 1210 break; 1211 int nextY; 1212 if (!absoluteCaretY(next, nextY)) 1213 break; 1214 if (direction == DirectionUp) 1215 nextY = -nextY; 1216 if (nextY - startY > static_cast<int>(verticalDistance)) 1217 break; 1218 if (nextY >= lastY) { 1219 lastY = nextY; 1220 result = next; 1221 } 1222 } 1223 1224 if (result.isNull()) 1225 return false; 1226 1227 switch (alter) { 1228 case AlterationMove: 1229 moveTo(result, userTriggered, align); 1230 break; 1231 case AlterationExtend: 1232 setExtent(result, userTriggered); 1233 break; 1234 } 1235 1236 if (userTriggered == UserTriggered) 1237 m_granularity = CharacterGranularity; 1238 1239 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); 1240 1241 return true; 1242} 1243 1244LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type) 1245{ 1246 LayoutUnit x = 0; 1247 1248 if (isNone()) 1249 return x; 1250 1251 Position pos; 1252 switch (type) { 1253 case START: 1254 pos = m_selection.start(); 1255 break; 1256 case END: 1257 pos = m_selection.end(); 1258 break; 1259 case BASE: 1260 pos = m_selection.base(); 1261 break; 1262 case EXTENT: 1263 pos = m_selection.extent(); 1264 break; 1265 } 1266 1267 Frame* frame = pos.anchorNode()->document().frame(); 1268 if (!frame) 1269 return x; 1270 1271 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) { 1272 VisiblePosition visiblePosition(pos, m_selection.affinity()); 1273 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden 1274 // after the selection is created and before this function is called. 1275 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0; 1276 m_xPosForVerticalArrowNavigation = x; 1277 } else 1278 x = m_xPosForVerticalArrowNavigation; 1279 1280 return x; 1281} 1282 1283void FrameSelection::clear() 1284{ 1285 m_granularity = CharacterGranularity; 1286 setSelection(VisibleSelection()); 1287} 1288 1289void FrameSelection::prepareForDestruction() 1290{ 1291 m_granularity = CharacterGranularity; 1292 1293#if ENABLE(TEXT_CARET) 1294 m_caretBlinkTimer.stop(); 1295#endif 1296 1297 RenderView* view = m_frame->contentRenderer(); 1298 if (view) 1299 view->clearSelection(); 1300 1301 setSelectionWithoutUpdatingAppearance(VisibleSelection(), defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, CharacterGranularity); 1302 m_previousCaretNode.clear(); 1303} 1304 1305void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger) 1306{ 1307 if (m_selection.isBaseFirst()) 1308 setBase(pos, trigger); 1309 else 1310 setExtent(pos, trigger); 1311} 1312 1313void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger) 1314{ 1315 if (m_selection.isBaseFirst()) 1316 setExtent(pos, trigger); 1317 else 1318 setBase(pos, trigger); 1319} 1320 1321void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered) 1322{ 1323 const bool selectionHasDirection = true; 1324 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 1325} 1326 1327void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered) 1328{ 1329 const bool selectionHasDirection = true; 1330 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 1331} 1332 1333void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered) 1334{ 1335 const bool selectionHasDirection = true; 1336 setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 1337} 1338 1339void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered) 1340{ 1341 const bool selectionHasDirection = true; 1342 setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered)); 1343} 1344 1345void CaretBase::clearCaretRect() 1346{ 1347 m_caretLocalRect = LayoutRect(); 1348} 1349 1350bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition) 1351{ 1352 document->updateLayoutIgnorePendingStylesheets(); 1353 m_caretRectNeedsUpdate = false; 1354 RenderObject* renderer; 1355 m_caretLocalRect = localCaretRectInRendererForCaretPainting(caretPosition, renderer); 1356 return !m_caretLocalRect.isEmpty(); 1357} 1358 1359RenderObject* FrameSelection::caretRendererWithoutUpdatingLayout() const 1360{ 1361 return rendererForCaretPainting(m_selection.start().deprecatedNode()); 1362} 1363 1364RenderObject* DragCaretController::caretRenderer() const 1365{ 1366 return rendererForCaretPainting(m_position.deepEquivalent().deprecatedNode()); 1367} 1368 1369static bool isNonOrphanedCaret(const VisibleSelection& selection) 1370{ 1371 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan(); 1372} 1373 1374IntRect FrameSelection::absoluteCaretBounds() 1375{ 1376 if (!m_frame) 1377 return IntRect(); 1378 updateSelectionByUpdatingLayoutOrStyle(*m_frame); 1379 recomputeCaretRect(); 1380 return m_absCaretBounds; 1381} 1382 1383static void repaintCaretForLocalRect(Node* node, const LayoutRect& rect) 1384{ 1385 RenderObject* caretPainter = rendererForCaretPainting(node); 1386 if (!caretPainter) 1387 return; 1388 1389 caretPainter->repaintRectangle(rect); 1390} 1391 1392bool FrameSelection::recomputeCaretRect() 1393{ 1394 if (!shouldUpdateCaretRect()) 1395 return false; 1396 1397 if (!m_frame) 1398 return false; 1399 1400 FrameView* v = m_frame->document()->view(); 1401 if (!v) 1402 return false; 1403 1404 LayoutRect oldRect = localCaretRectWithoutUpdate(); 1405 1406 RefPtr<Node> caretNode = m_previousCaretNode; 1407 if (shouldUpdateCaretRect()) { 1408 if (!isNonOrphanedCaret(m_selection)) 1409 clearCaretRect(); 1410 else { 1411 VisiblePosition visibleStart = m_selection.visibleStart(); 1412 if (updateCaretRect(m_frame->document(), visibleStart)) { 1413 caretNode = visibleStart.deepEquivalent().deprecatedNode(); 1414 m_absCaretBoundsDirty = true; 1415 } 1416 } 1417 } 1418 LayoutRect newRect = localCaretRectWithoutUpdate(); 1419 1420 if (caretNode == m_previousCaretNode && oldRect == newRect && !m_absCaretBoundsDirty) 1421 return false; 1422 1423 IntRect oldAbsCaretBounds = m_absCaretBounds; 1424 m_absCaretBounds = absoluteBoundsForLocalCaretRect(rendererForCaretPainting(caretNode.get()), newRect); 1425 1426 if (m_absCaretBoundsDirty && m_selection.isCaret()) // We should be able to always assert this condition. 1427 ASSERT(m_absCaretBounds == m_selection.visibleStart().absoluteCaretBounds()); 1428 1429 m_absCaretBoundsDirty = false; 1430 1431 if (caretNode == m_previousCaretNode && oldAbsCaretBounds == m_absCaretBounds) 1432 return false; 1433 1434#if ENABLE(TEXT_CARET) 1435 if (RenderView* view = m_frame->document()->renderView()) { 1436 bool previousOrNewCaretNodeIsContentEditable = m_selection.isContentEditable() || (m_previousCaretNode && m_previousCaretNode->isContentEditable()); 1437 if (shouldRepaintCaret(view, previousOrNewCaretNodeIsContentEditable)) { 1438 if (m_previousCaretNode) 1439 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect); 1440 m_previousCaretNode = caretNode; 1441 repaintCaretForLocalRect(caretNode.get(), newRect); 1442 } 1443 } 1444#endif 1445 return true; 1446} 1447 1448bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const 1449{ 1450 ASSERT(view); 1451 Frame* frame = &view->frameView().frame(); // The frame where the selection started. 1452 bool caretBrowsing = frame && frame->settings().caretBrowsingEnabled(); 1453 return (caretBrowsing || isContentEditable); 1454} 1455 1456void FrameSelection::invalidateCaretRect() 1457{ 1458 if (!isCaret()) 1459 return; 1460 1461 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect()); 1462} 1463 1464void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged) 1465{ 1466 // EDIT FIXME: This is an unfortunate hack. 1467 // Basically, we can't trust this layout position since we 1468 // can't guarantee that the check to see if we are in unrendered 1469 // content will work at this point. We may have to wait for 1470 // a layout and re-render of the document to happen. So, resetting this 1471 // flag will cause another caret layout to happen the first time 1472 // that we try to paint the caret after this call. That one will work since 1473 // it happens after the document has accounted for any editing 1474 // changes which may have been done. 1475 // And, we need to leave this layout here so the caret moves right 1476 // away after clicking. 1477 m_caretRectNeedsUpdate = true; 1478 1479 if (caretRectChanged) 1480 return; 1481 1482 if (RenderView* view = node->document().renderView()) { 1483 if (shouldRepaintCaret(view, node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable))) 1484 repaintCaretForLocalRect(node, localCaretRectWithoutUpdate()); 1485 } 1486} 1487 1488void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) 1489{ 1490 if (m_selection.isCaret() && m_caretPaint) 1491 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect); 1492} 1493 1494void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const 1495{ 1496#if ENABLE(TEXT_CARET) 1497 if (m_caretVisibility == Hidden) 1498 return; 1499 1500 LayoutRect drawingRect = localCaretRectWithoutUpdate(); 1501 RenderObject* renderer = rendererForCaretPainting(node); 1502 if (renderer && renderer->isBox()) 1503 toRenderBox(renderer)->flipForWritingMode(drawingRect); 1504 drawingRect.moveBy(roundedIntPoint(paintOffset)); 1505 LayoutRect caret = intersection(drawingRect, clipRect); 1506 if (caret.isEmpty()) 1507 return; 1508 1509 Color caretColor = Color::black; 1510 ColorSpace colorSpace = ColorSpaceDeviceRGB; 1511 Element* element = node->isElementNode() ? toElement(node) : node->parentElement(); 1512 1513 if (element && element->renderer()) { 1514 caretColor = element->renderer()->style().visitedDependentColor(CSSPropertyColor); 1515 colorSpace = element->renderer()->style().colorSpace(); 1516 } 1517 1518 context->fillRect(caret, caretColor, colorSpace); 1519#else 1520 UNUSED_PARAM(node); 1521 UNUSED_PARAM(context); 1522 UNUSED_PARAM(paintOffset); 1523 UNUSED_PARAM(clipRect); 1524#endif 1525} 1526 1527void FrameSelection::debugRenderer(RenderObject* r, bool selected) const 1528{ 1529 if (r->node()->isElementNode()) { 1530 Element* element = toElement(r->node()); 1531 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data()); 1532 } else if (r->isText()) { 1533 RenderText* textRenderer = toRenderText(r); 1534 if (!textRenderer->textLength() || !textRenderer->firstTextBox()) { 1535 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " "); 1536 return; 1537 } 1538 1539 static const int max = 36; 1540 String text = textRenderer->text(); 1541 int textLength = text.length(); 1542 if (selected) { 1543 int offset = 0; 1544 if (r->node() == m_selection.start().containerNode()) 1545 offset = m_selection.start().computeOffsetInContainerNode(); 1546 else if (r->node() == m_selection.end().containerNode()) 1547 offset = m_selection.end().computeOffsetInContainerNode(); 1548 1549 int pos; 1550 InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos); 1551 text = text.substring(box->start(), box->len()); 1552 1553 String show; 1554 int mid = max / 2; 1555 int caret = 0; 1556 1557 // text is shorter than max 1558 if (textLength < max) { 1559 show = text; 1560 caret = pos; 1561 } else if (pos - mid < 0) { 1562 // too few characters to left 1563 show = text.left(max - 3) + "..."; 1564 caret = pos; 1565 } else if (pos - mid >= 0 && pos + mid <= textLength) { 1566 // enough characters on each side 1567 show = "..." + text.substring(pos - mid + 3, max - 6) + "..."; 1568 caret = mid; 1569 } else { 1570 // too few characters on right 1571 show = "..." + text.right(max - 3); 1572 caret = pos - (textLength - show.length()); 1573 } 1574 1575 show.replace('\n', ' '); 1576 show.replace('\r', ' '); 1577 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos); 1578 fprintf(stderr, " "); 1579 for (int i = 0; i < caret; i++) 1580 fprintf(stderr, " "); 1581 fprintf(stderr, "^\n"); 1582 } else { 1583 if ((int)text.length() > max) 1584 text = text.left(max - 3) + "..."; 1585 else 1586 text = text.left(max); 1587 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data()); 1588 } 1589 } 1590} 1591 1592bool FrameSelection::contains(const LayoutPoint& point) 1593{ 1594 Document* document = m_frame->document(); 1595 1596 // Treat a collapsed selection like no selection. 1597 if (!isRange()) 1598 return false; 1599 if (!document->renderView()) 1600 return false; 1601 1602 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::DisallowShadowContent); 1603 HitTestResult result(point); 1604 document->renderView()->hitTest(request, result); 1605 Node* innerNode = result.innerNode(); 1606 if (!innerNode || !innerNode->renderer()) 1607 return false; 1608 1609 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint(), nullptr)); 1610 if (visiblePos.isNull()) 1611 return false; 1612 1613 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull()) 1614 return false; 1615 1616 Position start(m_selection.visibleStart().deepEquivalent()); 1617 Position end(m_selection.visibleEnd().deepEquivalent()); 1618 Position p(visiblePos.deepEquivalent()); 1619 1620 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0; 1621} 1622 1623// Workaround for the fact that it's hard to delete a frame. 1624// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected. 1625// Can't do this implicitly as part of every setSelection call because in some contexts it might not be good 1626// for the focus to move to another frame. So instead we call it from places where we are selecting with the 1627// mouse or the keyboard after setting the selection. 1628void FrameSelection::selectFrameElementInParentIfFullySelected() 1629{ 1630 // Find the parent frame; if there is none, then we have nothing to do. 1631 Frame* parent = m_frame->tree().parent(); 1632 if (!parent) 1633 return; 1634 Page* page = m_frame->page(); 1635 if (!page) 1636 return; 1637 1638 // Check if the selection contains the entire frame contents; if not, then there is nothing to do. 1639 if (!isRange()) 1640 return; 1641 if (!isStartOfDocument(selection().visibleStart())) 1642 return; 1643 if (!isEndOfDocument(selection().visibleEnd())) 1644 return; 1645 1646 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. 1647 Element* ownerElement = m_frame->ownerElement(); 1648 if (!ownerElement) 1649 return; 1650 ContainerNode* ownerElementParent = ownerElement->parentNode(); 1651 if (!ownerElementParent) 1652 return; 1653 1654 // This method's purpose is it to make it easier to select iframes (in order to delete them). Don't do anything if the iframe isn't deletable. 1655 if (!ownerElementParent->hasEditableStyle()) 1656 return; 1657 1658 // Create compute positions before and after the element. 1659 unsigned ownerElementNodeIndex = ownerElement->nodeIndex(); 1660 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor))); 1661 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE)); 1662 1663 // Focus on the parent frame, and then select from before this element to after. 1664 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement); 1665 if (parent->selection().shouldChangeSelection(newSelection)) { 1666 page->focusController().setFocusedFrame(parent); 1667 parent->selection().setSelection(newSelection); 1668 } 1669} 1670 1671void FrameSelection::selectAll() 1672{ 1673 Document* document = m_frame->document(); 1674 1675 Element* focusedElement = document->focusedElement(); 1676 if (focusedElement && focusedElement->hasTagName(selectTag)) { 1677 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement()); 1678 if (selectElement->canSelectAll()) { 1679 selectElement->selectAll(); 1680 return; 1681 } 1682 } 1683 1684 RefPtr<Node> root = 0; 1685 Node* selectStartTarget = 0; 1686 if (m_selection.isContentEditable()) { 1687 root = highestEditableRoot(m_selection.start()); 1688 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode()) 1689 selectStartTarget = shadowRoot->shadowHost(); 1690 else 1691 selectStartTarget = root.get(); 1692 } else { 1693 if (m_selection.isNone() && focusedElement) { 1694 if (focusedElement->isTextFormControl()) { 1695 toHTMLTextFormControlElement(focusedElement)->select(); 1696 return; 1697 } 1698 root = focusedElement->nonBoundaryShadowTreeRootNode(); 1699 } else 1700 root = m_selection.nonBoundaryShadowTreeRootNode(); 1701 1702 if (root) 1703 selectStartTarget = root->shadowHost(); 1704 else { 1705 root = document->documentElement(); 1706 selectStartTarget = document->body(); 1707 } 1708 } 1709 if (!root) 1710 return; 1711 1712 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true))) 1713 return; 1714 1715 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get())); 1716 1717 if (shouldChangeSelection(newSelection)) 1718 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent); 1719} 1720 1721bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping) 1722{ 1723 if (!range || !range->startContainer() || !range->endContainer()) 1724 return false; 1725 ASSERT(&range->startContainer()->document() == &range->endContainer()->document()); 1726 1727 VisibleSelection newSelection(range, affinity); 1728 1729#if PLATFORM(IOS) 1730 // FIXME: Why do we need this check only in iOS? 1731 if (range->startContainer() && range->endContainer() && newSelection.isNone()) 1732 return false; 1733#endif 1734 1735 setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0)); 1736 return true; 1737} 1738 1739void FrameSelection::focusedOrActiveStateChanged() 1740{ 1741 bool activeAndFocused = isFocusedAndActive(); 1742 Ref<Document> document(*m_frame->document()); 1743 1744 document->updateStyleIfNeeded(); 1745 1746#if USE(UIKIT_EDITING) 1747 // Caret blinking (blinks | does not blink) 1748 if (activeAndFocused) 1749 setSelectionFromNone(); 1750 setCaretVisible(activeAndFocused); 1751#else 1752 // Because RenderObject::selectionBackgroundColor() and 1753 // RenderObject::selectionForegroundColor() check if the frame is active, 1754 // we have to update places those colors were painted. 1755 if (RenderView* view = document->renderView()) 1756 view->repaintSelection(); 1757 1758 // Caret appears in the active frame. 1759 if (activeAndFocused) 1760 setSelectionFromNone(); 1761 setCaretVisibility(activeAndFocused ? Visible : Hidden); 1762 1763 // Update for caps lock state 1764 m_frame->eventHandler().capsLockStateMayHaveChanged(); 1765 1766 // Because StyleResolver::checkOneSelector() and 1767 // RenderTheme::isFocused() check if the frame is active, we have to 1768 // update style and theme state that depended on those. 1769 if (Element* element = document->focusedElement()) { 1770 element->setNeedsStyleRecalc(); 1771 if (RenderObject* renderer = element->renderer()) 1772 if (renderer && renderer->style().hasAppearance()) 1773 renderer->theme().stateChanged(*renderer, ControlStates::FocusState); 1774 } 1775#endif 1776} 1777 1778void FrameSelection::pageActivationChanged() 1779{ 1780 focusedOrActiveStateChanged(); 1781} 1782 1783void FrameSelection::setFocused(bool flag) 1784{ 1785 if (m_focused == flag) 1786 return; 1787 m_focused = flag; 1788 1789 focusedOrActiveStateChanged(); 1790} 1791 1792bool FrameSelection::isFocusedAndActive() const 1793{ 1794 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive(); 1795} 1796 1797#if ENABLE(TEXT_CARET) 1798inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame) 1799{ 1800 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking(); 1801} 1802#endif 1803 1804void FrameSelection::updateAppearance() 1805{ 1806#if PLATFORM(IOS) 1807 if (!m_updateAppearanceEnabled) 1808 return; 1809#endif 1810 1811 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case 1812 // the FrameSelection will paint a blinking caret as usual). 1813 VisibleSelection oldSelection = selection(); 1814 VisiblePosition forwardPosition; 1815 if (m_shouldShowBlockCursor && oldSelection.isCaret()) { 1816 forwardPosition = modifyExtendingForward(CharacterGranularity); 1817 m_caretPaint = forwardPosition.isNull(); 1818 } 1819 1820#if ENABLE(TEXT_CARET) 1821 bool caretRectChangedOrCleared = recomputeCaretRect(); 1822 1823 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled(); 1824 bool shouldBlink = caretIsVisible() && isCaret() && (oldSelection.isContentEditable() || caretBrowsing) && forwardPosition.isNull(); 1825 1826 // If the caret moved, stop the blink timer so we can restart with a 1827 // black caret in the new location. 1828 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) 1829 m_caretBlinkTimer.stop(); 1830 1831 // Start blinking with a black caret. Be sure not to restart if we're 1832 // already blinking in the right location. 1833 if (shouldBlink && !m_caretBlinkTimer.isActive()) { 1834 if (double blinkInterval = m_frame->page()->theme().caretBlinkInterval()) 1835 m_caretBlinkTimer.startRepeating(blinkInterval); 1836 1837 if (!m_caretPaint) { 1838 m_caretPaint = true; 1839 invalidateCaretRect(); 1840 } 1841 } 1842#endif 1843 1844 RenderView* view = m_frame->contentRenderer(); 1845 if (!view) 1846 return; 1847 1848 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps 1849 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>. 1850 VisibleSelection selection(oldSelection.visibleStart(), forwardPosition.isNotNull() ? forwardPosition : oldSelection.visibleEnd()); 1851 1852 if (!selection.isRange()) { 1853 view->clearSelection(); 1854 return; 1855 } 1856 1857 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection. 1858 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3] 1859 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected 1860 // and will fill the gap before 'bar'. 1861 Position startPos = selection.start(); 1862 Position candidate = startPos.downstream(); 1863 if (candidate.isCandidate()) 1864 startPos = candidate; 1865 Position endPos = selection.end(); 1866 candidate = endPos.upstream(); 1867 if (candidate.isCandidate()) 1868 endPos = candidate; 1869 1870 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted 1871 // because we don't yet notify the FrameSelection of text removal. 1872 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) { 1873 RenderObject* startRenderer = startPos.deprecatedNode()->renderer(); 1874 RenderObject* endRenderer = endPos.deprecatedNode()->renderer(); 1875 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset()); 1876 } 1877} 1878 1879void FrameSelection::setCaretVisibility(CaretVisibility visibility) 1880{ 1881 if (caretVisibility() == visibility) 1882 return; 1883 1884 // FIXME: We shouldn't trigger a synchrnously layout here. 1885 if (m_frame) 1886 updateSelectionByUpdatingLayoutOrStyle(*m_frame); 1887 1888#if ENABLE(TEXT_CARET) 1889 if (m_caretPaint) { 1890 m_caretPaint = false; 1891 invalidateCaretRect(); 1892 } 1893 CaretBase::setCaretVisibility(visibility); 1894#endif 1895 1896 updateAppearance(); 1897} 1898 1899void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>&) 1900{ 1901#if ENABLE(TEXT_CARET) 1902 ASSERT(caretIsVisible()); 1903 ASSERT(isCaret()); 1904 bool caretPaint = m_caretPaint; 1905 if (isCaretBlinkingSuspended() && caretPaint) 1906 return; 1907 m_caretPaint = !caretPaint; 1908 invalidateCaretRect(); 1909#endif 1910} 1911 1912// Helper function that tells whether a particular node is an element that has an entire 1913// Frame and FrameView, a <frame>, <iframe>, or <object>. 1914static bool isFrameElement(const Node* n) 1915{ 1916 if (!n) 1917 return false; 1918 RenderObject* renderer = n->renderer(); 1919 if (!renderer || !renderer->isWidget()) 1920 return false; 1921 Widget* widget = toRenderWidget(renderer)->widget(); 1922 return widget && widget->isFrameView(); 1923} 1924 1925void FrameSelection::setFocusedElementIfNeeded() 1926{ 1927 if (isNone() || !isFocused()) 1928 return; 1929 1930 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled(); 1931 if (caretBrowsing) { 1932 if (Element* anchor = enclosingAnchorElement(m_selection.base())) { 1933 m_frame->page()->focusController().setFocusedElement(anchor, m_frame); 1934 return; 1935 } 1936 } 1937 1938 if (Element* target = m_selection.rootEditableElement()) { 1939 // Walk up the DOM tree to search for an element to focus. 1940 while (target) { 1941 // We don't want to set focus on a subframe when selecting in a parent frame, 1942 // so add the !isFrameElement check here. There's probably a better way to make this 1943 // work in the long term, but this is the safest fix at this time. 1944 if (target->isMouseFocusable() && !isFrameElement(target)) { 1945 m_frame->page()->focusController().setFocusedElement(target, m_frame); 1946 return; 1947 } 1948 target = target->parentOrShadowHostElement(); 1949 } 1950 m_frame->document()->setFocusedElement(0); 1951 } 1952 1953 if (caretBrowsing) 1954 m_frame->page()->focusController().setFocusedElement(0, m_frame); 1955} 1956 1957void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const 1958{ 1959#if ENABLE(TEXT_CARET) 1960 if (m_position.deepEquivalent().deprecatedNode()->document().frame() == frame) 1961 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect); 1962#else 1963 UNUSED_PARAM(frame); 1964 UNUSED_PARAM(p); 1965 UNUSED_PARAM(paintOffset); 1966 UNUSED_PARAM(clipRect); 1967#endif 1968} 1969 1970PassRefPtr<MutableStyleProperties> FrameSelection::copyTypingStyle() const 1971{ 1972 if (!m_typingStyle || !m_typingStyle->style()) 1973 return 0; 1974 return m_typingStyle->style()->mutableCopy(); 1975} 1976 1977bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const 1978{ 1979#if PLATFORM(IOS) 1980 if (m_frame->selectionChangeCallbacksDisabled()) 1981 return true; 1982#endif 1983 return m_frame->editor().client()->shouldDeleteRange(selection.toNormalizedRange().get()); 1984} 1985 1986FloatRect FrameSelection::selectionBounds(bool clipToVisibleContent) const 1987{ 1988 if (!m_frame->document()) 1989 return LayoutRect(); 1990 1991 updateSelectionByUpdatingLayoutOrStyle(*m_frame); 1992 RenderView* root = m_frame->contentRenderer(); 1993 FrameView* view = m_frame->view(); 1994 if (!root || !view) 1995 return LayoutRect(); 1996 1997 LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent); 1998 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect)) : selectionRect; 1999} 2000 2001void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const 2002{ 2003 RenderView* root = m_frame->contentRenderer(); 2004 if (!root) 2005 return; 2006 2007 FloatRect visibleContentRect = m_frame->view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect); 2008 2009 Vector<FloatQuad> quads; 2010 toNormalizedRange()->textQuads(quads, true); 2011 2012 size_t size = quads.size(); 2013 for (size_t i = 0; i < size; ++i) { 2014 FloatRect intersectionRect = intersection(quads[i].enclosingBoundingBox(), visibleContentRect); 2015 if (!intersectionRect.isEmpty()) 2016 rectangles.append(intersectionRect); 2017 } 2018} 2019 2020// Scans logically forward from "start", including any child frames. 2021static HTMLFormElement* scanForForm(Element* start) 2022{ 2023 if (!start) 2024 return nullptr; 2025 2026 auto descendants = descendantsOfType<HTMLElement>(start->document()); 2027 for (auto it = descendants.from(*start), end = descendants.end(); it != end; ++it) { 2028 HTMLElement& element = *it; 2029 if (isHTMLFormElement(&element)) 2030 return toHTMLFormElement(&element); 2031 if (isHTMLFormControlElement(element)) 2032 return toHTMLFormControlElement(element).form(); 2033 if (isHTMLFrameElementBase(element)) { 2034 Document* contentDocument = toHTMLFrameElementBase(element).contentDocument(); 2035 if (!contentDocument) 2036 continue; 2037 if (HTMLFormElement* frameResult = scanForForm(contentDocument->documentElement())) 2038 return frameResult; 2039 } 2040 } 2041 return nullptr; 2042} 2043 2044// We look for either the form containing the current focus, or for one immediately after it 2045HTMLFormElement* FrameSelection::currentForm() const 2046{ 2047 // Start looking either at the active (first responder) node, or where the selection is. 2048 Element* start = m_frame->document()->focusedElement(); 2049 if (!start) 2050 start = m_selection.start().element(); 2051 if (!start) 2052 return nullptr; 2053 2054 if (auto form = lineageOfType<HTMLFormElement>(*start).first()) 2055 return form; 2056 if (auto formControl = lineageOfType<HTMLFormControlElement>(*start).first()) 2057 return formControl->form(); 2058 2059 // Try walking forward in the node tree to find a form element. 2060 return scanForForm(start); 2061} 2062 2063void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) 2064{ 2065 LayoutRect rect; 2066 2067 switch (m_selection.selectionType()) { 2068 case VisibleSelection::NoSelection: 2069 return; 2070 case VisibleSelection::CaretSelection: 2071 rect = absoluteCaretBounds(); 2072 break; 2073 case VisibleSelection::RangeSelection: 2074 rect = revealExtentOption == RevealExtent ? VisiblePosition(m_selection.extent()).absoluteCaretBounds() : enclosingIntRect(selectionBounds(false)); 2075 break; 2076 } 2077 2078 Position start = m_selection.start(); 2079 ASSERT(start.deprecatedNode()); 2080 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) { 2081#if PLATFORM(IOS) 2082 if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) { 2083 if (!m_scrollingSuppressCount) { 2084 layer->setAdjustForIOSCaretWhenScrolling(true); 2085 layer->scrollRectToVisible(rect, alignment, alignment); 2086 layer->setAdjustForIOSCaretWhenScrolling(false); 2087 updateAppearance(); 2088 if (m_frame->page()) 2089 m_frame->page()->chrome().client().notifyRevealedSelectionByScrollingFrame(m_frame); 2090 } 2091 } 2092#else 2093 // FIXME: This code only handles scrolling the startContainer's layer, but 2094 // the selection rect could intersect more than just that. 2095 // See <rdar://problem/4799899>. 2096 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment)) 2097 updateAppearance(); 2098#endif 2099 } 2100} 2101 2102void FrameSelection::setSelectionFromNone() 2103{ 2104 // Put a caret inside the body if the entire frame is editable (either the 2105 // entire WebView is editable or designMode is on for this document). 2106 2107 Document* document = m_frame->document(); 2108#if !PLATFORM(IOS) 2109 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled(); 2110 if (!isNone() || !(document->hasEditableStyle() || caretBrowsing)) 2111 return; 2112#else 2113 if (!document || !(isNone() || isStartOfDocument(VisiblePosition(m_selection.start(), m_selection.affinity()))) || !document->hasEditableStyle()) 2114 return; 2115#endif 2116 2117 Node* node = document->documentElement(); 2118 while (node && !node->hasTagName(bodyTag)) 2119 node = NodeTraversal::next(node); 2120 if (node) 2121 setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM)); 2122} 2123 2124bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const 2125{ 2126#if PLATFORM(IOS) 2127 if (m_frame->selectionChangeCallbacksDisabled()) 2128 return true; 2129#endif 2130 return m_frame->editor().shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false); 2131} 2132 2133bool FrameSelection::dispatchSelectStart() 2134{ 2135 Node* selectStartTarget = m_selection.extent().containerNode(); 2136 if (!selectStartTarget) 2137 return true; 2138 2139 return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)); 2140} 2141 2142void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor) 2143{ 2144 m_shouldShowBlockCursor = shouldShowBlockCursor; 2145 2146 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 2147 2148 updateAppearance(); 2149} 2150 2151void FrameSelection::didLayout() 2152{ 2153 setCaretRectNeedsUpdate(); 2154 updateAndRevealSelection(); 2155 updateDataDetectorsForSelection(); 2156} 2157 2158#ifndef NDEBUG 2159 2160void FrameSelection::formatForDebugger(char* buffer, unsigned length) const 2161{ 2162 m_selection.formatForDebugger(buffer, length); 2163} 2164 2165void FrameSelection::showTreeForThis() const 2166{ 2167 m_selection.showTreeForThis(); 2168} 2169 2170#endif 2171 2172#if PLATFORM(IOS) 2173void FrameSelection::expandSelectionToElementContainingCaretSelection() 2174{ 2175 RefPtr<Range> range = elementRangeContainingCaretSelection(); 2176 if (!range) 2177 return; 2178 VisibleSelection selection(range.get(), DOWNSTREAM); 2179 setSelection(selection); 2180} 2181 2182PassRefPtr<Range> FrameSelection::elementRangeContainingCaretSelection() const 2183{ 2184 if (m_selection.isNone()) 2185 return nullptr; 2186 2187 VisibleSelection selection = m_selection; 2188 if (selection.isNone()) 2189 return nullptr; 2190 2191 VisiblePosition visiblePos(selection.start(), VP_DEFAULT_AFFINITY); 2192 if (visiblePos.isNull()) 2193 return nullptr; 2194 2195 Node* node = visiblePos.deepEquivalent().deprecatedNode(); 2196 Element* element = deprecatedEnclosingBlockFlowElement(node); 2197 if (!element) 2198 return nullptr; 2199 2200 Position startPos = createLegacyEditingPosition(element, 0); 2201 Position endPos = createLegacyEditingPosition(element, element->childNodeCount()); 2202 2203 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY); 2204 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY); 2205 if (startVisiblePos.isNull() || endVisiblePos.isNull()) 2206 return nullptr; 2207 2208 selection.setBase(startVisiblePos); 2209 selection.setExtent(endVisiblePos); 2210 2211 return selection.toNormalizedRange(); 2212} 2213 2214void FrameSelection::expandSelectionToWordContainingCaretSelection() 2215{ 2216 VisibleSelection selection(wordSelectionContainingCaretSelection(m_selection)); 2217 if (selection.isCaretOrRange()) 2218 setSelection(selection); 2219} 2220 2221PassRefPtr<Range> FrameSelection::wordRangeContainingCaretSelection() 2222{ 2223 return wordSelectionContainingCaretSelection(m_selection).toNormalizedRange(); 2224} 2225 2226void FrameSelection::expandSelectionToStartOfWordContainingCaretSelection() 2227{ 2228 if (m_selection.isNone() || isStartOfDocument(m_selection.start())) 2229 return; 2230 2231 VisiblePosition s1(m_selection.start()); 2232 VisiblePosition e1(m_selection.end()); 2233 2234 VisibleSelection expanded(wordSelectionContainingCaretSelection(m_selection)); 2235 VisiblePosition s2(expanded.start()); 2236 2237 // Don't allow the start to become greater after the expansion. 2238 if (s2.isNull() || s2 > s1) 2239 s2 = s1; 2240 2241 moveTo(s2, e1); 2242} 2243 2244UChar FrameSelection::characterInRelationToCaretSelection(int amount) const 2245{ 2246 if (m_selection.isNone()) 2247 return 0; 2248 2249 VisibleSelection selection = m_selection; 2250 ASSERT(selection.isCaretOrRange()); 2251 2252 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY); 2253 2254 if (amount < 0) { 2255 int count = abs(amount); 2256 for (int i = 0; i < count; i++) 2257 visiblePosition = visiblePosition.previous(); 2258 return visiblePosition.characterBefore(); 2259 } 2260 for (int i = 0; i < amount; i++) 2261 visiblePosition = visiblePosition.next(); 2262 return visiblePosition.characterAfter(); 2263} 2264 2265UChar FrameSelection::characterBeforeCaretSelection() const 2266{ 2267 if (m_selection.isNone()) 2268 return 0; 2269 2270 VisibleSelection selection = m_selection; 2271 ASSERT(selection.isCaretOrRange()); 2272 2273 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY); 2274 return visiblePosition.characterBefore(); 2275} 2276 2277UChar FrameSelection::characterAfterCaretSelection() const 2278{ 2279 if (m_selection.isNone()) 2280 return 0; 2281 2282 VisibleSelection selection = m_selection; 2283 ASSERT(selection.isCaretOrRange()); 2284 2285 VisiblePosition visiblePosition(selection.end(), VP_DEFAULT_AFFINITY); 2286 return visiblePosition.characterAfter(); 2287} 2288 2289int FrameSelection::wordOffsetInRange(const Range *range) const 2290{ 2291 if (!range) 2292 return -1; 2293 2294 VisibleSelection selection = m_selection; 2295 if (!selection.isCaret()) 2296 return -1; 2297 2298 // FIXME: This will only work in cases where the selection remains in 2299 // the same node after it is expanded. Improve to handle more complicated 2300 // cases. 2301 ExceptionCode ec = 0; 2302 int result = selection.start().deprecatedEditingOffset() - range->startOffset(ec); 2303 ASSERT(!ec); 2304 if (result < 0) 2305 result = 0; 2306 return result; 2307} 2308 2309bool FrameSelection::spaceFollowsWordInRange(const Range *range) const 2310{ 2311 if (!range) 2312 return false; 2313 ExceptionCode ec = 0; 2314 Node* node = range->endContainer(ec); 2315 ASSERT(!ec); 2316 int endOffset = range->endOffset(ec); 2317 ASSERT(!ec); 2318 VisiblePosition pos(createLegacyEditingPosition(node, endOffset), VP_DEFAULT_AFFINITY); 2319 return isSpaceOrNewline(pos.characterAfter()); 2320} 2321 2322bool FrameSelection::selectionAtDocumentStart() const 2323{ 2324 VisibleSelection selection = m_selection; 2325 if (selection.isNone()) 2326 return false; 2327 2328 Position startPos(selection.start()); 2329 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY); 2330 if (pos.isNull()) 2331 return false; 2332 2333 return isStartOfDocument(pos); 2334} 2335 2336bool FrameSelection::selectionAtSentenceStart() const 2337{ 2338 VisibleSelection selection = m_selection; 2339 if (selection.isNone()) 2340 return false; 2341 2342 return actualSelectionAtSentenceStart(selection); 2343} 2344 2345bool FrameSelection::selectionAtWordStart() const 2346{ 2347 VisibleSelection selection = m_selection; 2348 if (selection.isNone()) 2349 return false; 2350 2351 Position startPos(selection.start()); 2352 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY); 2353 if (pos.isNull()) 2354 return false; 2355 2356 if (isStartOfParagraph(pos)) 2357 return true; 2358 2359 bool result = true; 2360 unsigned previousCount = 0; 2361 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) { 2362 previousCount++; 2363 if (isStartOfParagraph(pos)) { 2364 if (previousCount == 1) 2365 result = false; 2366 break; 2367 } 2368 UChar c(pos.characterAfter()); 2369 if (c) { 2370 result = isSpaceOrNewline(c) || c == 0xA0 || (u_ispunct(c) && c != ',' && c != '-' && c != '\''); 2371 break; 2372 } 2373 } 2374 2375 return result; 2376} 2377 2378PassRefPtr<Range> FrameSelection::rangeByMovingCurrentSelection(int amount) const 2379{ 2380 return rangeByAlteringCurrentSelection(AlterationMove, amount); 2381} 2382 2383PassRefPtr<Range> FrameSelection::rangeByExtendingCurrentSelection(int amount) const 2384{ 2385 return rangeByAlteringCurrentSelection(AlterationExtend, amount); 2386} 2387 2388void FrameSelection::selectRangeOnElement(unsigned location, unsigned length, Node* node) 2389{ 2390 RefPtr<Range> resultRange = m_frame->document()->createRange(); 2391 ExceptionCode ec = 0; 2392 resultRange->setStart(node, location, ec); 2393 ASSERT(!ec); 2394 resultRange->setEnd(node, location + length, ec); 2395 ASSERT(!ec); 2396 VisibleSelection selection = VisibleSelection(resultRange.get(), SEL_DEFAULT_AFFINITY); 2397 setSelection(selection, true); 2398} 2399 2400VisibleSelection FrameSelection::wordSelectionContainingCaretSelection(const VisibleSelection& selection) 2401{ 2402 if (selection.isNone()) 2403 return VisibleSelection(); 2404 2405 ASSERT(selection.isCaretOrRange()); 2406 FrameSelection frameSelection; 2407 frameSelection.setSelection(selection); 2408 2409 Position startPosBeforeExpansion(selection.start()); 2410 Position endPosBeforeExpansion(selection.end()); 2411 VisiblePosition startVisiblePosBeforeExpansion(startPosBeforeExpansion, VP_DEFAULT_AFFINITY); 2412 VisiblePosition endVisiblePosBeforeExpansion(endPosBeforeExpansion, VP_DEFAULT_AFFINITY); 2413 if (endVisiblePosBeforeExpansion.isNull()) 2414 return VisibleSelection(); 2415 2416 if (isEndOfParagraph(endVisiblePosBeforeExpansion)) { 2417 UChar c(endVisiblePosBeforeExpansion.characterBefore()); 2418 if (isSpaceOrNewline(c) || c == 0xA0) { 2419 // End of paragraph with space. 2420 return VisibleSelection(); 2421 } 2422 } 2423 2424 // If at end of paragraph, move backwards one character. 2425 // This has the effect of selecting the word on the line (which is 2426 // what we want, rather than selecting past the end of the line). 2427 if (isEndOfParagraph(endVisiblePosBeforeExpansion) && !isStartOfParagraph(endVisiblePosBeforeExpansion)) 2428 frameSelection.modify(FrameSelection::AlterationMove, DirectionBackward, CharacterGranularity); 2429 2430 VisibleSelection newSelection = frameSelection.selection(); 2431 newSelection.expandUsingGranularity(WordGranularity); 2432 frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, frameSelection.granularity()); 2433 2434 Position startPos(frameSelection.selection().start()); 2435 Position endPos(frameSelection.selection().end()); 2436 2437 // Expansion cannot be allowed to change selection so that it is no longer 2438 // touches (or contains) the original, unexpanded selection. 2439 // Enforce this on the way into these additional calculations to give them 2440 // the best chance to yield a suitable answer. 2441 if (startPos > startPosBeforeExpansion) 2442 startPos = startPosBeforeExpansion; 2443 if (endPos < endPosBeforeExpansion) 2444 endPos = endPosBeforeExpansion; 2445 2446 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY); 2447 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY); 2448 2449 if (startVisiblePos.isNull() || endVisiblePos.isNull()) { 2450 // Start or end is nil 2451 return VisibleSelection(); 2452 } 2453 2454 if (isEndOfLine(endVisiblePosBeforeExpansion)) { 2455 VisiblePosition previous(endVisiblePos.previous()); 2456 if (previous == endVisiblePos) { 2457 // Empty document 2458 return VisibleSelection(); 2459 } 2460 UChar c(previous.characterAfter()); 2461 if (isSpaceOrNewline(c) || c == 0xA0) { 2462 // Space at end of line 2463 return VisibleSelection(); 2464 } 2465 } 2466 2467 // Expansion has selected past end of line. 2468 // Try repositioning backwards. 2469 if (isEndOfLine(startVisiblePos) && isStartOfLine(endVisiblePos)) { 2470 VisiblePosition previous(startVisiblePos.previous()); 2471 if (isEndOfLine(previous)) { 2472 // On empty line 2473 return VisibleSelection(); 2474 } 2475 UChar c(previous.characterAfter()); 2476 if (isSpaceOrNewline(c) || c == 0xA0) { 2477 // Space at end of line 2478 return VisibleSelection(); 2479 } 2480 frameSelection.moveTo(startVisiblePos); 2481 frameSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, WordGranularity); 2482 startPos = frameSelection.selection().start(); 2483 endPos = frameSelection.selection().end(); 2484 startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY); 2485 endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY); 2486 if (startVisiblePos.isNull() || endVisiblePos.isNull()) { 2487 // Start or end is nil 2488 return VisibleSelection(); 2489 } 2490 } 2491 2492 // Now loop backwards until we find a non-space. 2493 while (endVisiblePos != startVisiblePos) { 2494 VisiblePosition previous(endVisiblePos.previous()); 2495 UChar c(previous.characterAfter()); 2496 if (!isSpaceOrNewline(c) && c != 0xA0) 2497 break; 2498 endVisiblePos = previous; 2499 } 2500 2501 // Expansion cannot be allowed to change selection so that it is no longer 2502 // touches (or contains) the original, unexpanded selection. 2503 // Enforce this on the way out of the function to preserve the invariant. 2504 if (startVisiblePos > startVisiblePosBeforeExpansion) 2505 startVisiblePos = startVisiblePosBeforeExpansion; 2506 if (endVisiblePos < endVisiblePosBeforeExpansion) 2507 endVisiblePos = endVisiblePosBeforeExpansion; 2508 2509 return VisibleSelection(startVisiblePos, endVisiblePos); 2510} 2511 2512bool FrameSelection::actualSelectionAtSentenceStart(const VisibleSelection& sel) const 2513{ 2514 Position startPos(sel.start()); 2515 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY); 2516 if (pos.isNull()) 2517 return false; 2518 2519 if (isStartOfParagraph(pos)) 2520 return true; 2521 2522 bool result = true; 2523 bool sawSpace = false; 2524 unsigned previousCount = 0; 2525 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) { 2526 previousCount++; 2527 if (isStartOfParagraph(pos)) { 2528 if (previousCount == 1 || (previousCount == 2 && sawSpace)) 2529 result = false; 2530 break; 2531 } 2532 UChar c(pos.characterAfter()); 2533 if (c) { 2534 if (isSpaceOrNewline(c) || c == 0xA0) { 2535 sawSpace = true; 2536 } 2537 else { 2538 result = (c == '.' || c == '!' || c == '?'); 2539 break; 2540 } 2541 } 2542 } 2543 2544 return result; 2545} 2546 2547PassRefPtr<Range> FrameSelection::rangeByAlteringCurrentSelection(EAlteration alteration, int amount) const 2548{ 2549 if (m_selection.isNone()) 2550 return nullptr; 2551 2552 if (!amount) 2553 return toNormalizedRange(); 2554 2555 FrameSelection frameSelection; 2556 frameSelection.setSelection(m_selection); 2557 SelectionDirection direction = amount > 0 ? DirectionForward : DirectionBackward; 2558 for (int i = 0; i < abs(amount); i++) 2559 frameSelection.modify(alteration, direction, CharacterGranularity); 2560 return frameSelection.toNormalizedRange(); 2561} 2562 2563void FrameSelection::clearCurrentSelection() 2564{ 2565 setSelection(VisibleSelection()); 2566} 2567 2568void FrameSelection::setCaretBlinks(bool caretBlinks) 2569{ 2570 if (m_caretBlinks == caretBlinks) 2571 return; 2572#if ENABLE(TEXT_CARET) 2573 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 2574 if (m_caretPaint) { 2575 m_caretPaint = false; 2576 invalidateCaretRect(); 2577 } 2578#endif 2579 if (caretBlinks) 2580 setFocusedElementIfNeeded(); 2581 m_caretBlinks = caretBlinks; 2582 updateAppearance(); 2583} 2584 2585void FrameSelection::setCaretColor(const Color& caretColor) 2586{ 2587 if (m_caretColor != caretColor) { 2588 m_caretColor = caretColor; 2589 if (caretIsVisible() && m_caretBlinks && isCaret()) 2590 invalidateCaretRect(); 2591 } 2592} 2593#endif // PLATFORM(IOS) 2594 2595} 2596 2597#ifndef NDEBUG 2598 2599void showTree(const WebCore::FrameSelection& sel) 2600{ 2601 sel.showTreeForThis(); 2602} 2603 2604void showTree(const WebCore::FrameSelection* sel) 2605{ 2606 if (sel) 2607 sel->showTreeForThis(); 2608} 2609 2610#endif 2611