2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "SelectionController.h"
29 #include "CharacterData.h"
30 #include "DeleteSelectionCommand.h"
33 #include "EditorClient.h"
35 #include "EventHandler.h"
36 #include "ExceptionCode.h"
37 #include "FloatQuad.h"
38 #include "FocusController.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"
51 #include "RenderLayer.h"
52 #include "RenderTextControl.h"
53 #include "RenderTheme.h"
54 #include "RenderView.h"
55 #include "RenderWidget.h"
56 #include "SecureTextInput.h"
58 #include "TextIterator.h"
59 #include "TypingCommand.h"
60 #include "htmlediting.h"
61 #include "visible_units.h"
63 #include <wtf/text/CString.h>
69 using namespace HTMLNames;
71 const int NoXPosForVerticalArrowNavigation = INT_MIN;
73 SelectionController::SelectionController(Frame* frame, bool isDragCaretController)
75 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation)
76 , m_granularity(CharacterGranularity)
77 , m_caretBlinkTimer(this, &SelectionController::caretBlinkTimerFired)
78 , m_caretRectNeedsUpdate(true)
79 , m_absCaretBoundsDirty(true)
80 , m_isDragCaretController(isDragCaretController)
81 , m_isCaretBlinkingSuspended(false)
82 , m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame)
83 , m_caretVisible(isDragCaretController)
86 setIsDirectional(false);
89 void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered, CursorAlignOnScroll align)
91 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
93 options |= UserTriggered;
94 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), options, align);
97 void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
99 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
101 options |= UserTriggered;
102 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), options);
105 void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
107 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
109 options |= UserTriggered;
110 setSelection(VisibleSelection(pos, affinity), options);
113 void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
115 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
117 options |= UserTriggered;
118 VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
119 setSelection(selection, options);
122 void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
124 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
126 options |= UserTriggered;
127 setSelection(VisibleSelection(base, extent, affinity), options);
130 void SelectionController::setSelection(const VisibleSelection& s, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy)
132 m_granularity = granularity;
134 bool closeTyping = options & CloseTyping;
135 bool shouldClearTypingStyle = options & ClearTypingStyle;
136 bool userTriggered = options & UserTriggered;
138 setIsDirectional(directionalityPolicy == MakeDirectionalSelection);
140 if (m_isDragCaretController) {
141 invalidateCaretRect();
143 m_caretRectNeedsUpdate = true;
144 invalidateCaretRect();
153 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection
154 // if document->frame() == m_frame we can get into an infinite loop
155 if (s.base().anchorNode()) {
156 Document* document = s.base().anchorNode()->document();
157 if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
158 document->frame()->selection()->setSelection(s, options);
164 TypingCommand::closeTyping(m_frame->editor()->lastEditCommand());
166 if (shouldClearTypingStyle)
169 if (m_selection == s) {
170 // Even if selection was not changed, selection offsets may have been changed.
171 notifyRendererOfSelectionChange(userTriggered);
175 VisibleSelection oldSelection = m_selection;
179 m_caretRectNeedsUpdate = true;
182 setFocusedNodeIfNeeded();
186 // Always clear the x position used for vertical arrow navigation.
187 // It will be restored by the vertical arrow navigation code if necessary.
188 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation;
189 selectFrameElementInParentIfFullySelected();
190 notifyRendererOfSelectionChange(userTriggered);
191 m_frame->editor()->respondToChangedSelection(oldSelection, options);
193 ScrollAlignment alignment;
195 if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
196 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
198 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
200 revealSelection(alignment, true);
203 notifyAccessibilityForSelectionChange();
204 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
207 static bool removingNodeRemovesPosition(Node* node, const Position& position)
209 if (!position.anchorNode())
212 if (position.anchorNode() == node)
215 if (!node->isElementNode())
218 Element* element = static_cast<Element*>(node);
219 return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
222 void SelectionController::nodeWillBeRemoved(Node *node)
227 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
228 // the selection in the document that created the fragment needs no adjustment.
229 if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
232 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
233 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
236 void SelectionController::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
238 bool clearRenderTreeSelection = false;
239 bool clearDOMTreeSelection = false;
241 if (startRemoved || endRemoved) {
242 // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away.
243 clearRenderTreeSelection = true;
244 clearDOMTreeSelection = true;
245 } else if (baseRemoved || extentRemoved) {
246 // The base and/or extent are about to be removed, but the start and end aren't.
247 // Change the base and extent to the start and end, but don't re-validate the
248 // selection, since doing so could move the start and end into the node
249 // that is about to be removed.
250 if (m_selection.isBaseFirst())
251 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
253 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
254 } else if (m_selection.firstRange()) {
255 ExceptionCode ec = 0;
256 Range::CompareResults compareResult = m_selection.firstRange()->compareNode(node, ec);
257 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
258 // If we did nothing here, when this node's renderer was destroyed, the rect that it
259 // occupied would be invalidated, but, selection gaps that change as a result of
260 // the removal wouldn't be invalidated.
261 // FIXME: Don't do so much unnecessary invalidation.
262 clearRenderTreeSelection = true;
266 if (clearRenderTreeSelection) {
267 RefPtr<Document> document = m_selection.start().anchorNode()->document();
268 document->updateStyleIfNeeded();
269 if (RenderView* view = toRenderView(document->renderer()))
270 view->clearSelection();
273 if (clearDOMTreeSelection)
274 setSelection(VisibleSelection(), 0);
277 enum EndPointType { EndPointIsStart, EndPointIsEnd };
279 static bool shouldRemovePositionAfterAdoptingTextReplacement(Position& position, EndPointType type, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
281 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
284 ASSERT(position.offsetInContainerNode() >= 0);
285 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
286 if (positionOffset > offset && positionOffset < offset + oldLength)
289 // Adjust the offset if the position is after or at the end of the deleted contents (positionOffset >= offset + oldLength)
290 // to avoid having a stale offset except when the position is the end of selection and nothing is deleted, in which case,
291 // adjusting offset results in incorrectly extending the selection until the end of newly inserted contents.
292 if ((positionOffset > offset + oldLength) || (positionOffset == offset + oldLength && (type == EndPointIsStart || oldLength)))
293 position.moveToOffset(positionOffset - oldLength + newLength);
298 void SelectionController::textWillBeReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
300 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
301 if (isNone() || !node || highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
304 Position base = m_selection.base();
305 Position extent = m_selection.extent();
306 Position start = m_selection.start();
307 Position end = m_selection.end();
308 bool shouldRemoveBase = shouldRemovePositionAfterAdoptingTextReplacement(base, m_selection.isBaseFirst() ? EndPointIsStart : EndPointIsEnd, node, offset, oldLength, newLength);
309 bool shouldRemoveExtent = shouldRemovePositionAfterAdoptingTextReplacement(extent, m_selection.isBaseFirst() ? EndPointIsEnd : EndPointIsStart, node, offset, oldLength, newLength);
310 bool shouldRemoveStart = shouldRemovePositionAfterAdoptingTextReplacement(start, EndPointIsStart, node, offset, oldLength, newLength);
311 bool shouldRemoveEnd = shouldRemovePositionAfterAdoptingTextReplacement(end, EndPointIsEnd, node, offset, oldLength, newLength);
313 if ((base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end())
314 && !shouldRemoveStart && !shouldRemoveEnd) {
315 VisibleSelection newSelection;
316 if (!shouldRemoveBase && !shouldRemoveExtent)
317 newSelection.setWithoutValidation(base, extent);
319 if (newSelection.isBaseFirst())
320 newSelection.setWithoutValidation(start, end);
322 newSelection.setWithoutValidation(end, start);
324 m_frame->document()->updateLayout();
325 setSelection(newSelection, 0);
329 respondToNodeModification(node, shouldRemoveBase, shouldRemoveExtent, shouldRemoveStart, shouldRemoveEnd);
332 void SelectionController::setIsDirectional(bool isDirectional)
334 m_isDirectional = !m_frame || m_frame->editor()->behavior().shouldConsiderSelectionAsDirectional() || isDirectional;
337 TextDirection SelectionController::directionOfEnclosingBlock()
339 return WebCore::directionOfEnclosingBlock(m_selection.extent());
342 void SelectionController::willBeModified(EAlteration alter, SelectionDirection direction)
344 if (alter != AlterationExtend)
347 Position start = m_selection.start();
348 Position end = m_selection.end();
350 bool baseIsStart = true;
352 if (m_isDirectional) {
353 // Make base and extent match start and end so we extend the user-visible selection.
354 // This only matters for cases where base and extend point to different positions than
355 // start and end (e.g. after a double-click to select a word).
356 if (m_selection.isBaseFirst())
363 if (directionOfEnclosingBlock() == LTR)
368 case DirectionForward:
372 if (directionOfEnclosingBlock() == LTR)
377 case DirectionBackward:
383 m_selection.setBase(start);
384 m_selection.setExtent(end);
386 m_selection.setBase(end);
387 m_selection.setExtent(start);
391 VisiblePosition SelectionController::positionForPlatform(bool isGetStart) const
393 Settings* settings = m_frame ? m_frame->settings() : 0;
394 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
395 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
396 // Linux and Windows always extend selections from the extent endpoint.
397 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
398 // base/extent always point to the same nodes as start/end, but which points
399 // to which depends on the value of isBaseFirst. Then this can be changed
400 // to just return m_sel.extent().
401 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
404 VisiblePosition SelectionController::startForPlatform() const
406 return positionForPlatform(true);
409 VisiblePosition SelectionController::endForPlatform() const
411 return positionForPlatform(false);
414 VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granularity)
416 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
418 // The difference between modifyExtendingRight and modifyExtendingForward is:
419 // modifyExtendingForward always extends forward logically.
420 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
421 // it extends forward logically if the enclosing block is LTR direction,
422 // but it extends backward logically if the enclosing block is RTL direction.
423 switch (granularity) {
424 case CharacterGranularity:
425 if (directionOfEnclosingBlock() == LTR)
426 pos = pos.next(CannotCrossEditingBoundary);
428 pos = pos.previous(CannotCrossEditingBoundary);
430 case WordGranularity:
431 if (directionOfEnclosingBlock() == LTR)
432 pos = nextWordPosition(pos);
434 pos = previousWordPosition(pos);
437 if (directionOfEnclosingBlock() == LTR)
438 pos = modifyExtendingForward(granularity);
440 pos = modifyExtendingBackward(granularity);
442 case SentenceGranularity:
443 case LineGranularity:
444 case ParagraphGranularity:
445 case SentenceBoundary:
446 case ParagraphBoundary:
447 case DocumentBoundary:
448 // FIXME: implement all of the above?
449 pos = modifyExtendingForward(granularity);
454 VisiblePosition SelectionController::modifyExtendingForward(TextGranularity granularity)
456 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
457 switch (granularity) {
458 case CharacterGranularity:
459 pos = pos.next(CannotCrossEditingBoundary);
461 case WordGranularity:
462 pos = nextWordPosition(pos);
464 case SentenceGranularity:
465 pos = nextSentencePosition(pos);
467 case LineGranularity:
468 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
470 case ParagraphGranularity:
471 pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
473 case SentenceBoundary:
474 pos = endOfSentence(endForPlatform());
477 pos = logicalEndOfLine(endForPlatform());
479 case ParagraphBoundary:
480 pos = endOfParagraph(endForPlatform());
482 case DocumentBoundary:
483 pos = endForPlatform();
484 if (isEditablePosition(pos.deepEquivalent()))
485 pos = endOfEditableContent(pos);
487 pos = endOfDocument(pos);
494 VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity)
497 switch (granularity) {
498 case CharacterGranularity:
500 if (directionOfEnclosingBlock() == LTR)
501 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
503 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
505 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
507 case WordGranularity:
508 case SentenceGranularity:
509 case LineGranularity:
510 case ParagraphGranularity:
511 case SentenceBoundary:
512 case ParagraphBoundary:
513 case DocumentBoundary:
514 // FIXME: Implement all of the above.
515 pos = modifyMovingForward(granularity);
518 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
524 VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity)
527 // FIXME: Stay in editable content for the less common granularities.
528 switch (granularity) {
529 case CharacterGranularity:
531 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
533 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
535 case WordGranularity:
536 pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
538 case SentenceGranularity:
539 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
541 case LineGranularity: {
542 // down-arrowing from a range selection that ends at the start of a line needs
543 // to leave the selection at that line start (no need to call nextLinePosition!)
544 pos = endForPlatform();
545 if (!isRange() || !isStartOfLine(pos))
546 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START));
549 case ParagraphGranularity:
550 pos = nextParagraphPosition(endForPlatform(), xPosForVerticalArrowNavigation(START));
552 case SentenceBoundary:
553 pos = endOfSentence(endForPlatform());
556 pos = logicalEndOfLine(endForPlatform());
558 case ParagraphBoundary:
559 pos = endOfParagraph(endForPlatform());
561 case DocumentBoundary:
562 pos = endForPlatform();
563 if (isEditablePosition(pos.deepEquivalent()))
564 pos = endOfEditableContent(pos);
566 pos = endOfDocument(pos);
572 VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granularity)
574 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
576 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
577 // modifyExtendingBackward always extends backward logically.
578 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
579 // it extends backward logically if the enclosing block is LTR direction,
580 // but it extends forward logically if the enclosing block is RTL direction.
581 switch (granularity) {
582 case CharacterGranularity:
583 if (directionOfEnclosingBlock() == LTR)
584 pos = pos.previous(CannotCrossEditingBoundary);
586 pos = pos.next(CannotCrossEditingBoundary);
588 case WordGranularity:
589 if (directionOfEnclosingBlock() == LTR)
590 pos = previousWordPosition(pos);
592 pos = nextWordPosition(pos);
595 if (directionOfEnclosingBlock() == LTR)
596 pos = modifyExtendingBackward(granularity);
598 pos = modifyExtendingForward(granularity);
600 case SentenceGranularity:
601 case LineGranularity:
602 case ParagraphGranularity:
603 case SentenceBoundary:
604 case ParagraphBoundary:
605 case DocumentBoundary:
606 pos = modifyExtendingBackward(granularity);
611 VisiblePosition SelectionController::modifyExtendingBackward(TextGranularity granularity)
613 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
615 // Extending a selection backward by word or character from just after a table selects
616 // the table. This "makes sense" from the user perspective, esp. when deleting.
617 // It was done here instead of in VisiblePosition because we want VPs to iterate
619 switch (granularity) {
620 case CharacterGranularity:
621 pos = pos.previous(CannotCrossEditingBoundary);
623 case WordGranularity:
624 pos = previousWordPosition(pos);
626 case SentenceGranularity:
627 pos = previousSentencePosition(pos);
629 case LineGranularity:
630 pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
632 case ParagraphGranularity:
633 pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
635 case SentenceBoundary:
636 pos = startOfSentence(startForPlatform());
639 pos = logicalStartOfLine(startForPlatform());
641 case ParagraphBoundary:
642 pos = startOfParagraph(startForPlatform());
644 case DocumentBoundary:
645 pos = startForPlatform();
646 if (isEditablePosition(pos.deepEquivalent()))
647 pos = startOfEditableContent(pos);
649 pos = startOfDocument(pos);
655 VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity)
658 switch (granularity) {
659 case CharacterGranularity:
661 if (directionOfEnclosingBlock() == LTR)
662 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
664 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
666 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
668 case WordGranularity:
669 case SentenceGranularity:
670 case LineGranularity:
671 case ParagraphGranularity:
672 case SentenceBoundary:
673 case ParagraphBoundary:
674 case DocumentBoundary:
675 // FIXME: Implement all of the above.
676 pos = modifyMovingBackward(granularity);
679 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
685 VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity)
688 switch (granularity) {
689 case CharacterGranularity:
691 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
693 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
695 case WordGranularity:
696 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
698 case SentenceGranularity:
699 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
701 case LineGranularity:
702 pos = previousLinePosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
704 case ParagraphGranularity:
705 pos = previousParagraphPosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
707 case SentenceBoundary:
708 pos = startOfSentence(startForPlatform());
711 pos = logicalStartOfLine(startForPlatform());
713 case ParagraphBoundary:
714 pos = startOfParagraph(startForPlatform());
716 case DocumentBoundary:
717 pos = startForPlatform();
718 if (isEditablePosition(pos.deepEquivalent()))
719 pos = startOfEditableContent(pos);
721 pos = startOfDocument(pos);
727 static bool isBoundary(TextGranularity granularity)
729 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
732 bool SelectionController::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, bool userTriggered)
735 SelectionController trialSelectionController;
736 trialSelectionController.setSelection(m_selection);
737 trialSelectionController.setIsDirectional(m_isDirectional);
738 trialSelectionController.modify(alter, direction, granularity, false);
740 bool change = shouldChangeSelection(trialSelectionController.selection());
745 willBeModified(alter, direction);
747 bool wasRange = m_selection.isRange();
748 Position originalStartPosition = m_selection.start();
749 VisiblePosition position;
752 if (alter == AlterationMove)
753 position = modifyMovingRight(granularity);
755 position = modifyExtendingRight(granularity);
757 case DirectionForward:
758 if (alter == AlterationExtend)
759 position = modifyExtendingForward(granularity);
761 position = modifyMovingForward(granularity);
764 if (alter == AlterationMove)
765 position = modifyMovingLeft(granularity);
767 position = modifyExtendingLeft(granularity);
769 case DirectionBackward:
770 if (alter == AlterationExtend)
771 position = modifyExtendingBackward(granularity);
773 position = modifyMovingBackward(granularity);
777 if (position.isNull())
780 if (isSpatialNavigationEnabled(m_frame))
781 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
784 // Some of the above operations set an xPosForVerticalArrowNavigation.
785 // Setting a selection will clear it, so save it to possibly restore later.
786 // Note: the START position type is arbitrary because it is unused, it would be
787 // the requested position type if there were no xPosForVerticalArrowNavigation set.
788 int x = xPosForVerticalArrowNavigation(START);
792 moveTo(position, userTriggered);
794 case AlterationExtend:
795 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
796 // base in place and moving the extent. Matches NSTextView.
797 if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
798 setExtent(position, userTriggered);
800 TextDirection textDirection = directionOfEnclosingBlock();
801 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
802 setEnd(position, userTriggered);
804 setStart(position, userTriggered);
809 if (granularity == LineGranularity || granularity == ParagraphGranularity)
810 m_xPosForVerticalArrowNavigation = x;
813 m_granularity = CharacterGranularity;
816 setCaretRectNeedsUpdate();
818 setIsDirectional(alter == AlterationExtend);
823 // FIXME: Maybe baseline would be better?
824 static bool absoluteCaretY(const VisiblePosition &c, int &y)
826 IntRect rect = c.absoluteCaretBounds();
829 y = rect.y() + rect.height() / 2;
833 bool SelectionController::modify(EAlteration alter, int verticalDistance, bool userTriggered, CursorAlignOnScroll align)
835 if (!verticalDistance)
839 SelectionController trialSelectionController;
840 trialSelectionController.setSelection(m_selection);
841 trialSelectionController.setIsDirectional(m_isDirectional);
842 trialSelectionController.modify(alter, verticalDistance, false);
844 bool change = shouldChangeSelection(trialSelectionController.selection());
849 bool up = verticalDistance < 0;
851 verticalDistance = -verticalDistance;
853 willBeModified(alter, up ? DirectionBackward : DirectionForward);
859 pos = VisiblePosition(up ? m_selection.start() : m_selection.end(), m_selection.affinity());
860 xPos = xPosForVerticalArrowNavigation(up ? START : END);
861 m_selection.setAffinity(up ? UPSTREAM : DOWNSTREAM);
863 case AlterationExtend:
864 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
865 xPos = xPosForVerticalArrowNavigation(EXTENT);
866 m_selection.setAffinity(DOWNSTREAM);
871 if (!absoluteCaretY(pos, startY))
877 VisiblePosition result;
878 VisiblePosition next;
879 for (VisiblePosition p = pos; ; p = next) {
880 next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
881 if (next.isNull() || next == p)
884 if (!absoluteCaretY(next, nextY))
888 if (nextY - startY > verticalDistance)
890 if (nextY >= lastY) {
901 moveTo(result, userTriggered, align);
903 case AlterationExtend:
904 setExtent(result, userTriggered);
909 m_granularity = CharacterGranularity;
911 setIsDirectional(alter == AlterationExtend);
916 int SelectionController::xPosForVerticalArrowNavigation(EPositionType type)
926 pos = m_selection.start();
929 pos = m_selection.end();
932 pos = m_selection.base();
935 pos = m_selection.extent();
939 Frame* frame = pos.anchorNode()->document()->frame();
943 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) {
944 VisiblePosition visiblePosition(pos, m_selection.affinity());
945 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
946 // after the selection is created and before this function is called.
947 x = visiblePosition.isNotNull() ? visiblePosition.xOffsetForVerticalNavigation() : 0;
948 m_xPosForVerticalArrowNavigation = x;
950 x = m_xPosForVerticalArrowNavigation;
955 void SelectionController::clear()
957 m_granularity = CharacterGranularity;
958 setSelection(VisibleSelection());
961 void SelectionController::setStart(const VisiblePosition &pos, bool userTriggered)
963 if (m_selection.isBaseFirst())
964 setBase(pos, userTriggered);
966 setExtent(pos, userTriggered);
969 void SelectionController::setEnd(const VisiblePosition &pos, bool userTriggered)
971 if (m_selection.isBaseFirst())
972 setExtent(pos, userTriggered);
974 setBase(pos, userTriggered);
977 void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered)
979 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
981 options |= UserTriggered;
982 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), options);
985 void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered)
987 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
989 options |= UserTriggered;
990 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), options);
993 void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
995 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
997 options |= UserTriggered;
998 setSelection(VisibleSelection(pos, m_selection.extent(), affinity), options);
1001 void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
1003 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
1005 options |= UserTriggered;
1006 setSelection(VisibleSelection(m_selection.base(), pos, affinity), options);
1009 void SelectionController::setCaretRectNeedsUpdate(bool flag)
1011 m_caretRectNeedsUpdate = flag;
1014 void SelectionController::updateCaretRect()
1016 if (isNone() || !m_selection.start().anchorNode()->inDocument() || !m_selection.end().anchorNode()->inDocument()) {
1017 m_caretRect = IntRect();
1021 m_selection.start().anchorNode()->document()->updateStyleIfNeeded();
1023 m_caretRect = IntRect();
1026 VisiblePosition pos(m_selection.start(), m_selection.affinity());
1027 if (pos.isNotNull()) {
1028 ASSERT(pos.deepEquivalent().deprecatedNode()->renderer());
1030 // First compute a rect local to the renderer at the selection start
1031 RenderObject* renderer;
1032 IntRect localRect = pos.localCaretRect(renderer);
1034 // Get the renderer that will be responsible for painting the caret (which
1035 // is either the renderer we just found, or one of its containers)
1036 RenderObject* caretPainter = caretRenderer();
1038 // Compute an offset between the renderer and the caretPainter
1039 bool unrooted = false;
1040 while (renderer != caretPainter) {
1041 RenderObject* containerObject = renderer->container();
1042 if (!containerObject) {
1046 localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
1047 renderer = containerObject;
1051 m_caretRect = localRect;
1053 m_absCaretBoundsDirty = true;
1057 m_caretRectNeedsUpdate = false;
1060 RenderObject* SelectionController::caretRenderer() const
1062 Node* node = m_selection.start().deprecatedNode();
1066 RenderObject* renderer = node->renderer();
1070 // if caretNode is a block and caret is inside it then caret should be painted by that block
1071 bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
1072 return paintedByBlock ? renderer : renderer->containingBlock();
1075 IntRect SelectionController::localCaretRect()
1077 if (m_caretRectNeedsUpdate)
1083 IntRect SelectionController::absoluteBoundsForLocalRect(const IntRect& rect) const
1085 RenderObject* caretPainter = caretRenderer();
1089 IntRect localRect(rect);
1090 if (caretPainter->isBox())
1091 toRenderBox(caretPainter)->flipForWritingMode(localRect);
1092 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
1095 IntRect SelectionController::absoluteCaretBounds()
1097 recomputeCaretRect();
1098 return m_absCaretBounds;
1101 static IntRect repaintRectForCaret(IntRect caret)
1103 if (caret.isEmpty())
1105 // Ensure that the dirty rect intersects the block that paints the caret even in the case where
1106 // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
1111 IntRect SelectionController::caretRepaintRect() const
1113 return absoluteBoundsForLocalRect(repaintRectForCaret(localCaretRectForPainting()));
1116 bool SelectionController::recomputeCaretRect()
1118 if (!m_caretRectNeedsUpdate)
1124 FrameView* v = m_frame->document()->view();
1128 IntRect oldRect = m_caretRect;
1129 IntRect newRect = localCaretRect();
1130 if (oldRect == newRect && !m_absCaretBoundsDirty)
1133 IntRect oldAbsCaretBounds = m_absCaretBounds;
1134 // FIXME: Rename m_caretRect to m_localCaretRect.
1135 m_absCaretBounds = absoluteBoundsForLocalRect(m_caretRect);
1136 m_absCaretBoundsDirty = false;
1138 if (oldAbsCaretBounds == m_absCaretBounds)
1141 IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
1142 // We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
1143 m_absoluteCaretRepaintBounds = caretRepaintRect();
1145 #if ENABLE(TEXT_CARET)
1146 if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
1147 // FIXME: make caret repainting container-aware.
1148 view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
1149 if (shouldRepaintCaret(view))
1150 view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
1156 bool SelectionController::shouldRepaintCaret(const RenderView* view) const
1159 Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
1160 bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
1161 return (caretBrowsing || isContentEditable());
1164 void SelectionController::invalidateCaretRect()
1169 Document* d = m_selection.start().anchorNode()->document();
1171 // recomputeCaretRect will always return false for the drag caret,
1172 // because its m_frame is always 0.
1173 bool caretRectChanged = recomputeCaretRect();
1175 // EDIT FIXME: This is an unfortunate hack.
1176 // Basically, we can't trust this layout position since we
1177 // can't guarantee that the check to see if we are in unrendered
1178 // content will work at this point. We may have to wait for
1179 // a layout and re-render of the document to happen. So, resetting this
1180 // flag will cause another caret layout to happen the first time
1181 // that we try to paint the caret after this call. That one will work since
1182 // it happens after the document has accounted for any editing
1183 // changes which may have been done.
1184 // And, we need to leave this layout here so the caret moves right
1185 // away after clicking.
1186 m_caretRectNeedsUpdate = true;
1188 if (!caretRectChanged) {
1189 RenderView* view = toRenderView(d->renderer());
1190 if (view && shouldRepaintCaret(view))
1191 view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(), false);
1195 void SelectionController::paintCaret(GraphicsContext* context, int tx, int ty, const IntRect& clipRect)
1197 #if ENABLE(TEXT_CARET)
1198 if (!m_caretVisible)
1202 if (!m_selection.isCaret())
1205 IntRect drawingRect = localCaretRectForPainting();
1206 if (caretRenderer() && caretRenderer()->isBox())
1207 toRenderBox(caretRenderer())->flipForWritingMode(drawingRect);
1208 drawingRect.move(tx, ty);
1209 IntRect caret = intersection(drawingRect, clipRect);
1210 if (caret.isEmpty())
1213 Color caretColor = Color::black;
1214 ColorSpace colorSpace = ColorSpaceDeviceRGB;
1215 Element* element = rootEditableElement();
1216 if (element && element->renderer()) {
1217 caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
1218 colorSpace = element->renderer()->style()->colorSpace();
1221 context->fillRect(caret, caretColor, colorSpace);
1223 UNUSED_PARAM(context);
1226 UNUSED_PARAM(clipRect);
1230 void SelectionController::debugRenderer(RenderObject *r, bool selected) const
1232 if (r->node()->isElementNode()) {
1233 Element* element = static_cast<Element *>(r->node());
1234 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
1235 } else if (r->isText()) {
1236 RenderText* textRenderer = toRenderText(r);
1237 if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
1238 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1242 static const int max = 36;
1243 String text = textRenderer->text();
1244 int textLength = text.length();
1247 if (r->node() == m_selection.start().containerNode())
1248 offset = m_selection.start().computeOffsetInContainerNode();
1249 else if (r->node() == m_selection.end().containerNode())
1250 offset = m_selection.end().computeOffsetInContainerNode();
1253 InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
1254 text = text.substring(box->start(), box->len());
1260 // text is shorter than max
1261 if (textLength < max) {
1264 } else if (pos - mid < 0) {
1265 // too few characters to left
1266 show = text.left(max - 3) + "...";
1268 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1269 // enough characters on each side
1270 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1273 // too few characters on right
1274 show = "..." + text.right(max - 3);
1275 caret = pos - (textLength - show.length());
1278 show.replace('\n', ' ');
1279 show.replace('\r', ' ');
1280 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1281 fprintf(stderr, " ");
1282 for (int i = 0; i < caret; i++)
1283 fprintf(stderr, " ");
1284 fprintf(stderr, "^\n");
1286 if ((int)text.length() > max)
1287 text = text.left(max - 3) + "...";
1289 text = text.left(max);
1290 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1295 bool SelectionController::contains(const IntPoint& point)
1297 Document* document = m_frame->document();
1299 // Treat a collapsed selection like no selection.
1302 if (!document->renderer())
1305 HitTestRequest request(HitTestRequest::ReadOnly |
1306 HitTestRequest::Active);
1307 HitTestResult result(point);
1308 document->renderView()->layer()->hitTest(request, result);
1309 Node* innerNode = result.innerNode();
1310 if (!innerNode || !innerNode->renderer())
1313 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1314 if (visiblePos.isNull())
1317 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1320 Position start(m_selection.visibleStart().deepEquivalent());
1321 Position end(m_selection.visibleEnd().deepEquivalent());
1322 Position p(visiblePos.deepEquivalent());
1324 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1327 // Workaround for the fact that it's hard to delete a frame.
1328 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1329 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1330 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1331 // mouse or the keyboard after setting the selection.
1332 void SelectionController::selectFrameElementInParentIfFullySelected()
1334 // Find the parent frame; if there is none, then we have nothing to do.
1335 Frame* parent = m_frame->tree()->parent();
1338 Page* page = m_frame->page();
1342 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1345 if (!isStartOfDocument(selection().visibleStart()))
1347 if (!isEndOfDocument(selection().visibleEnd()))
1350 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1351 Element* ownerElement = m_frame->ownerElement();
1354 ContainerNode* ownerElementParent = ownerElement->parentNode();
1355 if (!ownerElementParent)
1358 // 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.
1359 if (!ownerElementParent->rendererIsEditable())
1362 // Create compute positions before and after the element.
1363 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1364 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1365 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1367 // Focus on the parent frame, and then select from before this element to after.
1368 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1369 if (parent->selection()->shouldChangeSelection(newSelection)) {
1370 page->focusController()->setFocusedFrame(parent);
1371 parent->selection()->setSelection(newSelection);
1375 void SelectionController::selectAll()
1377 Document* document = m_frame->document();
1379 if (document->focusedNode() && document->focusedNode()->canSelectAll()) {
1380 document->focusedNode()->selectAll();
1385 if (isContentEditable())
1386 root = highestEditableRoot(m_selection.start());
1388 root = shadowTreeRootNode();
1390 root = document->documentElement();
1394 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root));
1395 if (shouldChangeSelection(newSelection))
1396 setSelection(newSelection);
1397 selectFrameElementInParentIfFullySelected();
1398 notifyRendererOfSelectionChange(true);
1401 bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1406 ExceptionCode ec = 0;
1407 Node* startContainer = range->startContainer(ec);
1411 Node* endContainer = range->endContainer(ec);
1415 ASSERT(startContainer);
1416 ASSERT(endContainer);
1417 ASSERT(startContainer->document() == endContainer->document());
1419 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1421 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1422 // they start at the beginning of the next line instead
1423 bool collapsed = range->collapsed(ec);
1427 int startOffset = range->startOffset(ec);
1431 int endOffset = range->endOffset(ec);
1435 // FIXME: Can we provide extentAffinity?
1436 VisiblePosition visibleStart(Position(startContainer, startOffset, Position::PositionIsOffsetInAnchor), collapsed ? affinity : DOWNSTREAM);
1437 VisiblePosition visibleEnd(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), SEL_DEFAULT_AFFINITY);
1438 SetSelectionOptions options = ClearTypingStyle;
1440 options |= CloseTyping;
1441 setSelection(VisibleSelection(visibleStart, visibleEnd), options);
1445 bool SelectionController::isInPasswordField() const
1447 ASSERT(start().isNull() || start().anchorType() == Position::PositionIsOffsetInAnchor
1448 || start().containerNode() || !start().anchorNode()->shadowAncestorNode());
1449 Node* startNode = start().containerNode();
1453 startNode = startNode->shadowAncestorNode();
1457 if (!startNode->hasTagName(inputTag))
1460 return static_cast<HTMLInputElement*>(startNode)->isPasswordField();
1463 bool SelectionController::caretRendersInsideNode(Node* node) const
1467 return !isTableElement(node) && !editingIgnoresContent(node);
1470 void SelectionController::focusedOrActiveStateChanged()
1472 bool activeAndFocused = isFocusedAndActive();
1474 // Because RenderObject::selectionBackgroundColor() and
1475 // RenderObject::selectionForegroundColor() check if the frame is active,
1476 // we have to update places those colors were painted.
1477 if (RenderView* view = toRenderView(m_frame->document()->renderer()))
1478 view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
1480 // Caret appears in the active frame.
1481 if (activeAndFocused)
1482 setSelectionFromNone();
1483 setCaretVisible(activeAndFocused);
1485 // Update for caps lock state
1486 m_frame->eventHandler()->capsLockStateMayHaveChanged();
1488 // Because CSSStyleSelector::checkOneSelector() and
1489 // RenderTheme::isFocused() check if the frame is active, we have to
1490 // update style and theme state that depended on those.
1491 if (Node* node = m_frame->document()->focusedNode()) {
1492 node->setNeedsStyleRecalc();
1493 if (RenderObject* renderer = node->renderer())
1494 if (renderer && renderer->style()->hasAppearance())
1495 renderer->theme()->stateChanged(renderer, FocusState);
1498 // Secure keyboard entry is set by the active frame.
1499 if (m_frame->document()->useSecureKeyboardEntryWhenActive())
1500 setUseSecureKeyboardEntry(activeAndFocused);
1503 void SelectionController::pageActivationChanged()
1505 focusedOrActiveStateChanged();
1508 void SelectionController::updateSecureKeyboardEntryIfActive()
1510 if (m_frame->document() && isFocusedAndActive())
1511 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1514 void SelectionController::setUseSecureKeyboardEntry(bool enable)
1517 enableSecureTextInput();
1519 disableSecureTextInput();
1522 void SelectionController::setFocused(bool flag)
1524 if (m_focused == flag)
1528 focusedOrActiveStateChanged();
1531 bool SelectionController::isFocusedAndActive() const
1533 return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
1536 void SelectionController::updateAppearance()
1538 ASSERT(!m_isDragCaretController);
1540 #if ENABLE(TEXT_CARET)
1541 bool caretRectChanged = recomputeCaretRect();
1543 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1544 bool shouldBlink = m_caretVisible
1545 && isCaret() && (isContentEditable() || caretBrowsing);
1547 // If the caret moved, stop the blink timer so we can restart with a
1548 // black caret in the new location.
1549 if (caretRectChanged || !shouldBlink)
1550 m_caretBlinkTimer.stop();
1552 // Start blinking with a black caret. Be sure not to restart if we're
1553 // already blinking in the right location.
1554 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1555 if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
1556 m_caretBlinkTimer.startRepeating(blinkInterval);
1558 if (!m_caretPaint) {
1559 m_caretPaint = true;
1560 invalidateCaretRect();
1565 // We need to update style in case the node containing the selection is made display:none.
1566 m_frame->document()->updateStyleIfNeeded();
1568 RenderView* view = m_frame->contentRenderer();
1572 VisibleSelection selection = this->selection();
1574 if (!selection.isRange()) {
1575 view->clearSelection();
1579 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1580 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1581 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1582 // and will fill the gap before 'bar'.
1583 Position startPos = selection.start();
1584 Position candidate = startPos.downstream();
1585 if (candidate.isCandidate())
1586 startPos = candidate;
1587 Position endPos = selection.end();
1588 candidate = endPos.upstream();
1589 if (candidate.isCandidate())
1592 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1593 // because we don't yet notify the SelectionController of text removal.
1594 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1595 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1596 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1597 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1601 void SelectionController::setCaretVisible(bool flag)
1603 if (m_caretVisible == flag)
1605 clearCaretRectIfNeeded();
1606 m_caretVisible = flag;
1610 void SelectionController::clearCaretRectIfNeeded()
1612 #if ENABLE(TEXT_CARET)
1615 m_caretPaint = false;
1616 invalidateCaretRect();
1620 void SelectionController::caretBlinkTimerFired(Timer<SelectionController>*)
1622 #if ENABLE(TEXT_CARET)
1623 ASSERT(m_caretVisible);
1625 bool caretPaint = m_caretPaint;
1626 if (isCaretBlinkingSuspended() && caretPaint)
1628 m_caretPaint = !caretPaint;
1629 invalidateCaretRect();
1633 void SelectionController::notifyRendererOfSelectionChange(bool userTriggered)
1635 m_frame->document()->updateStyleIfNeeded();
1637 if (!rootEditableElement())
1640 RenderObject* renderer = rootEditableElement()->shadowAncestorNode()->renderer();
1641 if (!renderer || !renderer->isTextControl())
1644 toRenderTextControl(renderer)->selectionChanged(userTriggered);
1647 // Helper function that tells whether a particular node is an element that has an entire
1648 // Frame and FrameView, a <frame>, <iframe>, or <object>.
1649 static bool isFrameElement(const Node* n)
1653 RenderObject* renderer = n->renderer();
1654 if (!renderer || !renderer->isWidget())
1656 Widget* widget = toRenderWidget(renderer)->widget();
1657 return widget && widget->isFrameView();
1660 void SelectionController::setFocusedNodeIfNeeded()
1662 if (isNone() || !isFocused())
1665 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1666 if (caretBrowsing) {
1667 if (Node* anchor = enclosingAnchorElement(base())) {
1668 m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
1673 if (Node* target = rootEditableElement()) {
1674 // Walk up the DOM tree to search for a node to focus.
1676 // We don't want to set focus on a subframe when selecting in a parent frame,
1677 // so add the !isFrameElement check here. There's probably a better way to make this
1678 // work in the long term, but this is the safest fix at this time.
1679 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
1680 m_frame->page()->focusController()->setFocusedNode(target, m_frame);
1683 target = target->parentOrHostNode();
1685 m_frame->document()->setFocusedNode(0);
1689 m_frame->page()->focusController()->setFocusedNode(0, m_frame);
1692 void SelectionController::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
1694 #if ENABLE(TEXT_CARET)
1695 SelectionController* dragCaretController = m_frame->page()->dragCaretController();
1696 ASSERT(dragCaretController->selection().isCaret());
1697 if (dragCaretController->selection().start().anchorNode()->document()->frame() == m_frame)
1698 dragCaretController->paintCaret(p, tx, ty, clipRect);
1703 UNUSED_PARAM(clipRect);
1707 PassRefPtr<CSSMutableStyleDeclaration> SelectionController::copyTypingStyle() const
1709 if (!m_typingStyle || !m_typingStyle->style())
1711 return m_typingStyle->style()->copy();
1714 bool SelectionController::shouldDeleteSelection(const VisibleSelection& selection) const
1716 return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
1719 FloatRect SelectionController::bounds(bool clipToVisibleContent) const
1721 RenderView* root = m_frame->contentRenderer();
1722 FrameView* view = m_frame->view();
1726 IntRect selectionRect = root->selectionBounds(clipToVisibleContent);
1727 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1730 void SelectionController::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
1732 RenderView* root = m_frame->contentRenderer();
1736 FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
1738 Vector<FloatQuad> quads;
1739 toNormalizedRange()->textQuads(quads, true);
1741 // FIXME: We are appending empty rectangles to the list for those that fall outside visibleContentRect.
1742 // It might be better to omit those rectangles entirely.
1743 size_t size = quads.size();
1744 for (size_t i = 0; i < size; ++i)
1745 rectangles.append(intersection(quads[i].enclosingBoundingBox(), visibleContentRect));
1748 // Scans logically forward from "start", including any child frames.
1749 static HTMLFormElement* scanForForm(Node* start)
1751 for (Node* node = start; node; node = node->traverseNextNode()) {
1752 if (node->hasTagName(formTag))
1753 return static_cast<HTMLFormElement*>(node);
1754 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1755 return static_cast<HTMLFormControlElement*>(node)->form();
1756 if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
1757 Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
1758 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1765 // We look for either the form containing the current focus, or for one immediately after it
1766 HTMLFormElement* SelectionController::currentForm() const
1768 // Start looking either at the active (first responder) node, or where the selection is.
1769 Node* start = m_frame->document()->focusedNode();
1771 start = this->start().deprecatedNode();
1773 // Try walking up the node tree to find a form element.
1775 for (node = start; node; node = node->parentNode()) {
1776 if (node->hasTagName(formTag))
1777 return static_cast<HTMLFormElement*>(node);
1778 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1779 return static_cast<HTMLFormControlElement*>(node)->form();
1782 // Try walking forward in the node tree to find a form element.
1783 return scanForForm(start);
1786 void SelectionController::revealSelection(const ScrollAlignment& alignment, bool revealExtent)
1790 switch (selectionType()) {
1791 case VisibleSelection::NoSelection:
1793 case VisibleSelection::CaretSelection:
1794 rect = absoluteCaretBounds();
1796 case VisibleSelection::RangeSelection:
1797 rect = revealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1801 Position start = this->start();
1802 ASSERT(start.deprecatedNode());
1803 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1804 // FIXME: This code only handles scrolling the startContainer's layer, but
1805 // the selection rect could intersect more than just that.
1806 // See <rdar://problem/4799899>.
1807 if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
1808 layer->scrollRectToVisible(rect, false, alignment, alignment);
1814 void SelectionController::setSelectionFromNone()
1816 // Put a caret inside the body if the entire frame is editable (either the
1817 // entire WebView is editable or designMode is on for this document).
1819 Document* document = m_frame->document();
1820 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1821 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1824 Node* node = document->documentElement();
1825 while (node && !node->hasTagName(bodyTag))
1826 node = node->traverseNextNode();
1828 setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
1831 bool SelectionController::shouldChangeSelection(const VisibleSelection& newSelection) const
1833 return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
1838 void SelectionController::formatForDebugger(char* buffer, unsigned length) const
1840 m_selection.formatForDebugger(buffer, length);
1843 void SelectionController::showTreeForThis() const
1845 m_selection.showTreeForThis();
1854 void showTree(const WebCore::SelectionController& sel)
1856 sel.showTreeForThis();
1859 void showTree(const WebCore::SelectionController* sel)
1862 sel->showTreeForThis();