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