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