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