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 "FrameSelection.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 "HTMLSelectElement.h"
47 #include "HTMLNames.h"
48 #include "HitTestRequest.h"
49 #include "HitTestResult.h"
50 #include "InlineTextBox.h"
53 #include "RenderText.h"
54 #include "RenderTextControl.h"
55 #include "RenderTheme.h"
56 #include "RenderView.h"
57 #include "RenderWidget.h"
58 #include "RenderedPosition.h"
59 #include "SecureTextInput.h"
61 #include "SpatialNavigation.h"
62 #include "TextIterator.h"
63 #include "TypingCommand.h"
64 #include "htmlediting.h"
65 #include "visible_units.h"
68 #include <wtf/text/CString.h>
74 using namespace HTMLNames;
76 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
78 return MIN_LAYOUT_UNIT;
81 CaretBase::CaretBase(CaretVisibility visibility)
82 : m_caretRectNeedsUpdate(true)
83 , m_caretVisibility(visibility)
87 DragCaretController::DragCaretController()
92 PassOwnPtr<DragCaretController> DragCaretController::create()
94 return adoptPtr(new DragCaretController);
97 bool DragCaretController::isContentRichlyEditable() const
99 return isRichlyEditablePosition(m_position.deepEquivalent());
102 static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame)
104 return !frame || frame->editor()->behavior().shouldConsiderSelectionAsDirectional();
107 FrameSelection::FrameSelection(Frame* frame)
109 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
110 , m_granularity(CharacterGranularity)
111 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
112 , m_absCaretBoundsDirty(true)
114 , m_isCaretBlinkingSuspended(false)
115 , m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame)
117 if (shouldAlwaysUseDirectionalSelection(m_frame))
118 m_selection.setIsDirectional(true);
121 Element* FrameSelection::rootEditableElementOrDocumentElement() const
123 Element* selectionRoot = m_selection.rootEditableElement();
124 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
127 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
129 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
130 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
133 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
135 const bool selectionHasDirection = true;
136 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
137 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
140 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
142 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
143 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
146 void FrameSelection::moveTo(const Range *r, EAffinity affinity, EUserTriggered userTriggered)
148 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
149 VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
150 setSelection(selection, options);
153 void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
155 const bool selectionHasDirection = true;
156 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
157 setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), options);
160 void DragCaretController::setCaretPosition(const VisiblePosition& position)
162 if (Node* node = m_position.deepEquivalent().deprecatedNode())
163 invalidateCaretRect(node);
164 m_position = position;
165 setCaretRectNeedsUpdate();
166 Document* document = 0;
167 if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
168 invalidateCaretRect(node);
169 document = node->document();
171 if (m_position.isNull() || m_position.isOrphan())
174 updateCaretRect(document, m_position);
177 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
179 RenderedPosition base(visibleBase);
180 RenderedPosition extent(visibleExtent);
182 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
185 if (base.atLeftBoundaryOfBidiRun()) {
186 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
187 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
188 visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
194 if (base.atRightBoundaryOfBidiRun()) {
195 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
196 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
197 visibleBase = base.positionAtRightBoundaryOfBiDiRun();
203 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
204 visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
208 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
209 visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
214 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
215 EndPointsAdjustmentMode endpointsAdjustmentMode)
217 VisibleSelection newSelection = passedNewSelection;
218 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
220 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
221 VisiblePosition newBase = base;
222 VisiblePosition newExtent = newSelection.visibleExtent();
223 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
224 adjustEndpointsAtBidiBoundary(newBase, newExtent);
226 if (newBase != base || newExtent != newSelection.visibleExtent()) {
227 m_originalBase = base;
228 newSelection.setBase(newBase);
229 newSelection.setExtent(newExtent);
230 } else if (m_originalBase.isNotNull()) {
231 if (m_selection.base() == newSelection.base())
232 newSelection.setBase(m_originalBase);
233 m_originalBase.clear();
236 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
237 if (m_selection == newSelection || !shouldChangeSelection(newSelection))
240 setSelection(newSelection, granularity);
243 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
245 bool closeTyping = options & CloseTyping;
246 bool shouldClearTypingStyle = options & ClearTypingStyle;
247 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
249 VisibleSelection s = newSelection;
250 if (shouldAlwaysUseDirectionalSelection(m_frame))
251 s.setIsDirectional(true);
258 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
259 // if document->frame() == m_frame we can get into an infinite loop
260 if (s.base().anchorNode()) {
261 Document* document = s.base().anchorNode()->document();
262 if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
263 document->frame()->selection()->setSelection(s, options, align, granularity);
268 m_granularity = granularity;
271 TypingCommand::closeTyping(m_frame);
273 if (shouldClearTypingStyle)
276 if (m_selection == s) {
277 // Even if selection was not changed, selection offsets may have been changed.
278 notifyRendererOfSelectionChange(userTriggered);
282 VisibleSelection oldSelection = m_selection;
285 setCaretRectNeedsUpdate();
287 if (!s.isNone() && !(options & DoNotSetFocus))
288 setFocusedNodeIfNeeded();
292 // Always clear the x position used for vertical arrow navigation.
293 // It will be restored by the vertical arrow navigation code if necessary.
294 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
295 selectFrameElementInParentIfFullySelected();
296 notifyRendererOfSelectionChange(userTriggered);
297 m_frame->editor()->respondToChangedSelection(oldSelection, options);
298 if (userTriggered == UserTriggered) {
299 ScrollAlignment alignment;
301 if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
302 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
304 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
306 revealSelection(alignment, RevealExtent);
309 notifyAccessibilityForSelectionChange();
310 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
313 static bool removingNodeRemovesPosition(Node* node, const Position& position)
315 if (!position.anchorNode())
318 if (position.anchorNode() == node)
321 if (!node->isElementNode())
324 Element* element = static_cast<Element*>(node);
325 return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
328 static void clearRenderViewSelection(const Position& position)
330 RefPtr<Document> document = position.anchorNode()->document();
331 document->updateStyleIfNeeded();
332 if (RenderView* view = toRenderView(document->renderer()))
333 view->clearSelection();
336 void DragCaretController::nodeWillBeRemoved(Node* node)
338 if (!hasCaret() || (node && !node->inDocument()))
341 if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
344 clearRenderViewSelection(m_position.deepEquivalent());
348 void FrameSelection::nodeWillBeRemoved(Node* node)
350 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
351 // the selection in the document that created the fragment needs no adjustment.
352 if (isNone() || (node && !node->inDocument()))
355 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
356 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
359 void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
361 bool clearRenderTreeSelection = false;
362 bool clearDOMTreeSelection = false;
364 if (startRemoved || endRemoved) {
365 Position start = m_selection.start();
366 Position end = m_selection.end();
368 updatePositionForNodeRemoval(start, node);
370 updatePositionForNodeRemoval(end, node);
372 if (start.isNotNull() && end.isNotNull()) {
373 if (m_selection.isBaseFirst())
374 m_selection.setWithoutValidation(start, end);
376 m_selection.setWithoutValidation(end, start);
378 clearDOMTreeSelection = true;
380 clearRenderTreeSelection = true;
381 } else if (baseRemoved || extentRemoved) {
382 // The base and/or extent are about to be removed, but the start and end aren't.
383 // Change the base and extent to the start and end, but don't re-validate the
384 // selection, since doing so could move the start and end into the node
385 // that is about to be removed.
386 if (m_selection.isBaseFirst())
387 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
389 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
390 } else if (RefPtr<Range> range = m_selection.firstRange()) {
391 ExceptionCode ec = 0;
392 Range::CompareResults compareResult = range->compareNode(node, ec);
393 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
394 // If we did nothing here, when this node's renderer was destroyed, the rect that it
395 // occupied would be invalidated, but, selection gaps that change as a result of
396 // the removal wouldn't be invalidated.
397 // FIXME: Don't do so much unnecessary invalidation.
398 clearRenderTreeSelection = true;
402 if (clearRenderTreeSelection)
403 clearRenderViewSelection(m_selection.start());
405 if (clearDOMTreeSelection)
406 setSelection(VisibleSelection(), DoNotSetFocus);
409 static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
411 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
414 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
415 ASSERT(position.offsetInContainerNode() >= 0);
416 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
417 // Replacing text can be viewed as a deletion followed by insertion.
418 if (positionOffset >= offset && positionOffset <= offset + oldLength)
419 position.moveToOffset(offset);
421 // Adjust the offset if the position is after the end of the deleted contents
422 // (positionOffset > offset + oldLength) to avoid having a stale offset.
423 if (positionOffset > offset + oldLength)
424 position.moveToOffset(positionOffset - oldLength + newLength);
426 ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length());
429 static inline bool nodeIsDetachedFromDocument(Node* node)
432 Node* highest = highestAncestor(node);
433 return highest->nodeType() == Node::DOCUMENT_FRAGMENT_NODE && !highest->isShadowRoot();
436 void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
438 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
439 if (isNone() || !node || nodeIsDetachedFromDocument(node))
442 Position base = m_selection.base();
443 Position extent = m_selection.extent();
444 Position start = m_selection.start();
445 Position end = m_selection.end();
446 updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength);
447 updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength);
448 updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength);
449 updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength);
451 if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) {
452 VisibleSelection newSelection;
453 newSelection.setWithoutValidation(base, extent);
454 m_frame->document()->updateLayout();
455 setSelection(newSelection, DoNotSetFocus);
459 TextDirection FrameSelection::directionOfEnclosingBlock()
461 return WebCore::directionOfEnclosingBlock(m_selection.extent());
464 TextDirection FrameSelection::directionOfSelection()
466 InlineBox* startBox = 0;
467 InlineBox* endBox = 0;
469 if (m_selection.start().isNotNull())
470 m_selection.visibleStart().getInlineBoxAndOffset(startBox, unusedOffset);
471 if (m_selection.end().isNotNull())
472 m_selection.visibleEnd().getInlineBoxAndOffset(endBox, unusedOffset);
473 if (startBox && endBox && startBox->direction() == endBox->direction())
474 return startBox->direction();
476 return directionOfEnclosingBlock();
479 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
481 if (alter != AlterationExtend)
484 Position start = m_selection.start();
485 Position end = m_selection.end();
487 bool baseIsStart = true;
489 if (m_selection.isDirectional()) {
490 // Make base and extent match start and end so we extend the user-visible selection.
491 // This only matters for cases where base and extend point to different positions than
492 // start and end (e.g. after a double-click to select a word).
493 if (m_selection.isBaseFirst())
500 if (directionOfSelection() == LTR)
505 case DirectionForward:
509 if (directionOfSelection() == LTR)
514 case DirectionBackward:
520 m_selection.setBase(start);
521 m_selection.setExtent(end);
523 m_selection.setBase(end);
524 m_selection.setExtent(start);
528 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
530 Settings* settings = m_frame ? m_frame->settings() : 0;
531 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
532 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
533 // Linux and Windows always extend selections from the extent endpoint.
534 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
535 // base/extent always point to the same nodes as start/end, but which points
536 // to which depends on the value of isBaseFirst. Then this can be changed
537 // to just return m_sel.extent().
538 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
541 VisiblePosition FrameSelection::startForPlatform() const
543 return positionForPlatform(true);
546 VisiblePosition FrameSelection::endForPlatform() const
548 return positionForPlatform(false);
551 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
553 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
555 // The difference between modifyExtendingRight and modifyExtendingForward is:
556 // modifyExtendingForward always extends forward logically.
557 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
558 // it extends forward logically if the enclosing block is LTR direction,
559 // but it extends backward logically if the enclosing block is RTL direction.
560 switch (granularity) {
561 case CharacterGranularity:
562 if (directionOfEnclosingBlock() == LTR)
563 pos = pos.next(CannotCrossEditingBoundary);
565 pos = pos.previous(CannotCrossEditingBoundary);
567 case WordGranularity:
568 if (directionOfEnclosingBlock() == LTR)
569 pos = nextWordPosition(pos);
571 pos = previousWordPosition(pos);
574 if (directionOfEnclosingBlock() == LTR)
575 pos = modifyExtendingForward(granularity);
577 pos = modifyExtendingBackward(granularity);
579 case SentenceGranularity:
580 case LineGranularity:
581 case ParagraphGranularity:
582 case SentenceBoundary:
583 case ParagraphBoundary:
584 case DocumentBoundary:
585 // FIXME: implement all of the above?
586 pos = modifyExtendingForward(granularity);
592 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
594 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
595 switch (granularity) {
596 case CharacterGranularity:
597 pos = pos.next(CannotCrossEditingBoundary);
599 case WordGranularity:
600 pos = nextWordPosition(pos);
602 case SentenceGranularity:
603 pos = nextSentencePosition(pos);
605 case LineGranularity:
606 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
608 case ParagraphGranularity:
609 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
611 case SentenceBoundary:
612 pos = endOfSentence(endForPlatform());
615 pos = logicalEndOfLine(endForPlatform());
617 case ParagraphBoundary:
618 pos = endOfParagraph(endForPlatform());
620 case DocumentBoundary:
621 pos = endForPlatform();
622 if (isEditablePosition(pos.deepEquivalent()))
623 pos = endOfEditableContent(pos);
625 pos = endOfDocument(pos);
632 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
635 switch (granularity) {
636 case CharacterGranularity:
638 if (directionOfSelection() == LTR)
639 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
641 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
643 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
645 case WordGranularity: {
647 // Visual word movement relies on isWordTextBreak which is not implemented in WinCE and QT.
648 // https://bugs.webkit.org/show_bug.cgi?id=81136.
649 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
650 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
654 case SentenceGranularity:
655 case LineGranularity:
656 case ParagraphGranularity:
657 case SentenceBoundary:
658 case ParagraphBoundary:
659 case DocumentBoundary:
660 // FIXME: Implement all of the above.
661 pos = modifyMovingForward(granularity);
664 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
670 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
673 // FIXME: Stay in editable content for the less common granularities.
674 switch (granularity) {
675 case CharacterGranularity:
677 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
679 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
681 case WordGranularity:
682 pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
684 case SentenceGranularity:
685 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
687 case LineGranularity: {
688 // down-arrowing from a range selection that ends at the start of a line needs
689 // to leave the selection at that line start (no need to call nextLinePosition!)
690 pos = endForPlatform();
691 if (!isRange() || !isStartOfLine(pos))
692 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
695 case ParagraphGranularity:
696 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
698 case SentenceBoundary:
699 pos = endOfSentence(endForPlatform());
702 pos = logicalEndOfLine(endForPlatform());
704 case ParagraphBoundary:
705 pos = endOfParagraph(endForPlatform());
707 case DocumentBoundary:
708 pos = endForPlatform();
709 if (isEditablePosition(pos.deepEquivalent()))
710 pos = endOfEditableContent(pos);
712 pos = endOfDocument(pos);
718 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
720 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
722 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
723 // modifyExtendingBackward always extends backward logically.
724 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
725 // it extends backward logically if the enclosing block is LTR direction,
726 // but it extends forward logically if the enclosing block is RTL direction.
727 switch (granularity) {
728 case CharacterGranularity:
729 if (directionOfEnclosingBlock() == LTR)
730 pos = pos.previous(CannotCrossEditingBoundary);
732 pos = pos.next(CannotCrossEditingBoundary);
734 case WordGranularity:
735 if (directionOfEnclosingBlock() == LTR)
736 pos = previousWordPosition(pos);
738 pos = nextWordPosition(pos);
741 if (directionOfEnclosingBlock() == LTR)
742 pos = modifyExtendingBackward(granularity);
744 pos = modifyExtendingForward(granularity);
746 case SentenceGranularity:
747 case LineGranularity:
748 case ParagraphGranularity:
749 case SentenceBoundary:
750 case ParagraphBoundary:
751 case DocumentBoundary:
752 pos = modifyExtendingBackward(granularity);
758 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
760 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
762 // Extending a selection backward by word or character from just after a table selects
763 // the table. This "makes sense" from the user perspective, esp. when deleting.
764 // It was done here instead of in VisiblePosition because we want VPs to iterate
766 switch (granularity) {
767 case CharacterGranularity:
768 pos = pos.previous(CannotCrossEditingBoundary);
770 case WordGranularity:
771 pos = previousWordPosition(pos);
773 case SentenceGranularity:
774 pos = previousSentencePosition(pos);
776 case LineGranularity:
777 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
779 case ParagraphGranularity:
780 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
782 case SentenceBoundary:
783 pos = startOfSentence(startForPlatform());
786 pos = logicalStartOfLine(startForPlatform());
788 case ParagraphBoundary:
789 pos = startOfParagraph(startForPlatform());
791 case DocumentBoundary:
792 pos = startForPlatform();
793 if (isEditablePosition(pos.deepEquivalent()))
794 pos = startOfEditableContent(pos);
796 pos = startOfDocument(pos);
802 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
805 switch (granularity) {
806 case CharacterGranularity:
808 if (directionOfSelection() == LTR)
809 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
811 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
813 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
815 case WordGranularity: {
817 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
818 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
822 case SentenceGranularity:
823 case LineGranularity:
824 case ParagraphGranularity:
825 case SentenceBoundary:
826 case ParagraphBoundary:
827 case DocumentBoundary:
828 // FIXME: Implement all of the above.
829 pos = modifyMovingBackward(granularity);
832 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
838 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
841 switch (granularity) {
842 case CharacterGranularity:
844 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
846 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
848 case WordGranularity:
849 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
851 case SentenceGranularity:
852 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
854 case LineGranularity:
855 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
857 case ParagraphGranularity:
858 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
860 case SentenceBoundary:
861 pos = startOfSentence(startForPlatform());
864 pos = logicalStartOfLine(startForPlatform());
866 case ParagraphBoundary:
867 pos = startOfParagraph(startForPlatform());
869 case DocumentBoundary:
870 pos = startForPlatform();
871 if (isEditablePosition(pos.deepEquivalent()))
872 pos = startOfEditableContent(pos);
874 pos = startOfDocument(pos);
880 static bool isBoundary(TextGranularity granularity)
882 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
885 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
887 if (userTriggered == UserTriggered) {
888 FrameSelection trialFrameSelection;
889 trialFrameSelection.setSelection(m_selection);
890 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
892 bool change = shouldChangeSelection(trialFrameSelection.selection());
896 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
900 willBeModified(alter, direction);
902 bool wasRange = m_selection.isRange();
903 Position originalStartPosition = m_selection.start();
904 VisiblePosition position;
907 if (alter == AlterationMove)
908 position = modifyMovingRight(granularity);
910 position = modifyExtendingRight(granularity);
912 case DirectionForward:
913 if (alter == AlterationExtend)
914 position = modifyExtendingForward(granularity);
916 position = modifyMovingForward(granularity);
919 if (alter == AlterationMove)
920 position = modifyMovingLeft(granularity);
922 position = modifyExtendingLeft(granularity);
924 case DirectionBackward:
925 if (alter == AlterationExtend)
926 position = modifyExtendingBackward(granularity);
928 position = modifyMovingBackward(granularity);
932 if (position.isNull())
935 if (isSpatialNavigationEnabled(m_frame))
936 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
939 // Some of the above operations set an xPosForVerticalArrowNavigation.
940 // Setting a selection will clear it, so save it to possibly restore later.
941 // Note: the START position type is arbitrary because it is unused, it would be
942 // the requested position type if there were no xPosForVerticalArrowNavigation set.
943 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
944 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
948 moveTo(position, userTriggered);
950 case AlterationExtend:
951 if (!m_selection.isCaret()
952 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
953 && m_frame && !m_frame->editor()->behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
954 // Don't let the selection go across the base position directly. Needed to match mac
955 // behavior when, for instance, word-selecting backwards starting with the caret in
956 // the middle of a word and then word-selecting forward, leaving the caret in the
957 // same place where it was, instead of directly selecting to the end of the word.
958 VisibleSelection newSelection = m_selection;
959 newSelection.setExtent(position);
960 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
961 position = m_selection.base();
964 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
965 // base in place and moving the extent. Matches NSTextView.
966 if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
967 setExtent(position, userTriggered);
969 TextDirection textDirection = directionOfEnclosingBlock();
970 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
971 setEnd(position, userTriggered);
973 setStart(position, userTriggered);
978 if (granularity == LineGranularity || granularity == ParagraphGranularity)
979 m_xPosForVerticalArrowNavigation = x;
981 if (userTriggered == UserTriggered)
982 m_granularity = CharacterGranularity;
984 setCaretRectNeedsUpdate();
989 // FIXME: Maybe baseline would be better?
990 static bool absoluteCaretY(const VisiblePosition &c, int &y)
992 IntRect rect = c.absoluteCaretBounds();
995 y = rect.y() + rect.height() / 2;
999 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1001 if (!verticalDistance)
1004 if (userTriggered == UserTriggered) {
1005 FrameSelection trialFrameSelection;
1006 trialFrameSelection.setSelection(m_selection);
1007 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1009 bool change = shouldChangeSelection(trialFrameSelection.selection());
1014 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1016 VisiblePosition pos;
1017 LayoutUnit xPos = 0;
1019 case AlterationMove:
1020 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1021 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1022 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1024 case AlterationExtend:
1025 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1026 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1027 m_selection.setAffinity(DOWNSTREAM);
1032 if (!absoluteCaretY(pos, startY))
1034 if (direction == DirectionUp)
1038 VisiblePosition result;
1039 VisiblePosition next;
1040 for (VisiblePosition p = pos; ; p = next) {
1041 if (direction == DirectionUp)
1042 next = previousLinePosition(p, xPos);
1044 next = nextLinePosition(p, xPos);
1046 if (next.isNull() || next == p)
1049 if (!absoluteCaretY(next, nextY))
1051 if (direction == DirectionUp)
1053 if (nextY - startY > static_cast<int>(verticalDistance))
1055 if (nextY >= lastY) {
1061 if (result.isNull())
1065 case AlterationMove:
1066 moveTo(result, userTriggered, align);
1068 case AlterationExtend:
1069 setExtent(result, userTriggered);
1073 if (userTriggered == UserTriggered)
1074 m_granularity = CharacterGranularity;
1076 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1081 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1091 pos = m_selection.start();
1094 pos = m_selection.end();
1097 pos = m_selection.base();
1100 pos = m_selection.extent();
1104 Frame* frame = pos.anchorNode()->document()->frame();
1108 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1109 VisiblePosition visiblePosition(pos, m_selection.affinity());
1110 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1111 // after the selection is created and before this function is called.
1112 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1113 m_xPosForVerticalArrowNavigation = x;
1115 x = m_xPosForVerticalArrowNavigation;
1120 void FrameSelection::clear()
1122 m_granularity = CharacterGranularity;
1123 setSelection(VisibleSelection());
1126 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1128 if (m_selection.isBaseFirst())
1129 setBase(pos, trigger);
1131 setExtent(pos, trigger);
1134 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1136 if (m_selection.isBaseFirst())
1137 setExtent(pos, trigger);
1139 setBase(pos, trigger);
1142 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1144 const bool selectionHasDirection = true;
1145 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1148 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1150 const bool selectionHasDirection = true;
1151 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1154 void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1156 const bool selectionHasDirection = true;
1157 setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1160 void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1162 const bool selectionHasDirection = true;
1163 setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1166 void CaretBase::clearCaretRect()
1168 m_caretLocalRect = LayoutRect();
1171 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
1173 document->updateStyleIfNeeded();
1174 m_caretLocalRect = LayoutRect();
1176 m_caretRectNeedsUpdate = false;
1178 if (caretPosition.isNull())
1181 ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
1183 // First compute a rect local to the renderer at the selection start.
1184 RenderObject* renderer;
1185 LayoutRect localRect = caretPosition.localCaretRect(renderer);
1187 // Get the renderer that will be responsible for painting the caret
1188 // (which is either the renderer we just found, or one of its containers).
1189 RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
1191 // Compute an offset between the renderer and the caretPainter.
1192 bool unrooted = false;
1193 while (renderer != caretPainter) {
1194 RenderObject* containerObject = renderer->container();
1195 if (!containerObject) {
1199 localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
1200 renderer = containerObject;
1204 m_caretLocalRect = localRect;
1209 static inline bool caretRendersInsideNode(Node* node)
1211 return node && !isTableElement(node) && !editingIgnoresContent(node);
1214 RenderObject* CaretBase::caretRenderer(Node* node) const
1219 RenderObject* renderer = node->renderer();
1223 // if caretNode is a block and caret is inside it then caret should be painted by that block
1224 bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
1225 return paintedByBlock ? renderer : renderer->containingBlock();
1228 RenderObject* FrameSelection::caretRenderer() const
1230 return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1233 RenderObject* DragCaretController::caretRenderer() const
1235 return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
1238 LayoutRect FrameSelection::localCaretRect()
1240 if (shouldUpdateCaretRect()) {
1241 if (!isCaret() || m_selection.start().isOrphan() || m_selection.end().isOrphan())
1243 else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
1244 m_absCaretBoundsDirty = true;
1247 return localCaretRectWithoutUpdate();
1250 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1251 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect, bool useTransforms) const
1253 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
1256 RenderObject* caretPainter = caretRenderer(node);
1260 LayoutRect localRect(rect);
1261 if (caretPainter->isBox())
1262 toRenderBox(caretPainter)->flipForWritingMode(localRect);
1264 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1266 return caretPainter->localToAbsoluteQuad(FloatRect(localRect), false, 0, false).enclosingBoundingBox();
1269 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
1272 IntRect FrameSelection::absoluteCaretBounds()
1274 recomputeCaretRect();
1275 return m_absCaretBounds;
1278 static LayoutRect repaintRectForCaret(LayoutRect caret)
1280 if (caret.isEmpty())
1281 return LayoutRect();
1282 // Ensure that the dirty rect intersects the block that paints the caret even in the case where
1283 // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
1288 IntRect CaretBase::caretRepaintRect(Node* node) const
1290 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1291 return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()), false);
1293 return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()));
1297 bool FrameSelection::recomputeCaretRect()
1299 if (!shouldUpdateCaretRect())
1305 FrameView* v = m_frame->document()->view();
1309 LayoutRect oldRect = localCaretRectWithoutUpdate();
1310 LayoutRect newRect = localCaretRect();
1311 if (oldRect == newRect && !m_absCaretBoundsDirty)
1314 IntRect oldAbsCaretBounds = m_absCaretBounds;
1315 // FIXME: Rename m_caretRect to m_localCaretRect.
1316 m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1317 m_absCaretBoundsDirty = false;
1319 if (oldAbsCaretBounds == m_absCaretBounds)
1322 #if ENABLE(TEXT_CARET)
1323 IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
1326 // We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
1327 m_absoluteCaretRepaintBounds = caretRepaintRect(m_selection.start().deprecatedNode());
1329 #if ENABLE(TEXT_CARET)
1330 if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
1331 // FIXME: make caret repainting container-aware.
1332 view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
1333 if (shouldRepaintCaret(view, isContentEditable()))
1334 view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
1340 bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
1343 Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
1344 bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
1345 return (caretBrowsing || isContentEditable);
1348 void FrameSelection::invalidateCaretRect()
1353 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1356 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
1358 // EDIT FIXME: This is an unfortunate hack.
1359 // Basically, we can't trust this layout position since we
1360 // can't guarantee that the check to see if we are in unrendered
1361 // content will work at this point. We may have to wait for
1362 // a layout and re-render of the document to happen. So, resetting this
1363 // flag will cause another caret layout to happen the first time
1364 // that we try to paint the caret after this call. That one will work since
1365 // it happens after the document has accounted for any editing
1366 // changes which may have been done.
1367 // And, we need to leave this layout here so the caret moves right
1368 // away after clicking.
1369 m_caretRectNeedsUpdate = true;
1371 if (!caretRectChanged) {
1372 RenderView* view = toRenderView(node->document()->renderer());
1373 if (view && shouldRepaintCaret(view, node->isContentEditable()))
1374 view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(node), false);
1378 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1380 if (m_selection.isCaret() && m_caretPaint)
1381 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1384 void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1386 #if ENABLE(TEXT_CARET)
1387 if (m_caretVisibility == Hidden)
1390 LayoutRect drawingRect = localCaretRectWithoutUpdate();
1391 RenderObject* renderer = caretRenderer(node);
1392 if (renderer && renderer->isBox())
1393 toRenderBox(renderer)->flipForWritingMode(drawingRect);
1394 drawingRect.moveBy(paintOffset);
1395 LayoutRect caret = intersection(drawingRect, clipRect);
1396 if (caret.isEmpty())
1399 Color caretColor = Color::black;
1400 ColorSpace colorSpace = ColorSpaceDeviceRGB;
1401 Element* element = node->rootEditableElement();
1402 if (element && element->renderer()) {
1403 caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
1404 colorSpace = element->renderer()->style()->colorSpace();
1407 context->fillRect(caret, caretColor, colorSpace);
1410 UNUSED_PARAM(context);
1411 UNUSED_PARAM(paintOffset);
1412 UNUSED_PARAM(clipRect);
1416 void FrameSelection::debugRenderer(RenderObject *r, bool selected) const
1418 if (r->node()->isElementNode()) {
1419 Element* element = static_cast<Element *>(r->node());
1420 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
1421 } else if (r->isText()) {
1422 RenderText* textRenderer = toRenderText(r);
1423 if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
1424 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1428 static const int max = 36;
1429 String text = textRenderer->text();
1430 int textLength = text.length();
1433 if (r->node() == m_selection.start().containerNode())
1434 offset = m_selection.start().computeOffsetInContainerNode();
1435 else if (r->node() == m_selection.end().containerNode())
1436 offset = m_selection.end().computeOffsetInContainerNode();
1439 InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
1440 text = text.substring(box->start(), box->len());
1446 // text is shorter than max
1447 if (textLength < max) {
1450 } else if (pos - mid < 0) {
1451 // too few characters to left
1452 show = text.left(max - 3) + "...";
1454 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1455 // enough characters on each side
1456 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1459 // too few characters on right
1460 show = "..." + text.right(max - 3);
1461 caret = pos - (textLength - show.length());
1464 show.replace('\n', ' ');
1465 show.replace('\r', ' ');
1466 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1467 fprintf(stderr, " ");
1468 for (int i = 0; i < caret; i++)
1469 fprintf(stderr, " ");
1470 fprintf(stderr, "^\n");
1472 if ((int)text.length() > max)
1473 text = text.left(max - 3) + "...";
1475 text = text.left(max);
1476 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1481 bool FrameSelection::contains(const LayoutPoint& point)
1483 Document* document = m_frame->document();
1485 // Treat a collapsed selection like no selection.
1488 if (!document->renderer())
1491 HitTestRequest request(HitTestRequest::ReadOnly |
1492 HitTestRequest::Active);
1493 HitTestResult result(point);
1494 document->renderView()->hitTest(request, result);
1495 Node* innerNode = result.innerNode();
1496 if (!innerNode || !innerNode->renderer())
1499 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1500 if (visiblePos.isNull())
1503 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1506 Position start(m_selection.visibleStart().deepEquivalent());
1507 Position end(m_selection.visibleEnd().deepEquivalent());
1508 Position p(visiblePos.deepEquivalent());
1510 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1513 // Workaround for the fact that it's hard to delete a frame.
1514 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1515 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1516 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1517 // mouse or the keyboard after setting the selection.
1518 void FrameSelection::selectFrameElementInParentIfFullySelected()
1520 // Find the parent frame; if there is none, then we have nothing to do.
1521 Frame* parent = m_frame->tree()->parent();
1524 Page* page = m_frame->page();
1528 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1531 if (!isStartOfDocument(selection().visibleStart()))
1533 if (!isEndOfDocument(selection().visibleEnd()))
1536 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1537 Element* ownerElement = m_frame->ownerElement();
1540 ContainerNode* ownerElementParent = ownerElement->parentNode();
1541 if (!ownerElementParent)
1544 // 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.
1545 if (!ownerElementParent->rendererIsEditable())
1548 // Create compute positions before and after the element.
1549 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1550 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1551 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1553 // Focus on the parent frame, and then select from before this element to after.
1554 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1555 if (parent->selection()->shouldChangeSelection(newSelection)) {
1556 page->focusController()->setFocusedFrame(parent);
1557 parent->selection()->setSelection(newSelection);
1561 void FrameSelection::selectAll()
1563 Document* document = m_frame->document();
1565 if (document->focusedNode() && document->focusedNode()->hasTagName(selectTag)) {
1566 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedNode());
1567 if (selectElement->canSelectAll()) {
1568 selectElement->selectAll();
1573 RefPtr<Node> root = 0;
1574 Node* selectStartTarget = 0;
1575 if (isContentEditable()) {
1576 root = highestEditableRoot(m_selection.start());
1577 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1578 selectStartTarget = shadowRoot->shadowAncestorNode();
1580 selectStartTarget = root.get();
1582 root = m_selection.nonBoundaryShadowTreeRootNode();
1584 selectStartTarget = root->shadowAncestorNode();
1586 root = document->documentElement();
1587 selectStartTarget = document->body();
1593 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
1596 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1598 if (shouldChangeSelection(newSelection))
1599 setSelection(newSelection);
1601 selectFrameElementInParentIfFullySelected();
1602 notifyRendererOfSelectionChange(UserTriggered);
1605 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1607 if (!range || !range->startContainer() || !range->endContainer())
1609 ASSERT(range->startContainer()->document() == range->endContainer()->document());
1611 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1613 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1614 // they start at the beginning of the next line instead
1615 ExceptionCode ec = 0;
1616 bool collapsed = range->collapsed(ec);
1620 // FIXME: Can we provide extentAffinity?
1621 VisiblePosition visibleStart(range->startPosition(), collapsed ? affinity : DOWNSTREAM);
1622 VisiblePosition visibleEnd(range->endPosition(), SEL_DEFAULT_AFFINITY);
1623 setSelection(VisibleSelection(visibleStart, visibleEnd), ClearTypingStyle | (closeTyping ? CloseTyping : 0));
1627 bool FrameSelection::isInPasswordField() const
1629 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1630 return textControl && textControl->hasTagName(inputTag) && static_cast<HTMLInputElement*>(textControl)->isPasswordField();
1633 void FrameSelection::focusedOrActiveStateChanged()
1635 bool activeAndFocused = isFocusedAndActive();
1637 // Because RenderObject::selectionBackgroundColor() and
1638 // RenderObject::selectionForegroundColor() check if the frame is active,
1639 // we have to update places those colors were painted.
1640 if (RenderView* view = toRenderView(m_frame->document()->renderer()))
1641 view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
1643 // Caret appears in the active frame.
1644 if (activeAndFocused)
1645 setSelectionFromNone();
1646 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1648 // Update for caps lock state
1649 m_frame->eventHandler()->capsLockStateMayHaveChanged();
1651 // Because StyleResolver::checkOneSelector() and
1652 // RenderTheme::isFocused() check if the frame is active, we have to
1653 // update style and theme state that depended on those.
1654 if (Node* node = m_frame->document()->focusedNode()) {
1655 node->setNeedsStyleRecalc();
1656 if (RenderObject* renderer = node->renderer())
1657 if (renderer && renderer->style()->hasAppearance())
1658 renderer->theme()->stateChanged(renderer, FocusState);
1661 // Secure keyboard entry is set by the active frame.
1662 if (m_frame->document()->useSecureKeyboardEntryWhenActive())
1663 setUseSecureKeyboardEntry(activeAndFocused);
1666 void FrameSelection::pageActivationChanged()
1668 focusedOrActiveStateChanged();
1671 void FrameSelection::updateSecureKeyboardEntryIfActive()
1673 if (m_frame->document() && isFocusedAndActive())
1674 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1677 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1680 enableSecureTextInput();
1682 disableSecureTextInput();
1685 void FrameSelection::setFocused(bool flag)
1687 if (m_focused == flag)
1691 focusedOrActiveStateChanged();
1694 bool FrameSelection::isFocusedAndActive() const
1696 return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
1699 inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame)
1701 return frame->editor()->lastEditCommand() && frame->editor()->lastEditCommand()->shouldStopCaretBlinking();
1704 void FrameSelection::updateAppearance()
1706 #if ENABLE(TEXT_CARET)
1707 bool caretRectChanged = recomputeCaretRect();
1709 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1710 bool shouldBlink = caretIsVisible() && isCaret() && (isContentEditable() || caretBrowsing);
1712 // If the caret moved, stop the blink timer so we can restart with a
1713 // black caret in the new location.
1714 if (caretRectChanged || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame))
1715 m_caretBlinkTimer.stop();
1717 // Start blinking with a black caret. Be sure not to restart if we're
1718 // already blinking in the right location.
1719 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1720 if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
1721 m_caretBlinkTimer.startRepeating(blinkInterval);
1723 if (!m_caretPaint) {
1724 m_caretPaint = true;
1725 invalidateCaretRect();
1730 // We need to update style in case the node containing the selection is made display:none.
1731 m_frame->document()->updateStyleIfNeeded();
1733 RenderView* view = m_frame->contentRenderer();
1737 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1738 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1739 VisibleSelection selection(m_selection.visibleStart(), m_selection.visibleEnd());
1741 if (!selection.isRange()) {
1742 view->clearSelection();
1746 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1747 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1748 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1749 // and will fill the gap before 'bar'.
1750 Position startPos = selection.start();
1751 Position candidate = startPos.downstream();
1752 if (candidate.isCandidate())
1753 startPos = candidate;
1754 Position endPos = selection.end();
1755 candidate = endPos.upstream();
1756 if (candidate.isCandidate())
1759 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1760 // because we don't yet notify the FrameSelection of text removal.
1761 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1762 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1763 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1764 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1768 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1770 if (caretVisibility() == visibility)
1772 clearCaretRectIfNeeded();
1773 CaretBase::setCaretVisibility(visibility);
1777 void FrameSelection::clearCaretRectIfNeeded()
1779 #if ENABLE(TEXT_CARET)
1782 m_caretPaint = false;
1783 invalidateCaretRect();
1787 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1789 #if ENABLE(TEXT_CARET)
1790 ASSERT(caretIsVisible());
1792 bool caretPaint = m_caretPaint;
1793 if (isCaretBlinkingSuspended() && caretPaint)
1795 m_caretPaint = !caretPaint;
1796 invalidateCaretRect();
1800 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1802 m_frame->document()->updateStyleIfNeeded();
1804 if (!rootEditableElement())
1807 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1808 textControl->selectionChanged(userTriggered == UserTriggered);
1811 // Helper function that tells whether a particular node is an element that has an entire
1812 // Frame and FrameView, a <frame>, <iframe>, or <object>.
1813 static bool isFrameElement(const Node* n)
1817 RenderObject* renderer = n->renderer();
1818 if (!renderer || !renderer->isWidget())
1820 Widget* widget = toRenderWidget(renderer)->widget();
1821 return widget && widget->isFrameView();
1824 void FrameSelection::setFocusedNodeIfNeeded()
1826 if (isNone() || !isFocused())
1829 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1830 if (caretBrowsing) {
1831 if (Node* anchor = enclosingAnchorElement(base())) {
1832 m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
1837 if (Node* target = rootEditableElement()) {
1838 // Walk up the DOM tree to search for a node to focus.
1840 // We don't want to set focus on a subframe when selecting in a parent frame,
1841 // so add the !isFrameElement check here. There's probably a better way to make this
1842 // work in the long term, but this is the safest fix at this time.
1843 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
1844 m_frame->page()->focusController()->setFocusedNode(target, m_frame);
1847 target = target->parentOrHostNode();
1849 m_frame->document()->setFocusedNode(0);
1853 m_frame->page()->focusController()->setFocusedNode(0, m_frame);
1856 void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1858 #if ENABLE(TEXT_CARET)
1859 if (m_position.deepEquivalent().deprecatedNode()->document()->frame() == frame)
1860 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
1862 UNUSED_PARAM(frame);
1864 UNUSED_PARAM(paintOffset);
1865 UNUSED_PARAM(clipRect);
1869 PassRefPtr<StylePropertySet> FrameSelection::copyTypingStyle() const
1871 if (!m_typingStyle || !m_typingStyle->style())
1873 return m_typingStyle->style()->copy();
1876 bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
1878 return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
1881 FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
1883 RenderView* root = m_frame->contentRenderer();
1884 FrameView* view = m_frame->view();
1886 return LayoutRect();
1888 LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent);
1889 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1892 void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
1894 RenderView* root = m_frame->contentRenderer();
1898 FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
1900 Vector<FloatQuad> quads;
1901 toNormalizedRange()->textQuads(quads, true);
1903 size_t size = quads.size();
1904 for (size_t i = 0; i < size; ++i) {
1905 FloatRect intersectionRect = intersection(quads[i].enclosingBoundingBox(), visibleContentRect);
1906 if (!intersectionRect.isEmpty())
1907 rectangles.append(intersectionRect);
1911 // Scans logically forward from "start", including any child frames.
1912 static HTMLFormElement* scanForForm(Node* start)
1914 for (Node* node = start; node; node = node->traverseNextNode()) {
1915 if (node->hasTagName(formTag))
1916 return static_cast<HTMLFormElement*>(node);
1917 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1918 return static_cast<HTMLFormControlElement*>(node)->form();
1919 if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
1920 Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
1921 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1928 // We look for either the form containing the current focus, or for one immediately after it
1929 HTMLFormElement* FrameSelection::currentForm() const
1931 // Start looking either at the active (first responder) node, or where the selection is.
1932 Node* start = m_frame->document()->focusedNode();
1934 start = this->start().deprecatedNode();
1936 // Try walking up the node tree to find a form element.
1938 for (node = start; node; node = node->parentNode()) {
1939 if (node->hasTagName(formTag))
1940 return static_cast<HTMLFormElement*>(node);
1941 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1942 return static_cast<HTMLFormControlElement*>(node)->form();
1945 // Try walking forward in the node tree to find a form element.
1946 return scanForForm(start);
1949 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1953 switch (selectionType()) {
1954 case VisibleSelection::NoSelection:
1956 case VisibleSelection::CaretSelection:
1957 rect = absoluteCaretBounds();
1959 case VisibleSelection::RangeSelection:
1960 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1964 Position start = this->start();
1965 ASSERT(start.deprecatedNode());
1966 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1967 // FIXME: This code only handles scrolling the startContainer's layer, but
1968 // the selection rect could intersect more than just that.
1969 // See <rdar://problem/4799899>.
1970 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1975 void FrameSelection::setSelectionFromNone()
1977 // Put a caret inside the body if the entire frame is editable (either the
1978 // entire WebView is editable or designMode is on for this document).
1980 Document* document = m_frame->document();
1981 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1982 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1985 Node* node = document->documentElement();
1986 while (node && !node->hasTagName(bodyTag))
1987 node = node->traverseNextNode();
1989 setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
1992 bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
1994 return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
1997 bool FrameSelection::dispatchSelectStart()
1999 Node* selectStartTarget = m_selection.extent().containerNode();
2000 if (!selectStartTarget)
2003 return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
2006 inline bool FrameSelection::visualWordMovementEnabled() const
2008 Settings* settings = m_frame ? m_frame->settings() : 0;
2009 return settings && settings->visualWordMovementEnabled();
2011 #if ENABLE(TIZEN_CONTEXT_MENU_SELECT)
2012 bool FrameSelection::canEditableSelectRange() const
2014 RefPtr<Node> editableRoot = 0;
2015 if (isContentEditable())
2016 editableRoot = highestEditableRoot(m_selection.start());
2020 if (!comparePositions(firstPositionInNode(editableRoot.get()), lastPositionInNode(editableRoot.get())))
2029 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
2031 m_selection.formatForDebugger(buffer, length);
2034 void FrameSelection::showTreeForThis() const
2036 m_selection.showTreeForThis();
2045 void showTree(const WebCore::FrameSelection& sel)
2047 sel.showTreeForThis();
2050 void showTree(const WebCore::FrameSelection* sel)
2053 sel->showTreeForThis();