1 /* 2 * Copyright (C) 2004, 2008, 2009, 2010 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 COMPUTER, 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 COMPUTER, 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 "core/editing/FrameSelection.h" 28 29 #include <stdio.h> 30 #include "bindings/v8/ExceptionState.h" 31 #include "core/HTMLNames.h" 32 #include "core/accessibility/AXObjectCache.h" 33 #include "core/css/StylePropertySet.h" 34 #include "core/dom/CharacterData.h" 35 #include "core/dom/Document.h" 36 #include "core/dom/Element.h" 37 #include "core/dom/ElementTraversal.h" 38 #include "core/dom/NodeTraversal.h" 39 #include "core/dom/Text.h" 40 #include "core/editing/Editor.h" 41 #include "core/editing/InputMethodController.h" 42 #include "core/editing/RenderedPosition.h" 43 #include "core/editing/SpellChecker.h" 44 #include "core/editing/TextIterator.h" 45 #include "core/editing/TypingCommand.h" 46 #include "core/editing/VisibleUnits.h" 47 #include "core/editing/htmlediting.h" 48 #include "core/frame/LocalDOMWindow.h" 49 #include "core/frame/LocalFrame.h" 50 #include "core/html/HTMLBodyElement.h" 51 #include "core/html/HTMLFormElement.h" 52 #include "core/html/HTMLFrameElementBase.h" 53 #include "core/html/HTMLInputElement.h" 54 #include "core/html/HTMLSelectElement.h" 55 #include "core/page/EditorClient.h" 56 #include "core/page/EventHandler.h" 57 #include "core/page/FocusController.h" 58 #include "core/page/FrameTree.h" 59 #include "core/frame/FrameView.h" 60 #include "core/page/Page.h" 61 #include "core/frame/Settings.h" 62 #include "core/page/SpatialNavigation.h" 63 #include "core/rendering/HitTestRequest.h" 64 #include "core/rendering/HitTestResult.h" 65 #include "core/rendering/InlineTextBox.h" 66 #include "core/rendering/RenderLayer.h" 67 #include "core/rendering/RenderText.h" 68 #include "core/rendering/RenderTheme.h" 69 #include "core/rendering/RenderView.h" 70 #include "core/rendering/RenderWidget.h" 71 #include "platform/SecureTextInput.h" 72 #include "platform/geometry/FloatQuad.h" 73 #include "platform/graphics/GraphicsContext.h" 74 #include "wtf/text/CString.h" 75 76 #define EDIT_DEBUG 0 77 78 namespace WebCore { 79 80 using namespace HTMLNames; 81 82 static inline LayoutUnit NoXPosForVerticalArrowNavigation() 83 { 84 return LayoutUnit::min(); 85 } 86 87 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame) 88 { 89 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional(); 90 } 91 92 FrameSelection::FrameSelection(LocalFrame* frame) 93 : m_frame(frame) 94 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation()) 95 , m_observingVisibleSelection(false) 96 , m_granularity(CharacterGranularity) 97 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired) 98 , m_absCaretBoundsDirty(true) 99 , m_caretPaint(true) 100 , m_isCaretBlinkingSuspended(false) 101 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame) 102 , m_shouldShowBlockCursor(false) 103 { 104 if (shouldAlwaysUseDirectionalSelection(m_frame)) 105 m_selection.setIsDirectional(true); 106 } 107 108 FrameSelection::~FrameSelection() 109 { 110 #if !ENABLE(OILPAN) 111 // Oilpan: No need to clear out VisibleSelection observer; 112 // it is finalized as a part object of FrameSelection. 113 stopObservingVisibleSelectionChangeIfNecessary(); 114 #endif 115 } 116 117 Element* FrameSelection::rootEditableElementOrDocumentElement() const 118 { 119 Element* selectionRoot = m_selection.rootEditableElement(); 120 return selectionRoot ? selectionRoot : m_frame->document()->documentElement(); 121 } 122 123 Node* FrameSelection::rootEditableElementOrTreeScopeRootNode() const 124 { 125 Element* selectionRoot = m_selection.rootEditableElement(); 126 if (selectionRoot) 127 return selectionRoot; 128 129 Node* node = m_selection.base().containerNode(); 130 return node ? &node->treeScope().rootNode() : 0; 131 } 132 133 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align) 134 { 135 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; 136 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align); 137 } 138 139 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered) 140 { 141 const bool selectionHasDirection = true; 142 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; 143 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options); 144 } 145 146 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered) 147 { 148 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered; 149 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options); 150 } 151 152 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent) 153 { 154 RenderedPosition base(visibleBase); 155 RenderedPosition extent(visibleExtent); 156 157 if (base.isNull() || extent.isNull() || base.isEquivalent(extent)) 158 return; 159 160 if (base.atLeftBoundaryOfBidiRun()) { 161 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight()) 162 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) { 163 visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun()); 164 return; 165 } 166 return; 167 } 168 169 if (base.atRightBoundaryOfBidiRun()) { 170 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft()) 171 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) { 172 visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun()); 173 return; 174 } 175 return; 176 } 177 178 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) { 179 visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun()); 180 return; 181 } 182 183 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) { 184 visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun()); 185 return; 186 } 187 } 188 189 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity, 190 EndPointsAdjustmentMode endpointsAdjustmentMode) 191 { 192 VisibleSelection newSelection = passedNewSelection; 193 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional(); 194 195 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase(); 196 VisiblePosition newBase = base; 197 VisiblePosition extent = newSelection.visibleExtent(); 198 VisiblePosition newExtent = extent; 199 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary) 200 adjustEndpointsAtBidiBoundary(newBase, newExtent); 201 202 if (newBase != base || newExtent != extent) { 203 m_originalBase = base; 204 newSelection.setBase(newBase); 205 newSelection.setExtent(newExtent); 206 } else if (m_originalBase.isNotNull()) { 207 if (m_selection.base() == newSelection.base()) 208 newSelection.setBase(m_originalBase); 209 m_originalBase.clear(); 210 } 211 212 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional 213 if (m_selection == newSelection) 214 return; 215 216 setSelection(newSelection, granularity); 217 } 218 219 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity) 220 { 221 bool closeTyping = options & CloseTyping; 222 bool shouldClearTypingStyle = options & ClearTypingStyle; 223 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options); 224 225 VisibleSelection s = validateSelection(newSelection); 226 if (shouldAlwaysUseDirectionalSelection(m_frame)) 227 s.setIsDirectional(true); 228 229 if (!m_frame) { 230 m_selection = s; 231 return; 232 } 233 234 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection 235 // if document->frame() == m_frame we can get into an infinite loop 236 if (s.base().anchorNode()) { 237 Document& document = *s.base().document(); 238 if (document.frame() && document.frame() != m_frame && document != m_frame->document()) { 239 RefPtr<LocalFrame> guard = document.frame(); 240 document.frame()->selection().setSelection(s, options, align, granularity); 241 // It's possible that during the above set selection, this FrameSelection has been modified by 242 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since 243 // the frame is about to be destroyed. If this is the case, clear our selection. 244 if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange()) 245 clear(); 246 return; 247 } 248 } 249 250 m_granularity = granularity; 251 252 if (closeTyping) 253 TypingCommand::closeTyping(m_frame); 254 255 if (shouldClearTypingStyle) 256 clearTypingStyle(); 257 258 if (m_selection == s) { 259 // Even if selection was not changed, selection offsets may have been changed. 260 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid(); 261 notifyRendererOfSelectionChange(userTriggered); 262 return; 263 } 264 265 VisibleSelection oldSelection = m_selection; 266 267 m_selection = s; 268 setCaretRectNeedsUpdate(); 269 270 if (!s.isNone() && !(options & DoNotSetFocus)) 271 setFocusedNodeIfNeeded(); 272 273 if (!(options & DoNotUpdateAppearance)) { 274 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 275 276 // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html 277 DisableCompositingQueryAsserts disabler; 278 updateAppearance(); 279 } 280 281 // Always clear the x position used for vertical arrow navigation. 282 // It will be restored by the vertical arrow navigation code if necessary. 283 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation(); 284 selectFrameElementInParentIfFullySelected(); 285 notifyRendererOfSelectionChange(userTriggered); 286 m_frame->editor().respondToChangedSelection(oldSelection, options); 287 if (userTriggered == UserTriggered) { 288 ScrollAlignment alignment; 289 290 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed()) 291 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded; 292 else 293 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded; 294 295 revealSelection(alignment, RevealExtent); 296 } 297 298 notifyAccessibilityForSelectionChange(); 299 m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange)); 300 } 301 302 static bool removingNodeRemovesPosition(Node& node, const Position& position) 303 { 304 if (!position.anchorNode()) 305 return false; 306 307 if (position.anchorNode() == node) 308 return true; 309 310 if (!node.isElementNode()) 311 return false; 312 313 Element& element = toElement(node); 314 return element.containsIncludingShadowDOM(position.anchorNode()); 315 } 316 317 void FrameSelection::nodeWillBeRemoved(Node& node) 318 { 319 // There can't be a selection inside a fragment, so if a fragment's node is being removed, 320 // the selection in the document that created the fragment needs no adjustment. 321 if (isNone() || !node.inActiveDocument()) 322 return; 323 324 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()), 325 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end())); 326 } 327 328 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved) 329 { 330 ASSERT(node.document().isActive()); 331 332 bool clearRenderTreeSelection = false; 333 bool clearDOMTreeSelection = false; 334 335 if (startRemoved || endRemoved) { 336 Position start = m_selection.start(); 337 Position end = m_selection.end(); 338 if (startRemoved) 339 updatePositionForNodeRemoval(start, node); 340 if (endRemoved) 341 updatePositionForNodeRemoval(end, node); 342 343 if (start.isNotNull() && end.isNotNull()) { 344 if (m_selection.isBaseFirst()) 345 m_selection.setWithoutValidation(start, end); 346 else 347 m_selection.setWithoutValidation(end, start); 348 } else 349 clearDOMTreeSelection = true; 350 351 clearRenderTreeSelection = true; 352 } else if (baseRemoved || extentRemoved) { 353 // The base and/or extent are about to be removed, but the start and end aren't. 354 // Change the base and extent to the start and end, but don't re-validate the 355 // selection, since doing so could move the start and end into the node 356 // that is about to be removed. 357 if (m_selection.isBaseFirst()) 358 m_selection.setWithoutValidation(m_selection.start(), m_selection.end()); 359 else 360 m_selection.setWithoutValidation(m_selection.end(), m_selection.start()); 361 } else if (RefPtrWillBeRawPtr<Range> range = m_selection.firstRange()) { 362 TrackExceptionState exceptionState; 363 Range::CompareResults compareResult = range->compareNode(&node, exceptionState); 364 if (!exceptionState.hadException() && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) { 365 // If we did nothing here, when this node's renderer was destroyed, the rect that it 366 // occupied would be invalidated, but, selection gaps that change as a result of 367 // the removal wouldn't be invalidated. 368 // FIXME: Don't do so much unnecessary invalidation. 369 clearRenderTreeSelection = true; 370 } 371 } 372 373 if (clearRenderTreeSelection) 374 m_selection.start().document()->renderView()->clearSelection(); 375 376 if (clearDOMTreeSelection) 377 setSelection(VisibleSelection(), DoNotSetFocus); 378 } 379 380 static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) 381 { 382 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor) 383 return position; 384 385 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation 386 ASSERT(position.offsetInContainerNode() >= 0); 387 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode()); 388 // Replacing text can be viewed as a deletion followed by insertion. 389 if (positionOffset >= offset && positionOffset <= offset + oldLength) 390 positionOffset = offset; 391 392 // Adjust the offset if the position is after the end of the deleted contents 393 // (positionOffset > offset + oldLength) to avoid having a stale offset. 394 if (positionOffset > offset + oldLength) 395 positionOffset = positionOffset - oldLength + newLength; 396 397 ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length()); 398 // CharacterNode in VisibleSelection must be Text node, because Comment 399 // and ProcessingInstruction node aren't visible. 400 return Position(toText(node), positionOffset); 401 } 402 403 void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength) 404 { 405 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062. 406 if (isNone() || !node || !node->inDocument()) 407 return; 408 409 Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength); 410 Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength); 411 Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength); 412 Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength); 413 updateSelectionIfNeeded(base, extent, start, end); 414 } 415 416 static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset) 417 { 418 if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor) 419 return position; 420 421 ASSERT(position.offsetInContainerNode() >= 0); 422 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode()); 423 424 if (position.anchorNode() == &oldNode) 425 return Position(toText(oldNode.previousSibling()), positionOffset + offset); 426 427 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset) 428 return Position(toText(oldNode.previousSibling()), offset); 429 430 return position; 431 } 432 433 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset) 434 { 435 if (isNone() || !oldNode.inDocument()) 436 return; 437 Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset); 438 Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset); 439 Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset); 440 Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset); 441 updateSelectionIfNeeded(base, extent, start, end); 442 } 443 444 static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode) 445 { 446 if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor) 447 return position; 448 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation 449 ASSERT(position.offsetInContainerNode() >= 0); 450 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode()); 451 unsigned oldLength = oldNode.length(); 452 if (positionOffset <= oldLength) 453 return position; 454 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength); 455 } 456 457 void FrameSelection::didSplitTextNode(const Text& oldNode) 458 { 459 if (isNone() || !oldNode.inDocument()) 460 return; 461 Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode); 462 Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode); 463 Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode); 464 Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode); 465 updateSelectionIfNeeded(base, extent, start, end); 466 } 467 468 void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end) 469 { 470 if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end()) 471 return; 472 VisibleSelection newSelection; 473 newSelection.setWithoutValidation(base, extent); 474 setSelection(newSelection, DoNotSetFocus); 475 } 476 477 TextDirection FrameSelection::directionOfEnclosingBlock() 478 { 479 return WebCore::directionOfEnclosingBlock(m_selection.extent()); 480 } 481 482 TextDirection FrameSelection::directionOfSelection() 483 { 484 InlineBox* startBox = 0; 485 InlineBox* endBox = 0; 486 int unusedOffset; 487 // Cache the VisiblePositions because visibleStart() and visibleEnd() 488 // can cause layout, which has the potential to invalidate lineboxes. 489 VisiblePosition startPosition = m_selection.visibleStart(); 490 VisiblePosition endPosition = m_selection.visibleEnd(); 491 if (startPosition.isNotNull()) 492 startPosition.getInlineBoxAndOffset(startBox, unusedOffset); 493 if (endPosition.isNotNull()) 494 endPosition.getInlineBoxAndOffset(endBox, unusedOffset); 495 if (startBox && endBox && startBox->direction() == endBox->direction()) 496 return startBox->direction(); 497 498 return directionOfEnclosingBlock(); 499 } 500 501 void FrameSelection::didChangeFocus() 502 { 503 // Hits in virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html 504 DisableCompositingQueryAsserts disabler; 505 updateAppearance(); 506 } 507 508 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction) 509 { 510 if (alter != AlterationExtend) 511 return; 512 513 Position start = m_selection.start(); 514 Position end = m_selection.end(); 515 516 bool baseIsStart = true; 517 518 if (m_selection.isDirectional()) { 519 // Make base and extent match start and end so we extend the user-visible selection. 520 // This only matters for cases where base and extend point to different positions than 521 // start and end (e.g. after a double-click to select a word). 522 if (m_selection.isBaseFirst()) 523 baseIsStart = true; 524 else 525 baseIsStart = false; 526 } else { 527 switch (direction) { 528 case DirectionRight: 529 if (directionOfSelection() == LTR) 530 baseIsStart = true; 531 else 532 baseIsStart = false; 533 break; 534 case DirectionForward: 535 baseIsStart = true; 536 break; 537 case DirectionLeft: 538 if (directionOfSelection() == LTR) 539 baseIsStart = false; 540 else 541 baseIsStart = true; 542 break; 543 case DirectionBackward: 544 baseIsStart = false; 545 break; 546 } 547 } 548 if (baseIsStart) { 549 m_selection.setBase(start); 550 m_selection.setExtent(end); 551 } else { 552 m_selection.setBase(end); 553 m_selection.setExtent(start); 554 } 555 } 556 557 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const 558 { 559 Settings* settings = m_frame ? m_frame->settings() : 0; 560 if (settings && settings->editingBehaviorType() == EditingMacBehavior) 561 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd(); 562 // Linux and Windows always extend selections from the extent endpoint. 563 // FIXME: VisibleSelection should be fixed to ensure as an invariant that 564 // base/extent always point to the same nodes as start/end, but which points 565 // to which depends on the value of isBaseFirst. Then this can be changed 566 // to just return m_sel.extent(). 567 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart(); 568 } 569 570 VisiblePosition FrameSelection::startForPlatform() const 571 { 572 return positionForPlatform(true); 573 } 574 575 VisiblePosition FrameSelection::endForPlatform() const 576 { 577 return positionForPlatform(false); 578 } 579 580 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition) 581 { 582 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition); 583 584 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) { 585 // In order to skip spaces when moving right, we advance one 586 // word further and then move one word back. Given the 587 // semantics of previousWordPosition() this will put us at the 588 // beginning of the word following. 589 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord); 590 if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord) 591 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord); 592 593 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition)); 594 if (movingBackwardsMovedPositionToStartOfCurrentWord) 595 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord; 596 } 597 return positionAfterCurrentWord; 598 } 599 600 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward) 601 { 602 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode())) 603 pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary)); 604 } 605 606 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity) 607 { 608 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 609 610 // The difference between modifyExtendingRight and modifyExtendingForward is: 611 // modifyExtendingForward always extends forward logically. 612 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word, 613 // it extends forward logically if the enclosing block is LTR direction, 614 // but it extends backward logically if the enclosing block is RTL direction. 615 switch (granularity) { 616 case CharacterGranularity: 617 if (directionOfEnclosingBlock() == LTR) 618 pos = pos.next(CanSkipOverEditingBoundary); 619 else 620 pos = pos.previous(CanSkipOverEditingBoundary); 621 break; 622 case WordGranularity: 623 if (directionOfEnclosingBlock() == LTR) 624 pos = nextWordPositionForPlatform(pos); 625 else 626 pos = previousWordPosition(pos); 627 break; 628 case LineBoundary: 629 if (directionOfEnclosingBlock() == LTR) 630 pos = modifyExtendingForward(granularity); 631 else 632 pos = modifyExtendingBackward(granularity); 633 break; 634 case SentenceGranularity: 635 case LineGranularity: 636 case ParagraphGranularity: 637 case SentenceBoundary: 638 case ParagraphBoundary: 639 case DocumentBoundary: 640 // FIXME: implement all of the above? 641 pos = modifyExtendingForward(granularity); 642 break; 643 } 644 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); 645 return pos; 646 } 647 648 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity) 649 { 650 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 651 switch (granularity) { 652 case CharacterGranularity: 653 pos = pos.next(CanSkipOverEditingBoundary); 654 break; 655 case WordGranularity: 656 pos = nextWordPositionForPlatform(pos); 657 break; 658 case SentenceGranularity: 659 pos = nextSentencePosition(pos); 660 break; 661 case LineGranularity: 662 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 663 break; 664 case ParagraphGranularity: 665 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 666 break; 667 case SentenceBoundary: 668 pos = endOfSentence(endForPlatform()); 669 break; 670 case LineBoundary: 671 pos = logicalEndOfLine(endForPlatform()); 672 break; 673 case ParagraphBoundary: 674 pos = endOfParagraph(endForPlatform()); 675 break; 676 case DocumentBoundary: 677 pos = endForPlatform(); 678 if (isEditablePosition(pos.deepEquivalent())) 679 pos = endOfEditableContent(pos); 680 else 681 pos = endOfDocument(pos); 682 break; 683 } 684 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR); 685 return pos; 686 } 687 688 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity) 689 { 690 VisiblePosition pos; 691 switch (granularity) { 692 case CharacterGranularity: 693 if (isRange()) { 694 if (directionOfSelection() == LTR) 695 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 696 else 697 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 698 } else 699 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true); 700 break; 701 case WordGranularity: { 702 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); 703 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); 704 break; 705 } 706 case SentenceGranularity: 707 case LineGranularity: 708 case ParagraphGranularity: 709 case SentenceBoundary: 710 case ParagraphBoundary: 711 case DocumentBoundary: 712 // FIXME: Implement all of the above. 713 pos = modifyMovingForward(granularity); 714 break; 715 case LineBoundary: 716 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); 717 break; 718 } 719 return pos; 720 } 721 722 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity) 723 { 724 VisiblePosition pos; 725 // FIXME: Stay in editable content for the less common granularities. 726 switch (granularity) { 727 case CharacterGranularity: 728 if (isRange()) 729 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 730 else 731 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary); 732 break; 733 case WordGranularity: 734 pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity())); 735 break; 736 case SentenceGranularity: 737 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 738 break; 739 case LineGranularity: { 740 // down-arrowing from a range selection that ends at the start of a line needs 741 // to leave the selection at that line start (no need to call nextLinePosition!) 742 pos = endForPlatform(); 743 if (!isRange() || !isStartOfLine(pos)) 744 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START)); 745 break; 746 } 747 case ParagraphGranularity: 748 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 749 break; 750 case SentenceBoundary: 751 pos = endOfSentence(endForPlatform()); 752 break; 753 case LineBoundary: 754 pos = logicalEndOfLine(endForPlatform()); 755 break; 756 case ParagraphBoundary: 757 pos = endOfParagraph(endForPlatform()); 758 break; 759 case DocumentBoundary: 760 pos = endForPlatform(); 761 if (isEditablePosition(pos.deepEquivalent())) 762 pos = endOfEditableContent(pos); 763 else 764 pos = endOfDocument(pos); 765 break; 766 } 767 return pos; 768 } 769 770 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity) 771 { 772 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 773 774 // The difference between modifyExtendingLeft and modifyExtendingBackward is: 775 // modifyExtendingBackward always extends backward logically. 776 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word, 777 // it extends backward logically if the enclosing block is LTR direction, 778 // but it extends forward logically if the enclosing block is RTL direction. 779 switch (granularity) { 780 case CharacterGranularity: 781 if (directionOfEnclosingBlock() == LTR) 782 pos = pos.previous(CanSkipOverEditingBoundary); 783 else 784 pos = pos.next(CanSkipOverEditingBoundary); 785 break; 786 case WordGranularity: 787 if (directionOfEnclosingBlock() == LTR) 788 pos = previousWordPosition(pos); 789 else 790 pos = nextWordPositionForPlatform(pos); 791 break; 792 case LineBoundary: 793 if (directionOfEnclosingBlock() == LTR) 794 pos = modifyExtendingBackward(granularity); 795 else 796 pos = modifyExtendingForward(granularity); 797 break; 798 case SentenceGranularity: 799 case LineGranularity: 800 case ParagraphGranularity: 801 case SentenceBoundary: 802 case ParagraphBoundary: 803 case DocumentBoundary: 804 pos = modifyExtendingBackward(granularity); 805 break; 806 } 807 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); 808 return pos; 809 } 810 811 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity) 812 { 813 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 814 815 // Extending a selection backward by word or character from just after a table selects 816 // the table. This "makes sense" from the user perspective, esp. when deleting. 817 // It was done here instead of in VisiblePosition because we want VPs to iterate 818 // over everything. 819 switch (granularity) { 820 case CharacterGranularity: 821 pos = pos.previous(CanSkipOverEditingBoundary); 822 break; 823 case WordGranularity: 824 pos = previousWordPosition(pos); 825 break; 826 case SentenceGranularity: 827 pos = previousSentencePosition(pos); 828 break; 829 case LineGranularity: 830 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 831 break; 832 case ParagraphGranularity: 833 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT)); 834 break; 835 case SentenceBoundary: 836 pos = startOfSentence(startForPlatform()); 837 break; 838 case LineBoundary: 839 pos = logicalStartOfLine(startForPlatform()); 840 break; 841 case ParagraphBoundary: 842 pos = startOfParagraph(startForPlatform()); 843 break; 844 case DocumentBoundary: 845 pos = startForPlatform(); 846 if (isEditablePosition(pos.deepEquivalent())) 847 pos = startOfEditableContent(pos); 848 else 849 pos = startOfDocument(pos); 850 break; 851 } 852 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR)); 853 return pos; 854 } 855 856 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity) 857 { 858 VisiblePosition pos; 859 switch (granularity) { 860 case CharacterGranularity: 861 if (isRange()) 862 if (directionOfSelection() == LTR) 863 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 864 else 865 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 866 else 867 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true); 868 break; 869 case WordGranularity: { 870 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight(); 871 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight); 872 break; 873 } 874 case SentenceGranularity: 875 case LineGranularity: 876 case ParagraphGranularity: 877 case SentenceBoundary: 878 case ParagraphBoundary: 879 case DocumentBoundary: 880 // FIXME: Implement all of the above. 881 pos = modifyMovingBackward(granularity); 882 break; 883 case LineBoundary: 884 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock()); 885 break; 886 } 887 return pos; 888 } 889 890 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity) 891 { 892 VisiblePosition pos; 893 switch (granularity) { 894 case CharacterGranularity: 895 if (isRange()) 896 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 897 else 898 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary); 899 break; 900 case WordGranularity: 901 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 902 break; 903 case SentenceGranularity: 904 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 905 break; 906 case LineGranularity: 907 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 908 break; 909 case ParagraphGranularity: 910 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START)); 911 break; 912 case SentenceBoundary: 913 pos = startOfSentence(startForPlatform()); 914 break; 915 case LineBoundary: 916 pos = logicalStartOfLine(startForPlatform()); 917 break; 918 case ParagraphBoundary: 919 pos = startOfParagraph(startForPlatform()); 920 break; 921 case DocumentBoundary: 922 pos = startForPlatform(); 923 if (isEditablePosition(pos.deepEquivalent())) 924 pos = startOfEditableContent(pos); 925 else 926 pos = startOfDocument(pos); 927 break; 928 } 929 return pos; 930 } 931 932 static bool isBoundary(TextGranularity granularity) 933 { 934 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary; 935 } 936 937 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered) 938 { 939 if (userTriggered == UserTriggered) { 940 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create(); 941 trialFrameSelection->setSelection(m_selection); 942 trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered); 943 944 if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart()) 945 return false; 946 } 947 948 willBeModified(alter, direction); 949 950 bool wasRange = m_selection.isRange(); 951 VisiblePosition originalStartPosition = m_selection.visibleStart(); 952 VisiblePosition position; 953 switch (direction) { 954 case DirectionRight: 955 if (alter == AlterationMove) 956 position = modifyMovingRight(granularity); 957 else 958 position = modifyExtendingRight(granularity); 959 break; 960 case DirectionForward: 961 if (alter == AlterationExtend) 962 position = modifyExtendingForward(granularity); 963 else 964 position = modifyMovingForward(granularity); 965 break; 966 case DirectionLeft: 967 if (alter == AlterationMove) 968 position = modifyMovingLeft(granularity); 969 else 970 position = modifyExtendingLeft(granularity); 971 break; 972 case DirectionBackward: 973 if (alter == AlterationExtend) 974 position = modifyExtendingBackward(granularity); 975 else 976 position = modifyMovingBackward(granularity); 977 break; 978 } 979 980 if (position.isNull()) 981 return false; 982 983 if (isSpatialNavigationEnabled(m_frame)) 984 if (!wasRange && alter == AlterationMove && position == originalStartPosition) 985 return false; 986 987 // Some of the above operations set an xPosForVerticalArrowNavigation. 988 // Setting a selection will clear it, so save it to possibly restore later. 989 // Note: the START position type is arbitrary because it is unused, it would be 990 // the requested position type if there were no xPosForVerticalArrowNavigation set. 991 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START); 992 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); 993 994 switch (alter) { 995 case AlterationMove: 996 moveTo(position, userTriggered); 997 break; 998 case AlterationExtend: 999 1000 if (!m_selection.isCaret() 1001 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity) 1002 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) { 1003 // Don't let the selection go across the base position directly. Needed to match mac 1004 // behavior when, for instance, word-selecting backwards starting with the caret in 1005 // the middle of a word and then word-selecting forward, leaving the caret in the 1006 // same place where it was, instead of directly selecting to the end of the word. 1007 VisibleSelection newSelection = m_selection; 1008 newSelection.setExtent(position); 1009 if (m_selection.isBaseFirst() != newSelection.isBaseFirst()) 1010 position = m_selection.visibleBase(); 1011 } 1012 1013 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the 1014 // base in place and moving the extent. Matches NSTextView. 1015 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity)) 1016 setExtent(position, userTriggered); 1017 else { 1018 TextDirection textDirection = directionOfEnclosingBlock(); 1019 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft)) 1020 setEnd(position, userTriggered); 1021 else 1022 setStart(position, userTriggered); 1023 } 1024 break; 1025 } 1026 1027 if (granularity == LineGranularity || granularity == ParagraphGranularity) 1028 m_xPosForVerticalArrowNavigation = x; 1029 1030 if (userTriggered == UserTriggered) 1031 m_granularity = CharacterGranularity; 1032 1033 setCaretRectNeedsUpdate(); 1034 1035 return true; 1036 } 1037 1038 // FIXME: Maybe baseline would be better? 1039 static bool absoluteCaretY(const VisiblePosition &c, int &y) 1040 { 1041 IntRect rect = c.absoluteCaretBounds(); 1042 if (rect.isEmpty()) 1043 return false; 1044 y = rect.y() + rect.height() / 2; 1045 return true; 1046 } 1047 1048 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align) 1049 { 1050 if (!verticalDistance) 1051 return false; 1052 1053 if (userTriggered == UserTriggered) { 1054 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create(); 1055 trialFrameSelection->setSelection(m_selection); 1056 trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered); 1057 } 1058 1059 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward); 1060 1061 VisiblePosition pos; 1062 LayoutUnit xPos = 0; 1063 switch (alter) { 1064 case AlterationMove: 1065 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity()); 1066 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END); 1067 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM); 1068 break; 1069 case AlterationExtend: 1070 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()); 1071 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT); 1072 m_selection.setAffinity(DOWNSTREAM); 1073 break; 1074 } 1075 1076 int startY; 1077 if (!absoluteCaretY(pos, startY)) 1078 return false; 1079 if (direction == DirectionUp) 1080 startY = -startY; 1081 int lastY = startY; 1082 1083 VisiblePosition result; 1084 VisiblePosition next; 1085 for (VisiblePosition p = pos; ; p = next) { 1086 if (direction == DirectionUp) 1087 next = previousLinePosition(p, xPos); 1088 else 1089 next = nextLinePosition(p, xPos); 1090 1091 if (next.isNull() || next == p) 1092 break; 1093 int nextY; 1094 if (!absoluteCaretY(next, nextY)) 1095 break; 1096 if (direction == DirectionUp) 1097 nextY = -nextY; 1098 if (nextY - startY > static_cast<int>(verticalDistance)) 1099 break; 1100 if (nextY >= lastY) { 1101 lastY = nextY; 1102 result = next; 1103 } 1104 } 1105 1106 if (result.isNull()) 1107 return false; 1108 1109 switch (alter) { 1110 case AlterationMove: 1111 moveTo(result, userTriggered, align); 1112 break; 1113 case AlterationExtend: 1114 setExtent(result, userTriggered); 1115 break; 1116 } 1117 1118 if (userTriggered == UserTriggered) 1119 m_granularity = CharacterGranularity; 1120 1121 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend); 1122 1123 return true; 1124 } 1125 1126 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type) 1127 { 1128 LayoutUnit x = 0; 1129 1130 if (isNone()) 1131 return x; 1132 1133 Position pos; 1134 switch (type) { 1135 case START: 1136 pos = m_selection.start(); 1137 break; 1138 case END: 1139 pos = m_selection.end(); 1140 break; 1141 case BASE: 1142 pos = m_selection.base(); 1143 break; 1144 case EXTENT: 1145 pos = m_selection.extent(); 1146 break; 1147 } 1148 1149 LocalFrame* frame = pos.document()->frame(); 1150 if (!frame) 1151 return x; 1152 1153 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) { 1154 VisiblePosition visiblePosition(pos, m_selection.affinity()); 1155 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden 1156 // after the selection is created and before this function is called. 1157 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0; 1158 m_xPosForVerticalArrowNavigation = x; 1159 } else 1160 x = m_xPosForVerticalArrowNavigation; 1161 1162 return x; 1163 } 1164 1165 void FrameSelection::clear() 1166 { 1167 m_granularity = CharacterGranularity; 1168 setSelection(VisibleSelection()); 1169 } 1170 1171 void FrameSelection::prepareForDestruction() 1172 { 1173 m_granularity = CharacterGranularity; 1174 1175 m_caretBlinkTimer.stop(); 1176 1177 RenderView* view = m_frame->contentRenderer(); 1178 if (view) 1179 view->clearSelection(); 1180 1181 setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance); 1182 m_previousCaretNode.clear(); 1183 } 1184 1185 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger) 1186 { 1187 if (m_selection.isBaseFirst()) 1188 setBase(pos, trigger); 1189 else 1190 setExtent(pos, trigger); 1191 } 1192 1193 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger) 1194 { 1195 if (m_selection.isBaseFirst()) 1196 setExtent(pos, trigger); 1197 else 1198 setBase(pos, trigger); 1199 } 1200 1201 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered) 1202 { 1203 const bool selectionHasDirection = true; 1204 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered); 1205 } 1206 1207 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered) 1208 { 1209 const bool selectionHasDirection = true; 1210 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered); 1211 } 1212 1213 RenderObject* FrameSelection::caretRenderer() const 1214 { 1215 return CaretBase::caretRenderer(m_selection.start().deprecatedNode()); 1216 } 1217 1218 static bool isNonOrphanedCaret(const VisibleSelection& selection) 1219 { 1220 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan(); 1221 } 1222 1223 LayoutRect FrameSelection::localCaretRect() 1224 { 1225 if (shouldUpdateCaretRect()) { 1226 if (!isNonOrphanedCaret(m_selection)) 1227 clearCaretRect(); 1228 else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity()))) 1229 m_absCaretBoundsDirty = true; 1230 } 1231 1232 return localCaretRectWithoutUpdate(); 1233 } 1234 1235 IntRect FrameSelection::absoluteCaretBounds() 1236 { 1237 recomputeCaretRect(); 1238 return m_absCaretBounds; 1239 } 1240 1241 bool FrameSelection::recomputeCaretRect() 1242 { 1243 if (!shouldUpdateCaretRect()) 1244 return false; 1245 1246 if (!m_frame || !m_frame->document()->view()) 1247 return false; 1248 1249 LayoutRect oldRect = localCaretRectWithoutUpdate(); 1250 LayoutRect newRect = localCaretRect(); 1251 if (oldRect == newRect && !m_absCaretBoundsDirty) 1252 return false; 1253 1254 IntRect oldAbsCaretBounds = m_absCaretBounds; 1255 m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate()); 1256 m_absCaretBoundsDirty = false; 1257 1258 if (oldAbsCaretBounds == m_absCaretBounds) 1259 return false; 1260 1261 if (RenderView* view = m_frame->document()->renderView()) { 1262 if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable())) 1263 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect); 1264 Node* node = m_selection.start().deprecatedNode(); 1265 m_previousCaretNode = node; 1266 if (shouldRepaintCaret(view, isContentEditable())) 1267 repaintCaretForLocalRect(node, newRect); 1268 } 1269 1270 return true; 1271 } 1272 1273 void FrameSelection::invalidateCaretRect() 1274 { 1275 if (!isCaret()) 1276 return; 1277 1278 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect()); 1279 } 1280 1281 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) 1282 { 1283 if (m_selection.isCaret() && m_caretPaint) 1284 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect); 1285 } 1286 1287 bool FrameSelection::contains(const LayoutPoint& point) 1288 { 1289 Document* document = m_frame->document(); 1290 1291 // Treat a collapsed selection like no selection. 1292 if (!isRange()) 1293 return false; 1294 if (!document->renderView()) 1295 return false; 1296 1297 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent); 1298 HitTestResult result(point); 1299 document->renderView()->hitTest(request, result); 1300 Node* innerNode = result.innerNode(); 1301 if (!innerNode || !innerNode->renderer()) 1302 return false; 1303 1304 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint())); 1305 if (visiblePos.isNull()) 1306 return false; 1307 1308 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull()) 1309 return false; 1310 1311 Position start(m_selection.visibleStart().deepEquivalent()); 1312 Position end(m_selection.visibleEnd().deepEquivalent()); 1313 Position p(visiblePos.deepEquivalent()); 1314 1315 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0; 1316 } 1317 1318 // Workaround for the fact that it's hard to delete a frame. 1319 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected. 1320 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good 1321 // for the focus to move to another frame. So instead we call it from places where we are selecting with the 1322 // mouse or the keyboard after setting the selection. 1323 void FrameSelection::selectFrameElementInParentIfFullySelected() 1324 { 1325 // Find the parent frame; if there is none, then we have nothing to do. 1326 Frame* parent = m_frame->tree().parent(); 1327 if (!parent) 1328 return; 1329 Page* page = m_frame->page(); 1330 if (!page) 1331 return; 1332 1333 // Check if the selection contains the entire frame contents; if not, then there is nothing to do. 1334 if (!isRange()) 1335 return; 1336 if (!isStartOfDocument(selection().visibleStart())) 1337 return; 1338 if (!isEndOfDocument(selection().visibleEnd())) 1339 return; 1340 1341 // FIXME: This is not yet implemented for cross-process frame relationships. 1342 if (!parent->isLocalFrame()) 1343 return; 1344 1345 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. 1346 // FIXME: Doesn't work for OOPI. 1347 Element* ownerElement = m_frame->deprecatedLocalOwner(); 1348 if (!ownerElement) 1349 return; 1350 ContainerNode* ownerElementParent = ownerElement->parentNode(); 1351 if (!ownerElementParent) 1352 return; 1353 1354 // 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. 1355 if (!ownerElementParent->rendererIsEditable()) 1356 return; 1357 1358 // Create compute positions before and after the element. 1359 unsigned ownerElementNodeIndex = ownerElement->nodeIndex(); 1360 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor))); 1361 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE)); 1362 1363 // Focus on the parent frame, and then select from before this element to after. 1364 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement); 1365 page->focusController().setFocusedFrame(parent); 1366 toLocalFrame(parent)->selection().setSelection(newSelection); 1367 } 1368 1369 void FrameSelection::selectAll() 1370 { 1371 Document* document = m_frame->document(); 1372 1373 if (isHTMLSelectElement(document->focusedElement())) { 1374 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement()); 1375 if (selectElement->canSelectAll()) { 1376 selectElement->selectAll(); 1377 return; 1378 } 1379 } 1380 1381 RefPtrWillBeRawPtr<Node> root = nullptr; 1382 Node* selectStartTarget = 0; 1383 if (isContentEditable()) { 1384 root = highestEditableRoot(m_selection.start()); 1385 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode()) 1386 selectStartTarget = shadowRoot->shadowHost(); 1387 else 1388 selectStartTarget = root.get(); 1389 } else { 1390 root = m_selection.nonBoundaryShadowTreeRootNode(); 1391 if (root) 1392 selectStartTarget = root->shadowHost(); 1393 else { 1394 root = document->documentElement(); 1395 selectStartTarget = document->body(); 1396 } 1397 } 1398 if (!root) 1399 return; 1400 1401 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart))) 1402 return; 1403 1404 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get())); 1405 setSelection(newSelection); 1406 selectFrameElementInParentIfFullySelected(); 1407 notifyRendererOfSelectionChange(UserTriggered); 1408 } 1409 1410 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options) 1411 { 1412 if (!range || !range->startContainer() || !range->endContainer()) 1413 return false; 1414 ASSERT(range->startContainer()->document() == range->endContainer()->document()); 1415 1416 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped, 1417 // they start at the beginning of the next line instead 1418 m_logicalRange = nullptr; 1419 stopObservingVisibleSelectionChangeIfNecessary(); 1420 1421 VisibleSelection newSelection(range, affinity, directional == Directional); 1422 setSelection(newSelection, options); 1423 1424 m_logicalRange = range->cloneRange(); 1425 startObservingVisibleSelectionChange(); 1426 1427 return true; 1428 } 1429 1430 PassRefPtrWillBeRawPtr<Range> FrameSelection::firstRange() const 1431 { 1432 if (m_logicalRange) 1433 return m_logicalRange->cloneRange(); 1434 return m_selection.firstRange(); 1435 } 1436 1437 bool FrameSelection::isInPasswordField() const 1438 { 1439 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()); 1440 return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField(); 1441 } 1442 1443 void FrameSelection::notifyAccessibilityForSelectionChange() 1444 { 1445 if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) { 1446 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache()) 1447 cache->selectionChanged(m_selection.start().containerNode()); 1448 } 1449 } 1450 1451 void FrameSelection::focusedOrActiveStateChanged() 1452 { 1453 bool activeAndFocused = isFocusedAndActive(); 1454 1455 RefPtrWillBeRawPtr<Document> document = m_frame->document(); 1456 document->updateRenderTreeIfNeeded(); 1457 1458 // Because RenderObject::selectionBackgroundColor() and 1459 // RenderObject::selectionForegroundColor() check if the frame is active, 1460 // we have to update places those colors were painted. 1461 if (RenderView* view = document->renderView()) 1462 view->repaintSelection(); 1463 1464 // Caret appears in the active frame. 1465 if (activeAndFocused) 1466 setSelectionFromNone(); 1467 else 1468 m_frame->spellChecker().spellCheckAfterBlur(); 1469 setCaretVisibility(activeAndFocused ? Visible : Hidden); 1470 1471 // Update for caps lock state 1472 m_frame->eventHandler().capsLockStateMayHaveChanged(); 1473 1474 // We may have lost active status even though the focusElement hasn't changed 1475 // give the element a chance to recalc style if its affected by focus. 1476 if (Element* element = document->focusedElement()) 1477 element->focusStateChanged(); 1478 1479 // Secure keyboard entry is set by the active frame. 1480 if (document->useSecureKeyboardEntryWhenActive()) 1481 setUseSecureKeyboardEntry(activeAndFocused); 1482 } 1483 1484 void FrameSelection::pageActivationChanged() 1485 { 1486 focusedOrActiveStateChanged(); 1487 } 1488 1489 void FrameSelection::updateSecureKeyboardEntryIfActive() 1490 { 1491 if (m_frame->document() && isFocusedAndActive()) 1492 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive()); 1493 } 1494 1495 void FrameSelection::setUseSecureKeyboardEntry(bool enable) 1496 { 1497 if (enable) 1498 enableSecureTextInput(); 1499 else 1500 disableSecureTextInput(); 1501 } 1502 1503 void FrameSelection::setFocused(bool flag) 1504 { 1505 if (m_focused == flag) 1506 return; 1507 m_focused = flag; 1508 1509 focusedOrActiveStateChanged(); 1510 } 1511 1512 bool FrameSelection::isFocusedAndActive() const 1513 { 1514 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive(); 1515 } 1516 1517 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame) 1518 { 1519 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking(); 1520 } 1521 1522 void FrameSelection::updateAppearance() 1523 { 1524 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case 1525 // the FrameSelection will paint a blinking caret as usual). 1526 VisiblePosition forwardPosition; 1527 if (m_shouldShowBlockCursor && m_selection.isCaret()) { 1528 forwardPosition = modifyExtendingForward(CharacterGranularity); 1529 m_caretPaint = forwardPosition.isNull(); 1530 } 1531 1532 bool caretRectChangedOrCleared = recomputeCaretRect(); 1533 bool shouldBlink = shouldBlinkCaret() && forwardPosition.isNull(); 1534 1535 // If the caret moved, stop the blink timer so we can restart with a 1536 // black caret in the new location. 1537 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) { 1538 m_caretBlinkTimer.stop(); 1539 if (!shouldBlink && m_caretPaint) { 1540 m_caretPaint = false; 1541 invalidateCaretRect(); 1542 } 1543 } 1544 1545 // Start blinking with a black caret. Be sure not to restart if we're 1546 // already blinking in the right location. 1547 if (shouldBlink && !m_caretBlinkTimer.isActive()) { 1548 if (double blinkInterval = RenderTheme::theme().caretBlinkInterval()) 1549 m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE); 1550 1551 if (!m_caretPaint) { 1552 m_caretPaint = true; 1553 invalidateCaretRect(); 1554 } 1555 } 1556 1557 RenderView* view = m_frame->contentRenderer(); 1558 if (!view) 1559 return; 1560 1561 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps 1562 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>. 1563 VisibleSelection selection(m_selection.visibleStart(), forwardPosition.isNotNull() ? forwardPosition : m_selection.visibleEnd()); 1564 1565 if (!selection.isRange()) { 1566 view->clearSelection(); 1567 return; 1568 } 1569 1570 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection. 1571 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3] 1572 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected 1573 // and will fill the gap before 'bar'. 1574 Position startPos = selection.start(); 1575 Position candidate = startPos.downstream(); 1576 if (candidate.isCandidate()) 1577 startPos = candidate; 1578 Position endPos = selection.end(); 1579 candidate = endPos.upstream(); 1580 if (candidate.isCandidate()) 1581 endPos = candidate; 1582 1583 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted 1584 // because we don't yet notify the FrameSelection of text removal. 1585 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) { 1586 RenderObject* startRenderer = startPos.deprecatedNode()->renderer(); 1587 RenderObject* endRenderer = endPos.deprecatedNode()->renderer(); 1588 if (startRenderer->view() == view && endRenderer->view() == view) 1589 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset()); 1590 } 1591 } 1592 1593 void FrameSelection::setCaretVisibility(CaretVisibility visibility) 1594 { 1595 if (caretVisibility() == visibility) 1596 return; 1597 1598 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1599 if (m_caretPaint) { 1600 m_caretPaint = false; 1601 invalidateCaretRect(); 1602 } 1603 CaretBase::setCaretVisibility(visibility); 1604 1605 updateAppearance(); 1606 } 1607 1608 bool FrameSelection::shouldBlinkCaret() const 1609 { 1610 if (!caretIsVisible() || !isCaret()) 1611 return false; 1612 1613 if (m_frame->settings() && m_frame->settings()->caretBrowsingEnabled()) 1614 return false; 1615 1616 Node* root = rootEditableElement(); 1617 if (!root) 1618 return false; 1619 1620 Element* focusedElement = root->document().focusedElement(); 1621 if (!focusedElement) 1622 return false; 1623 1624 return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode()); 1625 } 1626 1627 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*) 1628 { 1629 ASSERT(caretIsVisible()); 1630 ASSERT(isCaret()); 1631 bool caretPaint = m_caretPaint; 1632 if (isCaretBlinkingSuspended() && caretPaint) 1633 return; 1634 m_caretPaint = !caretPaint; 1635 invalidateCaretRect(); 1636 } 1637 1638 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered) 1639 { 1640 m_frame->document()->updateRenderTreeIfNeeded(); 1641 1642 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start())) 1643 textControl->selectionChanged(userTriggered == UserTriggered); 1644 } 1645 1646 // Helper function that tells whether a particular node is an element that has an entire 1647 // LocalFrame and FrameView, a <frame>, <iframe>, or <object>. 1648 static bool isFrameElement(const Node* n) 1649 { 1650 if (!n) 1651 return false; 1652 RenderObject* renderer = n->renderer(); 1653 if (!renderer || !renderer->isWidget()) 1654 return false; 1655 Widget* widget = toRenderWidget(renderer)->widget(); 1656 return widget && widget->isFrameView(); 1657 } 1658 1659 void FrameSelection::setFocusedNodeIfNeeded() 1660 { 1661 if (isNone() || !isFocused()) 1662 return; 1663 1664 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); 1665 if (caretBrowsing) { 1666 if (Element* anchor = enclosingAnchorElement(base())) { 1667 m_frame->page()->focusController().setFocusedElement(anchor, m_frame); 1668 return; 1669 } 1670 } 1671 1672 if (Element* target = rootEditableElement()) { 1673 // Walk up the DOM tree to search for a node to focus. 1674 while (target) { 1675 // We don't want to set focus on a subframe when selecting in a parent frame, 1676 // so add the !isFrameElement check here. There's probably a better way to make this 1677 // work in the long term, but this is the safest fix at this time. 1678 if (target->isMouseFocusable() && !isFrameElement(target)) { 1679 m_frame->page()->focusController().setFocusedElement(target, m_frame); 1680 return; 1681 } 1682 target = target->parentOrShadowHostElement(); 1683 } 1684 m_frame->document()->setFocusedElement(nullptr); 1685 } 1686 1687 if (caretBrowsing) 1688 m_frame->page()->focusController().setFocusedElement(0, m_frame); 1689 } 1690 1691 static String extractSelectedText(const FrameSelection& selection, TextIteratorBehavior behavior) 1692 { 1693 // We remove '\0' characters because they are not visibly rendered to the user. 1694 return plainText(selection.toNormalizedRange().get(), behavior).replace(0, ""); 1695 } 1696 1697 String FrameSelection::selectedText() const 1698 { 1699 return extractSelectedText(*this, TextIteratorDefaultBehavior); 1700 } 1701 1702 String FrameSelection::selectedTextForClipboard() const 1703 { 1704 if (m_frame->settings() && m_frame->settings()->selectionIncludesAltImageText()) 1705 return extractSelectedText(*this, TextIteratorEmitsImageAltText); 1706 return selectedText(); 1707 } 1708 1709 FloatRect FrameSelection::bounds(bool clipToVisibleContent) const 1710 { 1711 m_frame->document()->updateRenderTreeIfNeeded(); 1712 1713 FrameView* view = m_frame->view(); 1714 RenderView* renderView = m_frame->contentRenderer(); 1715 1716 if (!view || !renderView) 1717 return FloatRect(); 1718 1719 LayoutRect selectionRect = renderView->selectionBounds(clipToVisibleContent); 1720 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect; 1721 } 1722 1723 static inline HTMLFormElement* associatedFormElement(HTMLElement& element) 1724 { 1725 if (isHTMLFormElement(element)) 1726 return &toHTMLFormElement(element); 1727 return element.formOwner(); 1728 } 1729 1730 // Scans logically forward from "start", including any child frames. 1731 static HTMLFormElement* scanForForm(Node* start) 1732 { 1733 if (!start) 1734 return 0; 1735 1736 HTMLElement* element = start->isHTMLElement() ? toHTMLElement(start) : Traversal<HTMLElement>::next(*start); 1737 for (; element; element = Traversal<HTMLElement>::next(*element)) { 1738 if (HTMLFormElement* form = associatedFormElement(*element)) 1739 return form; 1740 1741 if (isHTMLFrameElementBase(*element)) { 1742 Node* childDocument = toHTMLFrameElementBase(*element).contentDocument(); 1743 if (HTMLFormElement* frameResult = scanForForm(childDocument)) 1744 return frameResult; 1745 } 1746 } 1747 return 0; 1748 } 1749 1750 // We look for either the form containing the current focus, or for one immediately after it 1751 HTMLFormElement* FrameSelection::currentForm() const 1752 { 1753 // Start looking either at the active (first responder) node, or where the selection is. 1754 Node* start = m_frame->document()->focusedElement(); 1755 if (!start) 1756 start = this->start().deprecatedNode(); 1757 if (!start) 1758 return 0; 1759 1760 // Try walking up the node tree to find a form element. 1761 for (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*start); element; element = Traversal<HTMLElement>::firstAncestor(*element)) { 1762 if (HTMLFormElement* form = associatedFormElement(*element)) 1763 return form; 1764 } 1765 1766 // Try walking forward in the node tree to find a form element. 1767 return scanForForm(start); 1768 } 1769 1770 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption) 1771 { 1772 LayoutRect rect; 1773 1774 switch (selectionType()) { 1775 case NoSelection: 1776 return; 1777 case CaretSelection: 1778 rect = absoluteCaretBounds(); 1779 break; 1780 case RangeSelection: 1781 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false)); 1782 break; 1783 } 1784 1785 Position start = this->start(); 1786 ASSERT(start.deprecatedNode()); 1787 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) { 1788 // FIXME: This code only handles scrolling the startContainer's layer, but 1789 // the selection rect could intersect more than just that. 1790 // See <rdar://problem/4799899>. 1791 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment)) 1792 updateAppearance(); 1793 } 1794 } 1795 1796 void FrameSelection::setSelectionFromNone() 1797 { 1798 // Put a caret inside the body if the entire frame is editable (either the 1799 // entire WebView is editable or designMode is on for this document). 1800 1801 Document* document = m_frame->document(); 1802 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); 1803 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing)) 1804 return; 1805 1806 Node* node = document->documentElement(); 1807 if (!node) 1808 return; 1809 Node* body = isHTMLBodyElement(*node) ? node : Traversal<HTMLBodyElement>::next(*node); 1810 if (body) 1811 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM)); 1812 } 1813 1814 bool FrameSelection::dispatchSelectStart() 1815 { 1816 Node* selectStartTarget = m_selection.extent().containerNode(); 1817 if (!selectStartTarget) 1818 return true; 1819 1820 return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)); 1821 } 1822 1823 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor) 1824 { 1825 m_shouldShowBlockCursor = shouldShowBlockCursor; 1826 1827 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1828 1829 updateAppearance(); 1830 } 1831 1832 void FrameSelection::didChangeVisibleSelection() 1833 { 1834 ASSERT(m_observingVisibleSelection); 1835 // Invalidate the logical range when the underlying VisibleSelection has changed. 1836 m_logicalRange = nullptr; 1837 m_selection.clearChangeObserver(); 1838 m_observingVisibleSelection = false; 1839 } 1840 1841 VisibleSelection FrameSelection::validateSelection(const VisibleSelection& selection) 1842 { 1843 if (!m_frame || selection.isNone()) 1844 return selection; 1845 1846 Position base = selection.base(); 1847 Position extent = selection.extent(); 1848 bool isBaseValid = base.document() == m_frame->document(); 1849 bool isExtentValid = extent.document() == m_frame->document(); 1850 1851 if (isBaseValid && isExtentValid) 1852 return selection; 1853 1854 VisibleSelection newSelection; 1855 if (isBaseValid) { 1856 newSelection.setWithoutValidation(base, base); 1857 } else if (isExtentValid) { 1858 newSelection.setWithoutValidation(extent, extent); 1859 } 1860 return newSelection; 1861 } 1862 1863 void FrameSelection::startObservingVisibleSelectionChange() 1864 { 1865 ASSERT(!m_observingVisibleSelection); 1866 m_selection.setChangeObserver(*this); 1867 m_observingVisibleSelection = true; 1868 } 1869 1870 void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary() 1871 { 1872 if (m_observingVisibleSelection) { 1873 m_selection.clearChangeObserver(); 1874 m_observingVisibleSelection = false; 1875 } 1876 } 1877 1878 #ifndef NDEBUG 1879 1880 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const 1881 { 1882 m_selection.formatForDebugger(buffer, length); 1883 } 1884 1885 void FrameSelection::showTreeForThis() const 1886 { 1887 m_selection.showTreeForThis(); 1888 } 1889 1890 #endif 1891 1892 void FrameSelection::trace(Visitor* visitor) 1893 { 1894 visitor->trace(m_selection); 1895 visitor->trace(m_originalBase); 1896 visitor->trace(m_logicalRange); 1897 visitor->trace(m_previousCaretNode); 1898 visitor->trace(m_typingStyle); 1899 VisibleSelection::ChangeObserver::trace(visitor); 1900 } 1901 1902 } 1903 1904 #ifndef NDEBUG 1905 1906 void showTree(const WebCore::FrameSelection& sel) 1907 { 1908 sel.showTreeForThis(); 1909 } 1910 1911 void showTree(const WebCore::FrameSelection* sel) 1912 { 1913 if (sel) 1914 sel->showTreeForThis(); 1915 } 1916 1917 #endif 1918