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