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