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 "CString.h" 30 #include "DeleteSelectionCommand.h" 31 #include "Document.h" 32 #include "Editor.h" 33 #include "Element.h" 34 #include "EventHandler.h" 35 #include "ExceptionCode.h" 36 #include "FocusController.h" 37 #include "FloatQuad.h" 38 #include "Frame.h" 39 #include "FrameTree.h" 40 #include "FrameView.h" 41 #include "GraphicsContext.h" 42 #include "HTMLInputElement.h" 43 #include "HTMLNames.h" 44 #include "HitTestRequest.h" 45 #include "HitTestResult.h" 46 #include "Page.h" 47 #include "Range.h" 48 #include "RenderTheme.h" 49 #include "RenderView.h" 50 #include "Settings.h" 51 #include "TextIterator.h" 52 #include "TypingCommand.h" 53 #include "htmlediting.h" 54 #include "visible_units.h" 55 #include <stdio.h> 56 57 #define EDIT_DEBUG 0 58 59 namespace WebCore { 60 61 using namespace HTMLNames; 62 63 const int NoXPosForVerticalArrowNavigation = INT_MIN; 64 65 SelectionController::SelectionController(Frame* frame, bool isDragCaretController) 66 : m_frame(frame) 67 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation) 68 , m_caretBlinkTimer(this, &SelectionController::caretBlinkTimerFired) 69 , m_needsLayout(true) 70 , m_absCaretBoundsDirty(true) 71 , m_lastChangeWasHorizontalExtension(false) 72 , m_isDragCaretController(isDragCaretController) 73 , m_isCaretBlinkingSuspended(false) 74 , m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame) 75 , m_caretVisible(isDragCaretController) 76 , m_caretPaint(true) 77 { 78 } 79 80 void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered) 81 { 82 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered); 83 } 84 85 void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered) 86 { 87 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), true, true, userTriggered); 88 } 89 90 void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered) 91 { 92 setSelection(VisibleSelection(pos, affinity), true, true, userTriggered); 93 } 94 95 void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered) 96 { 97 VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity); 98 setSelection(selection, true, true, userTriggered); 99 } 100 101 void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered) 102 { 103 setSelection(VisibleSelection(base, extent, affinity), true, true, userTriggered); 104 } 105 106 void SelectionController::setSelection(const VisibleSelection& s, bool closeTyping, bool clearTypingStyle, bool userTriggered) 107 { 108 m_lastChangeWasHorizontalExtension = false; 109 110 if (m_isDragCaretController) { 111 invalidateCaretRect(); 112 m_selection = s; 113 m_needsLayout = true; 114 invalidateCaretRect(); 115 return; 116 } 117 if (!m_frame) { 118 m_selection = s; 119 return; 120 } 121 122 Node* baseNode = s.base().node(); 123 Document* document = 0; 124 if (baseNode) 125 document = baseNode->document(); 126 127 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection 128 // if document->frame() == m_frame we can get into an infinite loop 129 if (document && document->frame() != m_frame && document != m_frame->document()) { 130 document->frame()->selection()->setSelection(s, closeTyping, clearTypingStyle, userTriggered); 131 return; 132 } 133 134 if (closeTyping) 135 TypingCommand::closeTyping(m_frame->editor()->lastEditCommand()); 136 137 if (clearTypingStyle) 138 m_frame->clearTypingStyle(); 139 140 if (m_selection == s) 141 return; 142 143 VisibleSelection oldSelection = m_selection; 144 145 m_selection = s; 146 147 m_needsLayout = true; 148 149 if (!s.isNone()) 150 m_frame->setFocusedNodeIfNeeded(); 151 152 updateAppearance(); 153 154 // Always clear the x position used for vertical arrow navigation. 155 // It will be restored by the vertical arrow navigation code if necessary. 156 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation; 157 selectFrameElementInParentIfFullySelected(); 158 m_frame->notifyRendererOfSelectionChange(userTriggered); 159 m_frame->respondToChangedSelection(oldSelection, closeTyping); 160 if (userTriggered) 161 m_frame->revealSelection(ScrollAlignment::alignToEdgeIfNeeded, true); 162 163 notifyAccessibilityForSelectionChange(); 164 } 165 166 static bool removingNodeRemovesPosition(Node* node, const Position& position) 167 { 168 if (!position.node()) 169 return false; 170 171 if (position.node() == node) 172 return true; 173 174 if (!node->isElementNode()) 175 return false; 176 177 Element* element = static_cast<Element*>(node); 178 return element->contains(position.node()) || element->contains(position.node()->shadowAncestorNode()); 179 } 180 181 void SelectionController::nodeWillBeRemoved(Node *node) 182 { 183 if (isNone()) 184 return; 185 186 // There can't be a selection inside a fragment, so if a fragment's node is being removed, 187 // the selection in the document that created the fragment needs no adjustment. 188 if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) 189 return; 190 191 bool baseRemoved = removingNodeRemovesPosition(node, m_selection.base()); 192 bool extentRemoved = removingNodeRemovesPosition(node, m_selection.extent()); 193 bool startRemoved = removingNodeRemovesPosition(node, m_selection.start()); 194 bool endRemoved = removingNodeRemovesPosition(node, m_selection.end()); 195 196 bool clearRenderTreeSelection = false; 197 bool clearDOMTreeSelection = false; 198 199 if (startRemoved || endRemoved) { 200 // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away. 201 clearRenderTreeSelection = true; 202 clearDOMTreeSelection = true; 203 } else if (baseRemoved || extentRemoved) { 204 // The base and/or extent are about to be removed, but the start and end aren't. 205 // Change the base and extent to the start and end, but don't re-validate the 206 // selection, since doing so could move the start and end into the node 207 // that is about to be removed. 208 if (m_selection.isBaseFirst()) 209 m_selection.setWithoutValidation(m_selection.start(), m_selection.end()); 210 else 211 m_selection.setWithoutValidation(m_selection.end(), m_selection.start()); 212 // FIXME: This could be more efficient if we had an isNodeInRange function on Ranges. 213 } else if (comparePositions(m_selection.start(), Position(node, 0)) == -1 && comparePositions(m_selection.end(), Position(node, 0)) == 1) { 214 // If we did nothing here, when this node's renderer was destroyed, the rect that it 215 // occupied would be invalidated, but, selection gaps that change as a result of 216 // the removal wouldn't be invalidated. 217 // FIXME: Don't do so much unnecessary invalidation. 218 clearRenderTreeSelection = true; 219 } 220 221 if (clearRenderTreeSelection) { 222 RefPtr<Document> document = m_selection.start().node()->document(); 223 document->updateStyleIfNeeded(); 224 if (RenderView* view = toRenderView(document->renderer())) 225 view->clearSelection(); 226 } 227 228 if (clearDOMTreeSelection) 229 setSelection(VisibleSelection(), false, false); 230 } 231 232 void SelectionController::willBeModified(EAlteration alter, EDirection direction) 233 { 234 if (alter != EXTEND) 235 return; 236 if (m_lastChangeWasHorizontalExtension) 237 return; 238 239 Position start = m_selection.start(); 240 Position end = m_selection.end(); 241 // FIXME: This is probably not correct for right and left when the direction is RTL. 242 switch (direction) { 243 case RIGHT: 244 case FORWARD: 245 m_selection.setBase(start); 246 m_selection.setExtent(end); 247 break; 248 case LEFT: 249 case BACKWARD: 250 m_selection.setBase(end); 251 m_selection.setExtent(start); 252 break; 253 } 254 } 255 256 TextDirection SelectionController::directionOfEnclosingBlock() 257 { 258 Node* n = m_selection.extent().node(); 259 Node* enclosingBlockNode = enclosingBlock(n); 260 if (!enclosingBlockNode) 261 return LTR; 262 RenderObject* renderer = enclosingBlockNode->renderer(); 263 if (renderer) 264 return renderer->style()->direction(); 265 return LTR; 266 } 267 268 VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granularity) 269 { 270 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 271 272 // The difference between modifyExtendingRight and modifyExtendingForward is: 273 // modifyExtendingForward always extends forward logically. 274 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word, 275 // it extends forward logically if the enclosing block is LTR direction, 276 // but it extends backward logically if the enclosing block is RTL direction. 277 switch (granularity) { 278 case CharacterGranularity: 279 if (directionOfEnclosingBlock() == LTR) 280 pos = pos.next(true); 281 else 282 pos = pos.previous(true); 283 break; 284 case WordGranularity: 285 if (directionOfEnclosingBlock() == LTR) 286 pos = nextWordPosition(pos); 287 else 288 pos = previousWordPosition(pos); 289 break; 290 case SentenceGranularity: 291 case LineGranularity: 292 case ParagraphGranularity: 293 case SentenceBoundary: 294 case LineBoundary: 295 case ParagraphBoundary: 296 case DocumentBoundary: 297 // FIXME: implement all of the above? 298 pos = modifyExtendingForward(granularity); 299 } 300 return pos; 301 } 302 303 VisiblePosition SelectionController::modifyExtendingForward(TextGranularity granularity) 304 { 305 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 306 switch (granularity) { 307 case CharacterGranularity: 308 pos = pos.next(true); 309 break; 310 case WordGranularity: 311 pos = nextWordPosition(pos); 312 break; 313 case SentenceGranularity: 314 pos = nextSentencePosition(pos); 315 break; 316 case LineGranularity: 317 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); 318 break; 319 case ParagraphGranularity: 320 pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); 321 break; 322 case SentenceBoundary: 323 pos = endOfSentence(VisiblePosition(m_selection.end(), m_selection.affinity())); 324 break; 325 case LineBoundary: 326 pos = logicalEndOfLine(VisiblePosition(m_selection.end(), m_selection.affinity())); 327 break; 328 case ParagraphBoundary: 329 pos = endOfParagraph(VisiblePosition(m_selection.end(), m_selection.affinity())); 330 break; 331 case DocumentBoundary: 332 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 333 if (isEditablePosition(pos.deepEquivalent())) 334 pos = endOfEditableContent(pos); 335 else 336 pos = endOfDocument(pos); 337 break; 338 } 339 340 return pos; 341 } 342 343 VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity) 344 { 345 VisiblePosition pos; 346 switch (granularity) { 347 case CharacterGranularity: 348 if (isRange()) 349 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 350 else 351 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true); 352 break; 353 case WordGranularity: 354 case SentenceGranularity: 355 case LineGranularity: 356 case ParagraphGranularity: 357 case SentenceBoundary: 358 case LineBoundary: 359 case ParagraphBoundary: 360 case DocumentBoundary: 361 // FIXME: Implement all of the above. 362 pos = modifyMovingForward(granularity); 363 break; 364 } 365 return pos; 366 } 367 368 VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity) 369 { 370 VisiblePosition pos; 371 // FIXME: Stay in editable content for the less common granularities. 372 switch (granularity) { 373 case CharacterGranularity: 374 if (isRange()) 375 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 376 else 377 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(true); 378 break; 379 case WordGranularity: 380 pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 381 break; 382 case SentenceGranularity: 383 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 384 break; 385 case LineGranularity: { 386 // down-arrowing from a range selection that ends at the start of a line needs 387 // to leave the selection at that line start (no need to call nextLinePosition!) 388 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 389 if (!isRange() || !isStartOfLine(pos)) 390 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START)); 391 break; 392 } 393 case ParagraphGranularity: 394 pos = nextParagraphPosition(VisiblePosition(m_selection.end(), m_selection.affinity()), xPosForVerticalArrowNavigation(START)); 395 break; 396 case SentenceBoundary: 397 pos = endOfSentence(VisiblePosition(m_selection.end(), m_selection.affinity())); 398 break; 399 case LineBoundary: 400 pos = logicalEndOfLine(VisiblePosition(m_selection.end(), m_selection.affinity())); 401 break; 402 case ParagraphBoundary: 403 pos = endOfParagraph(VisiblePosition(m_selection.end(), m_selection.affinity())); 404 break; 405 case DocumentBoundary: 406 pos = VisiblePosition(m_selection.end(), m_selection.affinity()); 407 if (isEditablePosition(pos.deepEquivalent())) 408 pos = endOfEditableContent(pos); 409 else 410 pos = endOfDocument(pos); 411 break; 412 413 } 414 return pos; 415 } 416 417 VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granularity) 418 { 419 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 420 421 // The difference between modifyExtendingLeft and modifyExtendingBackward is: 422 // modifyExtendingBackward always extends backward logically. 423 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word, 424 // it extends backward logically if the enclosing block is LTR direction, 425 // but it extends forward logically if the enclosing block is RTL direction. 426 switch (granularity) { 427 case CharacterGranularity: 428 if (directionOfEnclosingBlock() == LTR) 429 pos = pos.previous(true); 430 else 431 pos = pos.next(true); 432 break; 433 case WordGranularity: 434 if (directionOfEnclosingBlock() == LTR) 435 pos = previousWordPosition(pos); 436 else 437 pos = nextWordPosition(pos); 438 break; 439 case SentenceGranularity: 440 case LineGranularity: 441 case ParagraphGranularity: 442 case SentenceBoundary: 443 case LineBoundary: 444 case ParagraphBoundary: 445 case DocumentBoundary: 446 pos = modifyExtendingBackward(granularity); 447 } 448 return pos; 449 } 450 451 VisiblePosition SelectionController::modifyExtendingBackward(TextGranularity granularity) 452 { 453 VisiblePosition pos(m_selection.extent(), m_selection.affinity()); 454 455 // Extending a selection backward by word or character from just after a table selects 456 // the table. This "makes sense" from the user perspective, esp. when deleting. 457 // It was done here instead of in VisiblePosition because we want VPs to iterate 458 // over everything. 459 switch (granularity) { 460 case CharacterGranularity: 461 pos = pos.previous(true); 462 break; 463 case WordGranularity: 464 pos = previousWordPosition(pos); 465 break; 466 case SentenceGranularity: 467 pos = previousSentencePosition(pos); 468 break; 469 case LineGranularity: 470 pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT)); 471 break; 472 case ParagraphGranularity: 473 pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT)); 474 break; 475 case SentenceBoundary: 476 pos = startOfSentence(VisiblePosition(m_selection.start(), m_selection.affinity())); 477 break; 478 case LineBoundary: 479 pos = logicalStartOfLine(VisiblePosition(m_selection.start(), m_selection.affinity())); 480 break; 481 case ParagraphBoundary: 482 pos = startOfParagraph(VisiblePosition(m_selection.start(), m_selection.affinity())); 483 break; 484 case DocumentBoundary: 485 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 486 if (isEditablePosition(pos.deepEquivalent())) 487 pos = startOfEditableContent(pos); 488 else 489 pos = startOfDocument(pos); 490 break; 491 } 492 return pos; 493 } 494 495 VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity) 496 { 497 VisiblePosition pos; 498 switch (granularity) { 499 case CharacterGranularity: 500 if (isRange()) 501 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 502 else 503 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true); 504 break; 505 case WordGranularity: 506 case SentenceGranularity: 507 case LineGranularity: 508 case ParagraphGranularity: 509 case SentenceBoundary: 510 case LineBoundary: 511 case ParagraphBoundary: 512 case DocumentBoundary: 513 // FIXME: Implement all of the above. 514 pos = modifyMovingBackward(granularity); 515 break; 516 } 517 return pos; 518 } 519 520 VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity) 521 { 522 VisiblePosition pos; 523 switch (granularity) { 524 case CharacterGranularity: 525 if (isRange()) 526 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 527 else 528 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(true); 529 break; 530 case WordGranularity: 531 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 532 break; 533 case SentenceGranularity: 534 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity())); 535 break; 536 case LineGranularity: 537 pos = previousLinePosition(VisiblePosition(m_selection.start(), m_selection.affinity()), xPosForVerticalArrowNavigation(START)); 538 break; 539 case ParagraphGranularity: 540 pos = previousParagraphPosition(VisiblePosition(m_selection.start(), m_selection.affinity()), xPosForVerticalArrowNavigation(START)); 541 break; 542 case SentenceBoundary: 543 pos = startOfSentence(VisiblePosition(m_selection.start(), m_selection.affinity())); 544 break; 545 case LineBoundary: 546 pos = logicalStartOfLine(VisiblePosition(m_selection.start(), m_selection.affinity())); 547 break; 548 case ParagraphBoundary: 549 pos = startOfParagraph(VisiblePosition(m_selection.start(), m_selection.affinity())); 550 break; 551 case DocumentBoundary: 552 pos = VisiblePosition(m_selection.start(), m_selection.affinity()); 553 if (isEditablePosition(pos.deepEquivalent())) 554 pos = startOfEditableContent(pos); 555 else 556 pos = startOfDocument(pos); 557 break; 558 } 559 return pos; 560 } 561 562 bool SelectionController::modify(EAlteration alter, EDirection dir, TextGranularity granularity, bool userTriggered) 563 { 564 if (userTriggered) { 565 SelectionController trialSelectionController; 566 trialSelectionController.setSelection(m_selection); 567 trialSelectionController.setLastChangeWasHorizontalExtension(m_lastChangeWasHorizontalExtension); 568 trialSelectionController.modify(alter, dir, granularity, false); 569 570 bool change = m_frame->shouldChangeSelection(trialSelectionController.selection()); 571 if (!change) 572 return false; 573 } 574 575 if (m_frame) 576 m_frame->setSelectionGranularity(granularity); 577 578 willBeModified(alter, dir); 579 580 VisiblePosition pos; 581 switch (dir) { 582 case RIGHT: 583 if (alter == MOVE) 584 pos = modifyMovingRight(granularity); 585 else 586 pos = modifyExtendingRight(granularity); 587 break; 588 case FORWARD: 589 if (alter == EXTEND) 590 pos = modifyExtendingForward(granularity); 591 else 592 pos = modifyMovingForward(granularity); 593 break; 594 case LEFT: 595 if (alter == MOVE) 596 pos = modifyMovingLeft(granularity); 597 else 598 pos = modifyExtendingLeft(granularity); 599 break; 600 case BACKWARD: 601 if (alter == EXTEND) 602 pos = modifyExtendingBackward(granularity); 603 else 604 pos = modifyMovingBackward(granularity); 605 break; 606 } 607 608 if (pos.isNull()) 609 return false; 610 611 // Some of the above operations set an xPosForVerticalArrowNavigation. 612 // Setting a selection will clear it, so save it to possibly restore later. 613 // Note: the START position type is arbitrary because it is unused, it would be 614 // the requested position type if there were no xPosForVerticalArrowNavigation set. 615 int x = xPosForVerticalArrowNavigation(START); 616 617 switch (alter) { 618 case MOVE: 619 moveTo(pos, userTriggered); 620 break; 621 case EXTEND: 622 setExtent(pos, userTriggered); 623 break; 624 } 625 626 if (granularity == LineGranularity || granularity == ParagraphGranularity) 627 m_xPosForVerticalArrowNavigation = x; 628 629 if (userTriggered) { 630 // User modified selection change also sets the granularity back to character. 631 // NOTE: The one exception is that we need to keep word granularity to 632 // preserve smart delete behavior when extending by word (e.g. double-click), 633 // then shift-option-right arrow, then delete needs to smart delete, per TextEdit. 634 if (!(alter == EXTEND && granularity == WordGranularity && m_frame->selectionGranularity() == WordGranularity)) 635 m_frame->setSelectionGranularity(CharacterGranularity); 636 } 637 638 setNeedsLayout(); 639 640 m_lastChangeWasHorizontalExtension = alter == EXTEND; 641 642 return true; 643 } 644 645 // FIXME: Maybe baseline would be better? 646 static bool absoluteCaretY(const VisiblePosition &c, int &y) 647 { 648 IntRect rect = c.absoluteCaretBounds(); 649 if (rect.isEmpty()) 650 return false; 651 y = rect.y() + rect.height() / 2; 652 return true; 653 } 654 655 bool SelectionController::modify(EAlteration alter, int verticalDistance, bool userTriggered) 656 { 657 if (verticalDistance == 0) 658 return false; 659 660 if (userTriggered) { 661 SelectionController trialSelectionController; 662 trialSelectionController.setSelection(m_selection); 663 trialSelectionController.setLastChangeWasHorizontalExtension(m_lastChangeWasHorizontalExtension); 664 trialSelectionController.modify(alter, verticalDistance, false); 665 666 bool change = m_frame->shouldChangeSelection(trialSelectionController.selection()); 667 if (!change) 668 return false; 669 } 670 671 bool up = verticalDistance < 0; 672 if (up) 673 verticalDistance = -verticalDistance; 674 675 willBeModified(alter, up ? BACKWARD : FORWARD); 676 677 VisiblePosition pos; 678 int xPos = 0; 679 switch (alter) { 680 case MOVE: 681 pos = VisiblePosition(up ? m_selection.start() : m_selection.end(), m_selection.affinity()); 682 xPos = xPosForVerticalArrowNavigation(up ? START : END); 683 m_selection.setAffinity(up ? UPSTREAM : DOWNSTREAM); 684 break; 685 case EXTEND: 686 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()); 687 xPos = xPosForVerticalArrowNavigation(EXTENT); 688 m_selection.setAffinity(DOWNSTREAM); 689 break; 690 } 691 692 int startY; 693 if (!absoluteCaretY(pos, startY)) 694 return false; 695 if (up) 696 startY = -startY; 697 int lastY = startY; 698 699 VisiblePosition result; 700 VisiblePosition next; 701 for (VisiblePosition p = pos; ; p = next) { 702 next = (up ? previousLinePosition : nextLinePosition)(p, xPos); 703 if (next.isNull() || next == p) 704 break; 705 int nextY; 706 if (!absoluteCaretY(next, nextY)) 707 break; 708 if (up) 709 nextY = -nextY; 710 if (nextY - startY > verticalDistance) 711 break; 712 if (nextY >= lastY) { 713 lastY = nextY; 714 result = next; 715 } 716 } 717 718 if (result.isNull()) 719 return false; 720 721 switch (alter) { 722 case MOVE: 723 moveTo(result, userTriggered); 724 break; 725 case EXTEND: 726 setExtent(result, userTriggered); 727 break; 728 } 729 730 if (userTriggered) 731 m_frame->setSelectionGranularity(CharacterGranularity); 732 733 m_lastChangeWasHorizontalExtension = alter == EXTEND; 734 735 return true; 736 } 737 738 bool SelectionController::expandUsingGranularity(TextGranularity granularity) 739 { 740 if (isNone()) 741 return false; 742 743 m_selection.expandUsingGranularity(granularity); 744 m_needsLayout = true; 745 return true; 746 } 747 748 int SelectionController::xPosForVerticalArrowNavigation(EPositionType type) 749 { 750 int x = 0; 751 752 if (isNone()) 753 return x; 754 755 Position pos; 756 switch (type) { 757 case START: 758 pos = m_selection.start(); 759 break; 760 case END: 761 pos = m_selection.end(); 762 break; 763 case BASE: 764 pos = m_selection.base(); 765 break; 766 case EXTENT: 767 pos = m_selection.extent(); 768 break; 769 } 770 771 Frame *frame = pos.node()->document()->frame(); 772 if (!frame) 773 return x; 774 775 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) { 776 VisiblePosition visiblePosition(pos, m_selection.affinity()); 777 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden 778 // after the selection is created and before this function is called. 779 x = visiblePosition.isNotNull() ? visiblePosition.xOffsetForVerticalNavigation() : 0; 780 m_xPosForVerticalArrowNavigation = x; 781 } 782 else 783 x = m_xPosForVerticalArrowNavigation; 784 785 return x; 786 } 787 788 void SelectionController::clear() 789 { 790 setSelection(VisibleSelection()); 791 } 792 793 void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered) 794 { 795 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), true, true, userTriggered); 796 } 797 798 void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered) 799 { 800 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), true, true, userTriggered); 801 } 802 803 void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered) 804 { 805 setSelection(VisibleSelection(pos, m_selection.extent(), affinity), true, true, userTriggered); 806 } 807 808 void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered) 809 { 810 setSelection(VisibleSelection(m_selection.base(), pos, affinity), true, true, userTriggered); 811 } 812 813 void SelectionController::setNeedsLayout(bool flag) 814 { 815 m_needsLayout = flag; 816 } 817 818 void SelectionController::layout() 819 { 820 if (isNone() || !m_selection.start().node()->inDocument() || !m_selection.end().node()->inDocument()) { 821 m_caretRect = IntRect(); 822 return; 823 } 824 825 m_selection.start().node()->document()->updateStyleIfNeeded(); 826 827 m_caretRect = IntRect(); 828 829 if (isCaret()) { 830 VisiblePosition pos(m_selection.start(), m_selection.affinity()); 831 if (pos.isNotNull()) { 832 ASSERT(pos.deepEquivalent().node()->renderer()); 833 834 // First compute a rect local to the renderer at the selection start 835 RenderObject* renderer; 836 IntRect localRect = pos.localCaretRect(renderer); 837 838 // Get the renderer that will be responsible for painting the caret (which 839 // is either the renderer we just found, or one of its containers) 840 RenderObject* caretPainter = caretRenderer(); 841 842 // Compute an offset between the renderer and the caretPainter 843 IntSize offsetFromPainter; 844 bool unrooted = false; 845 while (renderer != caretPainter) { 846 RenderObject* containerObject = renderer->container(); 847 if (!containerObject) { 848 unrooted = true; 849 break; 850 } 851 offsetFromPainter += renderer->offsetFromContainer(containerObject); 852 renderer = containerObject; 853 } 854 855 if (!unrooted) { 856 // Move the caret rect to the coords of the painter 857 localRect.move(offsetFromPainter); 858 m_caretRect = localRect; 859 } 860 861 m_absCaretBoundsDirty = true; 862 } 863 } 864 865 m_needsLayout = false; 866 } 867 868 RenderObject* SelectionController::caretRenderer() const 869 { 870 Node* node = m_selection.start().node(); 871 if (!node) 872 return 0; 873 874 RenderObject* renderer = node->renderer(); 875 if (!renderer) 876 return 0; 877 878 // if caretNode is a block and caret is inside it then caret should be painted by that block 879 bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node); 880 return paintedByBlock ? renderer : renderer->containingBlock(); 881 } 882 883 IntRect SelectionController::localCaretRect() const 884 { 885 if (m_needsLayout) 886 const_cast<SelectionController*>(this)->layout(); 887 888 return m_caretRect; 889 } 890 891 IntRect SelectionController::absoluteBoundsForLocalRect(const IntRect& rect) const 892 { 893 RenderObject* caretPainter = caretRenderer(); 894 if (!caretPainter) 895 return IntRect(); 896 897 return caretPainter->localToAbsoluteQuad(FloatRect(rect)).enclosingBoundingBox(); 898 } 899 900 IntRect SelectionController::absoluteCaretBounds() 901 { 902 recomputeCaretRect(); 903 return m_absCaretBounds; 904 } 905 906 static IntRect repaintRectForCaret(IntRect caret) 907 { 908 if (caret.isEmpty()) 909 return IntRect(); 910 // Ensure that the dirty rect intersects the block that paints the caret even in the case where 911 // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>. 912 caret.inflateX(1); 913 return caret; 914 } 915 916 IntRect SelectionController::caretRepaintRect() const 917 { 918 return absoluteBoundsForLocalRect(repaintRectForCaret(localCaretRect())); 919 } 920 921 bool SelectionController::recomputeCaretRect() 922 { 923 if (!m_frame) 924 return false; 925 926 FrameView* v = m_frame->document()->view(); 927 if (!v) 928 return false; 929 930 if (!m_needsLayout) 931 return false; 932 933 IntRect oldRect = m_caretRect; 934 IntRect newRect = localCaretRect(); 935 if (oldRect == newRect && !m_absCaretBoundsDirty) 936 return false; 937 938 IntRect oldAbsCaretBounds = m_absCaretBounds; 939 // FIXME: Rename m_caretRect to m_localCaretRect. 940 m_absCaretBounds = absoluteBoundsForLocalRect(m_caretRect); 941 m_absCaretBoundsDirty = false; 942 943 if (oldAbsCaretBounds == m_absCaretBounds) 944 return false; 945 946 IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds; 947 // We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds. 948 m_absoluteCaretRepaintBounds = caretRepaintRect(); 949 950 if (RenderView* view = toRenderView(m_frame->document()->renderer())) { 951 // FIXME: make caret repainting container-aware. 952 view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false); 953 view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false); 954 } 955 956 return true; 957 } 958 959 void SelectionController::invalidateCaretRect() 960 { 961 if (!isCaret()) 962 return; 963 964 Document* d = m_selection.start().node()->document(); 965 966 // recomputeCaretRect will always return false for the drag caret, 967 // because its m_frame is always 0. 968 bool caretRectChanged = recomputeCaretRect(); 969 970 // EDIT FIXME: This is an unfortunate hack. 971 // Basically, we can't trust this layout position since we 972 // can't guarantee that the check to see if we are in unrendered 973 // content will work at this point. We may have to wait for 974 // a layout and re-render of the document to happen. So, resetting this 975 // flag will cause another caret layout to happen the first time 976 // that we try to paint the caret after this call. That one will work since 977 // it happens after the document has accounted for any editing 978 // changes which may have been done. 979 // And, we need to leave this layout here so the caret moves right 980 // away after clicking. 981 m_needsLayout = true; 982 983 if (!caretRectChanged) { 984 if (RenderView* view = toRenderView(d->renderer())) 985 view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(), false); 986 } 987 } 988 989 void SelectionController::paintCaret(GraphicsContext* context, int tx, int ty, const IntRect& clipRect) 990 { 991 #if ENABLE(TEXT_CARET) 992 if (!m_caretVisible) 993 return; 994 if (!m_caretPaint) 995 return; 996 if (!m_selection.isCaret()) 997 return; 998 999 IntRect drawingRect = localCaretRect(); 1000 drawingRect.move(tx, ty); 1001 IntRect caret = intersection(drawingRect, clipRect); 1002 if (caret.isEmpty()) 1003 return; 1004 1005 Color caretColor = Color::black; 1006 ColorSpace colorSpace = DeviceColorSpace; 1007 Element* element = rootEditableElement(); 1008 if (element && element->renderer()) { 1009 caretColor = element->renderer()->style()->color(); 1010 colorSpace = element->renderer()->style()->colorSpace(); 1011 } 1012 1013 context->fillRect(caret, caretColor, colorSpace); 1014 #endif 1015 } 1016 1017 void SelectionController::debugRenderer(RenderObject *r, bool selected) const 1018 { 1019 if (r->node()->isElementNode()) { 1020 Element *element = static_cast<Element *>(r->node()); 1021 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data()); 1022 } 1023 else if (r->isText()) { 1024 RenderText* textRenderer = toRenderText(r); 1025 if (textRenderer->textLength() == 0 || !textRenderer->firstTextBox()) { 1026 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " "); 1027 return; 1028 } 1029 1030 static const int max = 36; 1031 String text = textRenderer->text(); 1032 int textLength = text.length(); 1033 if (selected) { 1034 int offset = 0; 1035 if (r->node() == m_selection.start().node()) 1036 offset = m_selection.start().deprecatedEditingOffset(); 1037 else if (r->node() == m_selection.end().node()) 1038 offset = m_selection.end().deprecatedEditingOffset(); 1039 1040 int pos; 1041 InlineTextBox *box = textRenderer->findNextInlineTextBox(offset, pos); 1042 text = text.substring(box->start(), box->len()); 1043 1044 String show; 1045 int mid = max / 2; 1046 int caret = 0; 1047 1048 // text is shorter than max 1049 if (textLength < max) { 1050 show = text; 1051 caret = pos; 1052 } 1053 1054 // too few characters to left 1055 else if (pos - mid < 0) { 1056 show = text.left(max - 3) + "..."; 1057 caret = pos; 1058 } 1059 1060 // enough characters on each side 1061 else if (pos - mid >= 0 && pos + mid <= textLength) { 1062 show = "..." + text.substring(pos - mid + 3, max - 6) + "..."; 1063 caret = mid; 1064 } 1065 1066 // too few characters on right 1067 else { 1068 show = "..." + text.right(max - 3); 1069 caret = pos - (textLength - show.length()); 1070 } 1071 1072 show.replace('\n', ' '); 1073 show.replace('\r', ' '); 1074 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos); 1075 fprintf(stderr, " "); 1076 for (int i = 0; i < caret; i++) 1077 fprintf(stderr, " "); 1078 fprintf(stderr, "^\n"); 1079 } 1080 else { 1081 if ((int)text.length() > max) 1082 text = text.left(max - 3) + "..."; 1083 else 1084 text = text.left(max); 1085 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data()); 1086 } 1087 } 1088 } 1089 1090 bool SelectionController::contains(const IntPoint& point) 1091 { 1092 Document* document = m_frame->document(); 1093 1094 // Treat a collapsed selection like no selection. 1095 if (!isRange()) 1096 return false; 1097 if (!document->renderer()) 1098 return false; 1099 1100 HitTestRequest request(HitTestRequest::ReadOnly | 1101 HitTestRequest::Active); 1102 HitTestResult result(point); 1103 document->renderView()->layer()->hitTest(request, result); 1104 Node* innerNode = result.innerNode(); 1105 if (!innerNode || !innerNode->renderer()) 1106 return false; 1107 1108 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint())); 1109 if (visiblePos.isNull()) 1110 return false; 1111 1112 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull()) 1113 return false; 1114 1115 Position start(m_selection.visibleStart().deepEquivalent()); 1116 Position end(m_selection.visibleEnd().deepEquivalent()); 1117 Position p(visiblePos.deepEquivalent()); 1118 1119 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0; 1120 } 1121 1122 // Workaround for the fact that it's hard to delete a frame. 1123 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected. 1124 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good 1125 // for the focus to move to another frame. So instead we call it from places where we are selecting with the 1126 // mouse or the keyboard after setting the selection. 1127 void SelectionController::selectFrameElementInParentIfFullySelected() 1128 { 1129 // Find the parent frame; if there is none, then we have nothing to do. 1130 Frame* parent = m_frame->tree()->parent(); 1131 if (!parent) 1132 return; 1133 Page* page = m_frame->page(); 1134 if (!page) 1135 return; 1136 1137 // Check if the selection contains the entire frame contents; if not, then there is nothing to do. 1138 if (!isRange()) 1139 return; 1140 if (!isStartOfDocument(selection().visibleStart())) 1141 return; 1142 if (!isEndOfDocument(selection().visibleEnd())) 1143 return; 1144 1145 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame. 1146 Document* doc = m_frame->document(); 1147 Element* ownerElement = doc->ownerElement(); 1148 if (!ownerElement) 1149 return; 1150 Node* ownerElementParent = ownerElement->parentNode(); 1151 if (!ownerElementParent) 1152 return; 1153 1154 // 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. 1155 if (!ownerElementParent->isContentEditable()) 1156 return; 1157 1158 // Create compute positions before and after the element. 1159 unsigned ownerElementNodeIndex = ownerElement->nodeIndex(); 1160 VisiblePosition beforeOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex, SEL_DEFAULT_AFFINITY)); 1161 VisiblePosition afterOwnerElement(VisiblePosition(ownerElementParent, ownerElementNodeIndex + 1, VP_UPSTREAM_IF_POSSIBLE)); 1162 1163 // Focus on the parent frame, and then select from before this element to after. 1164 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement); 1165 if (parent->shouldChangeSelection(newSelection)) { 1166 page->focusController()->setFocusedFrame(parent); 1167 parent->selection()->setSelection(newSelection); 1168 } 1169 } 1170 1171 void SelectionController::selectAll() 1172 { 1173 Document* document = m_frame->document(); 1174 1175 if (document->focusedNode() && document->focusedNode()->canSelectAll()) { 1176 document->focusedNode()->selectAll(); 1177 return; 1178 } 1179 1180 Node* root = 0; 1181 if (isContentEditable()) 1182 root = highestEditableRoot(m_selection.start()); 1183 else { 1184 root = shadowTreeRootNode(); 1185 if (!root) 1186 root = document->documentElement(); 1187 } 1188 if (!root) 1189 return; 1190 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root)); 1191 if (m_frame->shouldChangeSelection(newSelection)) 1192 setSelection(newSelection); 1193 selectFrameElementInParentIfFullySelected(); 1194 m_frame->notifyRendererOfSelectionChange(true); 1195 } 1196 1197 bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping) 1198 { 1199 if (!range) 1200 return false; 1201 1202 ExceptionCode ec = 0; 1203 Node* startContainer = range->startContainer(ec); 1204 if (ec) 1205 return false; 1206 1207 Node* endContainer = range->endContainer(ec); 1208 if (ec) 1209 return false; 1210 1211 ASSERT(startContainer); 1212 ASSERT(endContainer); 1213 ASSERT(startContainer->document() == endContainer->document()); 1214 1215 m_frame->document()->updateLayoutIgnorePendingStylesheets(); 1216 1217 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped, 1218 // they start at the beginning of the next line instead 1219 bool collapsed = range->collapsed(ec); 1220 if (ec) 1221 return false; 1222 1223 int startOffset = range->startOffset(ec); 1224 if (ec) 1225 return false; 1226 1227 int endOffset = range->endOffset(ec); 1228 if (ec) 1229 return false; 1230 1231 // FIXME: Can we provide extentAffinity? 1232 VisiblePosition visibleStart(startContainer, startOffset, collapsed ? affinity : DOWNSTREAM); 1233 VisiblePosition visibleEnd(endContainer, endOffset, SEL_DEFAULT_AFFINITY); 1234 setSelection(VisibleSelection(visibleStart, visibleEnd), closeTyping); 1235 return true; 1236 } 1237 1238 bool SelectionController::isInPasswordField() const 1239 { 1240 Node* startNode = start().node(); 1241 if (!startNode) 1242 return false; 1243 1244 startNode = startNode->shadowAncestorNode(); 1245 if (!startNode) 1246 return false; 1247 1248 if (!startNode->hasTagName(inputTag)) 1249 return false; 1250 1251 return static_cast<HTMLInputElement*>(startNode)->inputType() == HTMLInputElement::PASSWORD; 1252 } 1253 1254 bool SelectionController::caretRendersInsideNode(Node* node) const 1255 { 1256 if (!node) 1257 return false; 1258 return !isTableElement(node) && !editingIgnoresContent(node); 1259 } 1260 1261 void SelectionController::focusedOrActiveStateChanged() 1262 { 1263 bool activeAndFocused = isFocusedAndActive(); 1264 1265 // Because RenderObject::selectionBackgroundColor() and 1266 // RenderObject::selectionForegroundColor() check if the frame is active, 1267 // we have to update places those colors were painted. 1268 if (RenderView* view = toRenderView(m_frame->document()->renderer())) 1269 view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(m_frame->selectionBounds())); 1270 1271 // Caret appears in the active frame. 1272 if (activeAndFocused) 1273 m_frame->setSelectionFromNone(); 1274 setCaretVisible(activeAndFocused); 1275 1276 // Update for caps lock state 1277 m_frame->eventHandler()->capsLockStateMayHaveChanged(); 1278 1279 // Because CSSStyleSelector::checkOneSelector() and 1280 // RenderTheme::isFocused() check if the frame is active, we have to 1281 // update style and theme state that depended on those. 1282 if (Node* node = m_frame->document()->focusedNode()) { 1283 node->setNeedsStyleRecalc(); 1284 if (RenderObject* renderer = node->renderer()) 1285 if (renderer && renderer->style()->hasAppearance()) 1286 renderer->theme()->stateChanged(renderer, FocusState); 1287 } 1288 1289 // Secure keyboard entry is set by the active frame. 1290 if (m_frame->document()->useSecureKeyboardEntryWhenActive()) 1291 m_frame->setUseSecureKeyboardEntry(activeAndFocused); 1292 } 1293 1294 void SelectionController::pageActivationChanged() 1295 { 1296 focusedOrActiveStateChanged(); 1297 } 1298 1299 void SelectionController::setFocused(bool flag) 1300 { 1301 if (m_focused == flag) 1302 return; 1303 m_focused = flag; 1304 1305 focusedOrActiveStateChanged(); 1306 } 1307 1308 bool SelectionController::isFocusedAndActive() const 1309 { 1310 return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive(); 1311 } 1312 1313 void SelectionController::updateAppearance() 1314 { 1315 ASSERT(!m_isDragCaretController); 1316 1317 #if ENABLE(TEXT_CARET) 1318 bool caretRectChanged = recomputeCaretRect(); 1319 1320 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled(); 1321 bool shouldBlink = m_caretVisible 1322 && isCaret() && (isContentEditable() || caretBrowsing); 1323 1324 // If the caret moved, stop the blink timer so we can restart with a 1325 // black caret in the new location. 1326 if (caretRectChanged || !shouldBlink) 1327 m_caretBlinkTimer.stop(); 1328 1329 // Start blinking with a black caret. Be sure not to restart if we're 1330 // already blinking in the right location. 1331 if (shouldBlink && !m_caretBlinkTimer.isActive()) { 1332 if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval()) 1333 m_caretBlinkTimer.startRepeating(blinkInterval); 1334 1335 if (!m_caretPaint) { 1336 m_caretPaint = true; 1337 invalidateCaretRect(); 1338 } 1339 } 1340 #endif 1341 1342 // We need to update style in case the node containing the selection is made display:none. 1343 m_frame->document()->updateStyleIfNeeded(); 1344 1345 RenderView* view = m_frame->contentRenderer(); 1346 if (!view) 1347 return; 1348 1349 VisibleSelection selection = this->selection(); 1350 1351 if (!selection.isRange()) { 1352 view->clearSelection(); 1353 return; 1354 } 1355 1356 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection. 1357 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3] 1358 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected 1359 // and will fill the gap before 'bar'. 1360 Position startPos = selection.start(); 1361 Position candidate = startPos.downstream(); 1362 if (candidate.isCandidate()) 1363 startPos = candidate; 1364 Position endPos = selection.end(); 1365 candidate = endPos.upstream(); 1366 if (candidate.isCandidate()) 1367 endPos = candidate; 1368 1369 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted 1370 // because we don't yet notify the SelectionController of text removal. 1371 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) { 1372 RenderObject* startRenderer = startPos.node()->renderer(); 1373 RenderObject* endRenderer = endPos.node()->renderer(); 1374 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset()); 1375 } 1376 } 1377 1378 void SelectionController::setCaretVisible(bool flag) 1379 { 1380 if (m_caretVisible == flag) 1381 return; 1382 clearCaretRectIfNeeded(); 1383 m_caretVisible = flag; 1384 updateAppearance(); 1385 } 1386 1387 void SelectionController::clearCaretRectIfNeeded() 1388 { 1389 #if ENABLE(TEXT_CARET) 1390 if (!m_caretPaint) 1391 return; 1392 m_caretPaint = false; 1393 invalidateCaretRect(); 1394 #endif 1395 } 1396 1397 void SelectionController::caretBlinkTimerFired(Timer<SelectionController>*) 1398 { 1399 #if ENABLE(TEXT_CARET) 1400 ASSERT(m_caretVisible); 1401 ASSERT(isCaret()); 1402 bool caretPaint = m_caretPaint; 1403 if (isCaretBlinkingSuspended() && caretPaint) 1404 return; 1405 m_caretPaint = !caretPaint; 1406 invalidateCaretRect(); 1407 #endif 1408 } 1409 1410 #ifndef NDEBUG 1411 1412 void SelectionController::formatForDebugger(char* buffer, unsigned length) const 1413 { 1414 m_selection.formatForDebugger(buffer, length); 1415 } 1416 1417 void SelectionController::showTreeForThis() const 1418 { 1419 m_selection.showTreeForThis(); 1420 } 1421 1422 #endif 1423 1424 } 1425 1426 #ifndef NDEBUG 1427 1428 void showTree(const WebCore::SelectionController& sel) 1429 { 1430 sel.showTreeForThis(); 1431 } 1432 1433 void showTree(const WebCore::SelectionController* sel) 1434 { 1435 if (sel) 1436 sel->showTreeForThis(); 1437 } 1438 1439 #endif 1440