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 "core/editing/FrameSelection.h"
30 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "core/accessibility/AXObjectCache.h"
33 #include "core/css/StylePropertySet.h"
34 #include "core/dom/CharacterData.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/Element.h"
37 #include "core/dom/ElementTraversal.h"
38 #include "core/dom/NodeTraversal.h"
39 #include "core/dom/Text.h"
40 #include "core/editing/Editor.h"
41 #include "core/editing/InputMethodController.h"
42 #include "core/editing/RenderedPosition.h"
43 #include "core/editing/SpellChecker.h"
44 #include "core/editing/TextIterator.h"
45 #include "core/editing/TypingCommand.h"
46 #include "core/editing/VisibleUnits.h"
47 #include "core/editing/htmlediting.h"
48 #include "core/frame/DOMWindow.h"
49 #include "core/frame/LocalFrame.h"
50 #include "core/html/HTMLBodyElement.h"
51 #include "core/html/HTMLFormElement.h"
52 #include "core/html/HTMLFrameElementBase.h"
53 #include "core/html/HTMLInputElement.h"
54 #include "core/html/HTMLSelectElement.h"
55 #include "core/page/EditorClient.h"
56 #include "core/page/EventHandler.h"
57 #include "core/page/FocusController.h"
58 #include "core/page/FrameTree.h"
59 #include "core/frame/FrameView.h"
60 #include "core/page/Page.h"
61 #include "core/frame/Settings.h"
62 #include "core/page/SpatialNavigation.h"
63 #include "core/rendering/HitTestRequest.h"
64 #include "core/rendering/HitTestResult.h"
65 #include "core/rendering/InlineTextBox.h"
66 #include "core/rendering/RenderLayer.h"
67 #include "core/rendering/RenderText.h"
68 #include "core/rendering/RenderTheme.h"
69 #include "core/rendering/RenderView.h"
70 #include "core/rendering/RenderWidget.h"
71 #include "platform/SecureTextInput.h"
72 #include "platform/geometry/FloatQuad.h"
73 #include "platform/graphics/GraphicsContext.h"
74 #include "wtf/text/CString.h"
80 using namespace HTMLNames;
82 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
84 return LayoutUnit::min();
87 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
89 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
92 FrameSelection::FrameSelection(LocalFrame* frame)
94 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
95 , m_observingVisibleSelection(false)
96 , m_granularity(CharacterGranularity)
97 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
98 , m_absCaretBoundsDirty(true)
100 , m_isCaretBlinkingSuspended(false)
101 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
102 , m_shouldShowBlockCursor(false)
104 if (shouldAlwaysUseDirectionalSelection(m_frame))
105 m_selection.setIsDirectional(true);
108 FrameSelection::~FrameSelection()
110 stopObservingVisibleSelectionChangeIfNecessary();
113 Element* FrameSelection::rootEditableElementOrDocumentElement() const
115 Element* selectionRoot = m_selection.rootEditableElement();
116 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
119 Node* FrameSelection::rootEditableElementOrTreeScopeRootNode() const
121 Element* selectionRoot = m_selection.rootEditableElement();
123 return selectionRoot;
125 Node* node = m_selection.base().containerNode();
126 return node ? &node->treeScope().rootNode() : 0;
129 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
131 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
132 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
135 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
137 const bool selectionHasDirection = true;
138 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
139 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
142 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
144 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
145 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
148 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
150 RenderedPosition base(visibleBase);
151 RenderedPosition extent(visibleExtent);
153 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
156 if (base.atLeftBoundaryOfBidiRun()) {
157 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
158 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
159 visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun());
165 if (base.atRightBoundaryOfBidiRun()) {
166 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
167 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
168 visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun());
174 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
175 visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun());
179 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
180 visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun());
185 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
186 EndPointsAdjustmentMode endpointsAdjustmentMode)
188 VisibleSelection newSelection = passedNewSelection;
189 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
191 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
192 VisiblePosition newBase = base;
193 VisiblePosition extent = newSelection.visibleExtent();
194 VisiblePosition newExtent = extent;
195 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
196 adjustEndpointsAtBidiBoundary(newBase, newExtent);
198 if (newBase != base || newExtent != extent) {
199 m_originalBase = base;
200 newSelection.setBase(newBase);
201 newSelection.setExtent(newExtent);
202 } else if (m_originalBase.isNotNull()) {
203 if (m_selection.base() == newSelection.base())
204 newSelection.setBase(m_originalBase);
205 m_originalBase.clear();
208 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
209 if (m_selection == newSelection)
212 setSelection(newSelection, granularity);
215 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
217 bool closeTyping = options & CloseTyping;
218 bool shouldClearTypingStyle = options & ClearTypingStyle;
219 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
221 VisibleSelection s = newSelection;
222 if (shouldAlwaysUseDirectionalSelection(m_frame))
223 s.setIsDirectional(true);
230 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
231 // if document->frame() == m_frame we can get into an infinite loop
232 if (s.base().anchorNode()) {
233 Document& document = *s.base().document();
234 if (document.frame() && document.frame() != m_frame && document != m_frame->document()) {
235 RefPtr<LocalFrame> guard = document.frame();
236 document.frame()->selection().setSelection(s, options, align, granularity);
237 // It's possible that during the above set selection, this FrameSelection has been modified by
238 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
239 // the frame is about to be destroyed. If this is the case, clear our selection.
240 if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange())
246 m_granularity = granularity;
249 TypingCommand::closeTyping(m_frame);
251 if (shouldClearTypingStyle)
254 if (m_selection == s) {
255 // Even if selection was not changed, selection offsets may have been changed.
256 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
257 notifyRendererOfSelectionChange(userTriggered);
261 VisibleSelection oldSelection = m_selection;
264 setCaretRectNeedsUpdate();
266 if (!s.isNone() && !(options & DoNotSetFocus))
267 setFocusedNodeIfNeeded();
269 if (!(options & DoNotUpdateAppearance)) {
270 m_frame->document()->updateLayoutIgnorePendingStylesheets();
272 // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html
273 DisableCompositingQueryAsserts disabler;
277 // Always clear the x position used for vertical arrow navigation.
278 // It will be restored by the vertical arrow navigation code if necessary.
279 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
280 selectFrameElementInParentIfFullySelected();
281 notifyRendererOfSelectionChange(userTriggered);
282 m_frame->editor().respondToChangedSelection(oldSelection, options);
283 if (userTriggered == UserTriggered) {
284 ScrollAlignment alignment;
286 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
287 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
289 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
291 revealSelection(alignment, RevealExtent);
294 notifyAccessibilityForSelectionChange();
295 m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange));
298 static bool removingNodeRemovesPosition(Node& node, const Position& position)
300 if (!position.anchorNode())
303 if (position.anchorNode() == node)
306 if (!node.isElementNode())
309 Element& element = toElement(node);
310 return element.containsIncludingShadowDOM(position.anchorNode());
313 void FrameSelection::nodeWillBeRemoved(Node& node)
315 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
316 // the selection in the document that created the fragment needs no adjustment.
317 if (isNone() || !node.inActiveDocument())
320 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
321 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
324 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
326 ASSERT(node.document().isActive());
328 bool clearRenderTreeSelection = false;
329 bool clearDOMTreeSelection = false;
331 if (startRemoved || endRemoved) {
332 Position start = m_selection.start();
333 Position end = m_selection.end();
335 updatePositionForNodeRemoval(start, node);
337 updatePositionForNodeRemoval(end, node);
339 if (start.isNotNull() && end.isNotNull()) {
340 if (m_selection.isBaseFirst())
341 m_selection.setWithoutValidation(start, end);
343 m_selection.setWithoutValidation(end, start);
345 clearDOMTreeSelection = true;
347 clearRenderTreeSelection = true;
348 } else if (baseRemoved || extentRemoved) {
349 // The base and/or extent are about to be removed, but the start and end aren't.
350 // Change the base and extent to the start and end, but don't re-validate the
351 // selection, since doing so could move the start and end into the node
352 // that is about to be removed.
353 if (m_selection.isBaseFirst())
354 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
356 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
357 } else if (RefPtrWillBeRawPtr<Range> range = m_selection.firstRange()) {
358 TrackExceptionState exceptionState;
359 Range::CompareResults compareResult = range->compareNode(&node, exceptionState);
360 if (!exceptionState.hadException() && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
361 // If we did nothing here, when this node's renderer was destroyed, the rect that it
362 // occupied would be invalidated, but, selection gaps that change as a result of
363 // the removal wouldn't be invalidated.
364 // FIXME: Don't do so much unnecessary invalidation.
365 clearRenderTreeSelection = true;
369 if (clearRenderTreeSelection)
370 m_selection.start().document()->renderView()->clearSelection();
372 if (clearDOMTreeSelection)
373 setSelection(VisibleSelection(), DoNotSetFocus);
376 static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
378 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
381 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
382 ASSERT(position.offsetInContainerNode() >= 0);
383 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
384 // Replacing text can be viewed as a deletion followed by insertion.
385 if (positionOffset >= offset && positionOffset <= offset + oldLength)
386 positionOffset = offset;
388 // Adjust the offset if the position is after the end of the deleted contents
389 // (positionOffset > offset + oldLength) to avoid having a stale offset.
390 if (positionOffset > offset + oldLength)
391 positionOffset = positionOffset - oldLength + newLength;
393 ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length());
394 // CharacterNode in VisibleSelection must be Text node, because Comment
395 // and ProcessingInstruction node aren't visible.
396 return Position(toText(node), positionOffset);
399 void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
401 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
402 if (isNone() || !node || !node->inDocument())
405 Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength);
406 Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength);
407 Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength);
408 Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength);
409 updateSelectionIfNeeded(base, extent, start, end);
412 static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset)
414 if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor)
417 ASSERT(position.offsetInContainerNode() >= 0);
418 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
420 if (position.anchorNode() == &oldNode)
421 return Position(toText(oldNode.previousSibling()), positionOffset + offset);
423 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset)
424 return Position(toText(oldNode.previousSibling()), offset);
429 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset)
431 if (isNone() || !oldNode.inDocument())
433 Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset);
434 Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset);
435 Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset);
436 Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset);
437 updateSelectionIfNeeded(base, extent, start, end);
440 static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode)
442 if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor)
444 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
445 ASSERT(position.offsetInContainerNode() >= 0);
446 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
447 unsigned oldLength = oldNode.length();
448 if (positionOffset <= oldLength)
450 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
453 void FrameSelection::didSplitTextNode(const Text& oldNode)
455 if (isNone() || !oldNode.inDocument())
457 Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode);
458 Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode);
459 Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode);
460 Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode);
461 updateSelectionIfNeeded(base, extent, start, end);
464 void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end)
466 if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end())
468 VisibleSelection newSelection;
469 newSelection.setWithoutValidation(base, extent);
470 m_frame->document()->updateLayout();
471 setSelection(newSelection, DoNotSetFocus);
474 TextDirection FrameSelection::directionOfEnclosingBlock()
476 return WebCore::directionOfEnclosingBlock(m_selection.extent());
479 TextDirection FrameSelection::directionOfSelection()
481 InlineBox* startBox = 0;
482 InlineBox* endBox = 0;
484 // Cache the VisiblePositions because visibleStart() and visibleEnd()
485 // can cause layout, which has the potential to invalidate lineboxes.
486 VisiblePosition startPosition = m_selection.visibleStart();
487 VisiblePosition endPosition = m_selection.visibleEnd();
488 if (startPosition.isNotNull())
489 startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
490 if (endPosition.isNotNull())
491 endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
492 if (startBox && endBox && startBox->direction() == endBox->direction())
493 return startBox->direction();
495 return directionOfEnclosingBlock();
498 void FrameSelection::didChangeFocus()
500 // Hits in virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html
501 DisableCompositingQueryAsserts disabler;
505 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
507 if (alter != AlterationExtend)
510 Position start = m_selection.start();
511 Position end = m_selection.end();
513 bool baseIsStart = true;
515 if (m_selection.isDirectional()) {
516 // Make base and extent match start and end so we extend the user-visible selection.
517 // This only matters for cases where base and extend point to different positions than
518 // start and end (e.g. after a double-click to select a word).
519 if (m_selection.isBaseFirst())
526 if (directionOfSelection() == LTR)
531 case DirectionForward:
535 if (directionOfSelection() == LTR)
540 case DirectionBackward:
546 m_selection.setBase(start);
547 m_selection.setExtent(end);
549 m_selection.setBase(end);
550 m_selection.setExtent(start);
554 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
556 Settings* settings = m_frame ? m_frame->settings() : 0;
557 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
558 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
559 // Linux and Windows always extend selections from the extent endpoint.
560 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
561 // base/extent always point to the same nodes as start/end, but which points
562 // to which depends on the value of isBaseFirst. Then this can be changed
563 // to just return m_sel.extent().
564 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
567 VisiblePosition FrameSelection::startForPlatform() const
569 return positionForPlatform(true);
572 VisiblePosition FrameSelection::endForPlatform() const
574 return positionForPlatform(false);
577 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
579 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
581 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
582 // In order to skip spaces when moving right, we advance one
583 // word further and then move one word back. Given the
584 // semantics of previousWordPosition() this will put us at the
585 // beginning of the word following.
586 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
587 if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
588 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
590 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
591 if (movingBackwardsMovedPositionToStartOfCurrentWord)
592 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
594 return positionAfterCurrentWord;
597 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
599 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
600 pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
603 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
605 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
607 // The difference between modifyExtendingRight and modifyExtendingForward is:
608 // modifyExtendingForward always extends forward logically.
609 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
610 // it extends forward logically if the enclosing block is LTR direction,
611 // but it extends backward logically if the enclosing block is RTL direction.
612 switch (granularity) {
613 case CharacterGranularity:
614 if (directionOfEnclosingBlock() == LTR)
615 pos = pos.next(CanSkipOverEditingBoundary);
617 pos = pos.previous(CanSkipOverEditingBoundary);
619 case WordGranularity:
620 if (directionOfEnclosingBlock() == LTR)
621 pos = nextWordPositionForPlatform(pos);
623 pos = previousWordPosition(pos);
626 if (directionOfEnclosingBlock() == LTR)
627 pos = modifyExtendingForward(granularity);
629 pos = modifyExtendingBackward(granularity);
631 case SentenceGranularity:
632 case LineGranularity:
633 case ParagraphGranularity:
634 case SentenceBoundary:
635 case ParagraphBoundary:
636 case DocumentBoundary:
637 // FIXME: implement all of the above?
638 pos = modifyExtendingForward(granularity);
641 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
645 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
647 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
648 switch (granularity) {
649 case CharacterGranularity:
650 pos = pos.next(CanSkipOverEditingBoundary);
652 case WordGranularity:
653 pos = nextWordPositionForPlatform(pos);
655 case SentenceGranularity:
656 pos = nextSentencePosition(pos);
658 case LineGranularity:
659 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
661 case ParagraphGranularity:
662 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
664 case SentenceBoundary:
665 pos = endOfSentence(endForPlatform());
668 pos = logicalEndOfLine(endForPlatform());
670 case ParagraphBoundary:
671 pos = endOfParagraph(endForPlatform());
673 case DocumentBoundary:
674 pos = endForPlatform();
675 if (isEditablePosition(pos.deepEquivalent()))
676 pos = endOfEditableContent(pos);
678 pos = endOfDocument(pos);
681 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
685 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
688 switch (granularity) {
689 case CharacterGranularity:
691 if (directionOfSelection() == LTR)
692 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
694 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
696 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
698 case WordGranularity: {
699 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
700 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
703 case SentenceGranularity:
704 case LineGranularity:
705 case ParagraphGranularity:
706 case SentenceBoundary:
707 case ParagraphBoundary:
708 case DocumentBoundary:
709 // FIXME: Implement all of the above.
710 pos = modifyMovingForward(granularity);
713 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
719 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
722 // FIXME: Stay in editable content for the less common granularities.
723 switch (granularity) {
724 case CharacterGranularity:
726 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
728 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary);
730 case WordGranularity:
731 pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
733 case SentenceGranularity:
734 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
736 case LineGranularity: {
737 // down-arrowing from a range selection that ends at the start of a line needs
738 // to leave the selection at that line start (no need to call nextLinePosition!)
739 pos = endForPlatform();
740 if (!isRange() || !isStartOfLine(pos))
741 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
744 case ParagraphGranularity:
745 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
747 case SentenceBoundary:
748 pos = endOfSentence(endForPlatform());
751 pos = logicalEndOfLine(endForPlatform());
753 case ParagraphBoundary:
754 pos = endOfParagraph(endForPlatform());
756 case DocumentBoundary:
757 pos = endForPlatform();
758 if (isEditablePosition(pos.deepEquivalent()))
759 pos = endOfEditableContent(pos);
761 pos = endOfDocument(pos);
767 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
769 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
771 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
772 // modifyExtendingBackward always extends backward logically.
773 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
774 // it extends backward logically if the enclosing block is LTR direction,
775 // but it extends forward logically if the enclosing block is RTL direction.
776 switch (granularity) {
777 case CharacterGranularity:
778 if (directionOfEnclosingBlock() == LTR)
779 pos = pos.previous(CanSkipOverEditingBoundary);
781 pos = pos.next(CanSkipOverEditingBoundary);
783 case WordGranularity:
784 if (directionOfEnclosingBlock() == LTR)
785 pos = previousWordPosition(pos);
787 pos = nextWordPositionForPlatform(pos);
790 if (directionOfEnclosingBlock() == LTR)
791 pos = modifyExtendingBackward(granularity);
793 pos = modifyExtendingForward(granularity);
795 case SentenceGranularity:
796 case LineGranularity:
797 case ParagraphGranularity:
798 case SentenceBoundary:
799 case ParagraphBoundary:
800 case DocumentBoundary:
801 pos = modifyExtendingBackward(granularity);
804 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
808 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
810 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
812 // Extending a selection backward by word or character from just after a table selects
813 // the table. This "makes sense" from the user perspective, esp. when deleting.
814 // It was done here instead of in VisiblePosition because we want VPs to iterate
816 switch (granularity) {
817 case CharacterGranularity:
818 pos = pos.previous(CanSkipOverEditingBoundary);
820 case WordGranularity:
821 pos = previousWordPosition(pos);
823 case SentenceGranularity:
824 pos = previousSentencePosition(pos);
826 case LineGranularity:
827 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
829 case ParagraphGranularity:
830 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
832 case SentenceBoundary:
833 pos = startOfSentence(startForPlatform());
836 pos = logicalStartOfLine(startForPlatform());
838 case ParagraphBoundary:
839 pos = startOfParagraph(startForPlatform());
841 case DocumentBoundary:
842 pos = startForPlatform();
843 if (isEditablePosition(pos.deepEquivalent()))
844 pos = startOfEditableContent(pos);
846 pos = startOfDocument(pos);
849 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
853 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
856 switch (granularity) {
857 case CharacterGranularity:
859 if (directionOfSelection() == LTR)
860 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
862 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
864 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
866 case WordGranularity: {
867 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
868 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
871 case SentenceGranularity:
872 case LineGranularity:
873 case ParagraphGranularity:
874 case SentenceBoundary:
875 case ParagraphBoundary:
876 case DocumentBoundary:
877 // FIXME: Implement all of the above.
878 pos = modifyMovingBackward(granularity);
881 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
887 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
890 switch (granularity) {
891 case CharacterGranularity:
893 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
895 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary);
897 case WordGranularity:
898 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
900 case SentenceGranularity:
901 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
903 case LineGranularity:
904 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
906 case ParagraphGranularity:
907 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
909 case SentenceBoundary:
910 pos = startOfSentence(startForPlatform());
913 pos = logicalStartOfLine(startForPlatform());
915 case ParagraphBoundary:
916 pos = startOfParagraph(startForPlatform());
918 case DocumentBoundary:
919 pos = startForPlatform();
920 if (isEditablePosition(pos.deepEquivalent()))
921 pos = startOfEditableContent(pos);
923 pos = startOfDocument(pos);
929 static bool isBoundary(TextGranularity granularity)
931 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
934 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
936 if (userTriggered == UserTriggered) {
937 FrameSelection trialFrameSelection;
938 trialFrameSelection.setSelection(m_selection);
939 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
941 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
945 willBeModified(alter, direction);
947 bool wasRange = m_selection.isRange();
948 VisiblePosition originalStartPosition = m_selection.visibleStart();
949 VisiblePosition position;
952 if (alter == AlterationMove)
953 position = modifyMovingRight(granularity);
955 position = modifyExtendingRight(granularity);
957 case DirectionForward:
958 if (alter == AlterationExtend)
959 position = modifyExtendingForward(granularity);
961 position = modifyMovingForward(granularity);
964 if (alter == AlterationMove)
965 position = modifyMovingLeft(granularity);
967 position = modifyExtendingLeft(granularity);
969 case DirectionBackward:
970 if (alter == AlterationExtend)
971 position = modifyExtendingBackward(granularity);
973 position = modifyMovingBackward(granularity);
977 if (position.isNull())
980 if (isSpatialNavigationEnabled(m_frame))
981 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
984 // Some of the above operations set an xPosForVerticalArrowNavigation.
985 // Setting a selection will clear it, so save it to possibly restore later.
986 // Note: the START position type is arbitrary because it is unused, it would be
987 // the requested position type if there were no xPosForVerticalArrowNavigation set.
988 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
989 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
993 moveTo(position, userTriggered);
995 case AlterationExtend:
997 if (!m_selection.isCaret()
998 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
999 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1000 // Don't let the selection go across the base position directly. Needed to match mac
1001 // behavior when, for instance, word-selecting backwards starting with the caret in
1002 // the middle of a word and then word-selecting forward, leaving the caret in the
1003 // same place where it was, instead of directly selecting to the end of the word.
1004 VisibleSelection newSelection = m_selection;
1005 newSelection.setExtent(position);
1006 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1007 position = m_selection.visibleBase();
1010 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1011 // base in place and moving the extent. Matches NSTextView.
1012 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1013 setExtent(position, userTriggered);
1015 TextDirection textDirection = directionOfEnclosingBlock();
1016 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1017 setEnd(position, userTriggered);
1019 setStart(position, userTriggered);
1024 if (granularity == LineGranularity || granularity == ParagraphGranularity)
1025 m_xPosForVerticalArrowNavigation = x;
1027 if (userTriggered == UserTriggered)
1028 m_granularity = CharacterGranularity;
1030 setCaretRectNeedsUpdate();
1035 // FIXME: Maybe baseline would be better?
1036 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1038 IntRect rect = c.absoluteCaretBounds();
1041 y = rect.y() + rect.height() / 2;
1045 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1047 if (!verticalDistance)
1050 if (userTriggered == UserTriggered) {
1051 FrameSelection trialFrameSelection;
1052 trialFrameSelection.setSelection(m_selection);
1053 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1056 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1058 VisiblePosition pos;
1059 LayoutUnit xPos = 0;
1061 case AlterationMove:
1062 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1063 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1064 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1066 case AlterationExtend:
1067 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1068 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1069 m_selection.setAffinity(DOWNSTREAM);
1074 if (!absoluteCaretY(pos, startY))
1076 if (direction == DirectionUp)
1080 VisiblePosition result;
1081 VisiblePosition next;
1082 for (VisiblePosition p = pos; ; p = next) {
1083 if (direction == DirectionUp)
1084 next = previousLinePosition(p, xPos);
1086 next = nextLinePosition(p, xPos);
1088 if (next.isNull() || next == p)
1091 if (!absoluteCaretY(next, nextY))
1093 if (direction == DirectionUp)
1095 if (nextY - startY > static_cast<int>(verticalDistance))
1097 if (nextY >= lastY) {
1103 if (result.isNull())
1107 case AlterationMove:
1108 moveTo(result, userTriggered, align);
1110 case AlterationExtend:
1111 setExtent(result, userTriggered);
1115 if (userTriggered == UserTriggered)
1116 m_granularity = CharacterGranularity;
1118 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1123 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1133 pos = m_selection.start();
1136 pos = m_selection.end();
1139 pos = m_selection.base();
1142 pos = m_selection.extent();
1146 LocalFrame* frame = pos.document()->frame();
1150 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1151 VisiblePosition visiblePosition(pos, m_selection.affinity());
1152 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1153 // after the selection is created and before this function is called.
1154 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1155 m_xPosForVerticalArrowNavigation = x;
1157 x = m_xPosForVerticalArrowNavigation;
1162 void FrameSelection::clear()
1164 m_granularity = CharacterGranularity;
1165 setSelection(VisibleSelection());
1168 void FrameSelection::prepareForDestruction()
1170 m_granularity = CharacterGranularity;
1172 m_caretBlinkTimer.stop();
1174 RenderView* view = m_frame->contentRenderer();
1176 view->clearSelection();
1178 setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
1179 m_previousCaretNode.clear();
1182 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1184 if (m_selection.isBaseFirst())
1185 setBase(pos, trigger);
1187 setExtent(pos, trigger);
1190 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1192 if (m_selection.isBaseFirst())
1193 setExtent(pos, trigger);
1195 setBase(pos, trigger);
1198 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1200 const bool selectionHasDirection = true;
1201 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1204 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1206 const bool selectionHasDirection = true;
1207 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1210 RenderObject* FrameSelection::caretRenderer() const
1212 return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1215 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1217 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1220 LayoutRect FrameSelection::localCaretRect()
1222 if (shouldUpdateCaretRect()) {
1223 if (!isNonOrphanedCaret(m_selection))
1225 else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
1226 m_absCaretBoundsDirty = true;
1229 return localCaretRectWithoutUpdate();
1232 IntRect FrameSelection::absoluteCaretBounds()
1234 recomputeCaretRect();
1235 return m_absCaretBounds;
1238 bool FrameSelection::recomputeCaretRect()
1240 if (!shouldUpdateCaretRect())
1243 if (!m_frame || !m_frame->document()->view())
1246 LayoutRect oldRect = localCaretRectWithoutUpdate();
1247 LayoutRect newRect = localCaretRect();
1248 if (oldRect == newRect && !m_absCaretBoundsDirty)
1251 IntRect oldAbsCaretBounds = m_absCaretBounds;
1252 m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1253 m_absCaretBoundsDirty = false;
1255 if (oldAbsCaretBounds == m_absCaretBounds)
1258 if (RenderView* view = m_frame->document()->renderView()) {
1259 if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable()))
1260 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect);
1261 Node* node = m_selection.start().deprecatedNode();
1262 m_previousCaretNode = node;
1263 if (shouldRepaintCaret(view, isContentEditable()))
1264 repaintCaretForLocalRect(node, newRect);
1270 void FrameSelection::invalidateCaretRect()
1275 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1278 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1280 if (m_selection.isCaret() && m_caretPaint)
1281 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1284 bool FrameSelection::contains(const LayoutPoint& point)
1286 Document* document = m_frame->document();
1288 // Treat a collapsed selection like no selection.
1291 if (!document->renderView())
1294 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1295 HitTestResult result(point);
1296 document->renderView()->hitTest(request, result);
1297 Node* innerNode = result.innerNode();
1298 if (!innerNode || !innerNode->renderer())
1301 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1302 if (visiblePos.isNull())
1305 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1308 Position start(m_selection.visibleStart().deepEquivalent());
1309 Position end(m_selection.visibleEnd().deepEquivalent());
1310 Position p(visiblePos.deepEquivalent());
1312 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1315 // Workaround for the fact that it's hard to delete a frame.
1316 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1317 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1318 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1319 // mouse or the keyboard after setting the selection.
1320 void FrameSelection::selectFrameElementInParentIfFullySelected()
1322 // Find the parent frame; if there is none, then we have nothing to do.
1323 LocalFrame* parent = m_frame->tree().parent();
1326 Page* page = m_frame->page();
1330 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1333 if (!isStartOfDocument(selection().visibleStart()))
1335 if (!isEndOfDocument(selection().visibleEnd()))
1338 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1339 Element* ownerElement = m_frame->ownerElement();
1342 ContainerNode* ownerElementParent = ownerElement->parentNode();
1343 if (!ownerElementParent)
1346 // 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.
1347 if (!ownerElementParent->rendererIsEditable())
1350 // Create compute positions before and after the element.
1351 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1352 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1353 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1355 // Focus on the parent frame, and then select from before this element to after.
1356 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1357 page->focusController().setFocusedFrame(parent);
1358 parent->selection().setSelection(newSelection);
1361 void FrameSelection::selectAll()
1363 Document* document = m_frame->document();
1365 if (isHTMLSelectElement(document->focusedElement())) {
1366 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement());
1367 if (selectElement->canSelectAll()) {
1368 selectElement->selectAll();
1373 RefPtr<Node> root = nullptr;
1374 Node* selectStartTarget = 0;
1375 if (isContentEditable()) {
1376 root = highestEditableRoot(m_selection.start());
1377 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1378 selectStartTarget = shadowRoot->shadowHost();
1380 selectStartTarget = root.get();
1382 root = m_selection.nonBoundaryShadowTreeRootNode();
1384 selectStartTarget = root->shadowHost();
1386 root = document->documentElement();
1387 selectStartTarget = document->body();
1393 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)))
1396 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1397 setSelection(newSelection);
1398 selectFrameElementInParentIfFullySelected();
1399 notifyRendererOfSelectionChange(UserTriggered);
1402 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, SetSelectionOptions options)
1404 if (!range || !range->startContainer() || !range->endContainer())
1406 ASSERT(range->startContainer()->document() == range->endContainer()->document());
1408 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1410 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1411 // they start at the beginning of the next line instead
1412 m_logicalRange = nullptr;
1413 stopObservingVisibleSelectionChangeIfNecessary();
1415 // FIXME: Can we provide extentAffinity?
1416 VisiblePosition visibleStart(range->startPosition(), range->collapsed() ? affinity : DOWNSTREAM);
1417 VisiblePosition visibleEnd(range->endPosition(), SEL_DEFAULT_AFFINITY);
1418 setSelection(VisibleSelection(visibleStart, visibleEnd), options);
1420 m_logicalRange = range->cloneRange();
1421 startObservingVisibleSelectionChange();
1426 PassRefPtrWillBeRawPtr<Range> FrameSelection::firstRange() const
1429 return m_logicalRange->cloneRange();
1430 return m_selection.firstRange();
1433 bool FrameSelection::isInPasswordField() const
1435 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1436 return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField();
1439 void FrameSelection::notifyAccessibilityForSelectionChange()
1441 if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) {
1442 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1443 cache->selectionChanged(m_selection.start().containerNode());
1447 void FrameSelection::focusedOrActiveStateChanged()
1449 bool activeAndFocused = isFocusedAndActive();
1451 RefPtr<Document> document = m_frame->document();
1452 document->updateRenderTreeIfNeeded();
1454 // Because RenderObject::selectionBackgroundColor() and
1455 // RenderObject::selectionForegroundColor() check if the frame is active,
1456 // we have to update places those colors were painted.
1457 if (RenderView* view = document->renderView())
1458 view->repaintSelection();
1460 // Caret appears in the active frame.
1461 if (activeAndFocused)
1462 setSelectionFromNone();
1464 m_frame->spellChecker().spellCheckAfterBlur();
1465 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1467 // Update for caps lock state
1468 m_frame->eventHandler().capsLockStateMayHaveChanged();
1470 // We may have lost active status even though the focusElement hasn't changed
1471 // give the element a chance to recalc style if its affected by focus.
1472 if (Element* element = document->focusedElement())
1473 element->focusStateChanged();
1475 // Secure keyboard entry is set by the active frame.
1476 if (document->useSecureKeyboardEntryWhenActive())
1477 setUseSecureKeyboardEntry(activeAndFocused);
1480 void FrameSelection::pageActivationChanged()
1482 focusedOrActiveStateChanged();
1485 void FrameSelection::updateSecureKeyboardEntryIfActive()
1487 if (m_frame->document() && isFocusedAndActive())
1488 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1491 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1494 enableSecureTextInput();
1496 disableSecureTextInput();
1499 void FrameSelection::setFocused(bool flag)
1501 if (m_focused == flag)
1505 focusedOrActiveStateChanged();
1508 bool FrameSelection::isFocusedAndActive() const
1510 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
1513 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame)
1515 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
1518 void FrameSelection::updateAppearance()
1520 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
1521 // the FrameSelection will paint a blinking caret as usual).
1522 VisiblePosition forwardPosition;
1523 if (m_shouldShowBlockCursor && m_selection.isCaret()) {
1524 forwardPosition = modifyExtendingForward(CharacterGranularity);
1525 m_caretPaint = forwardPosition.isNull();
1528 bool caretRectChangedOrCleared = recomputeCaretRect();
1529 bool shouldBlink = shouldBlinkCaret() && forwardPosition.isNull();
1531 // If the caret moved, stop the blink timer so we can restart with a
1532 // black caret in the new location.
1533 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) {
1534 m_caretBlinkTimer.stop();
1535 if (!shouldBlink && m_caretPaint) {
1536 m_caretPaint = false;
1537 invalidateCaretRect();
1541 // Start blinking with a black caret. Be sure not to restart if we're
1542 // already blinking in the right location.
1543 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1544 if (double blinkInterval = RenderTheme::theme().caretBlinkInterval())
1545 m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE);
1547 if (!m_caretPaint) {
1548 m_caretPaint = true;
1549 invalidateCaretRect();
1553 RenderView* view = m_frame->contentRenderer();
1557 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1558 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1559 VisibleSelection selection(m_selection.visibleStart(), forwardPosition.isNotNull() ? forwardPosition : m_selection.visibleEnd());
1561 if (!selection.isRange()) {
1562 view->clearSelection();
1566 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1567 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1568 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1569 // and will fill the gap before 'bar'.
1570 Position startPos = selection.start();
1571 Position candidate = startPos.downstream();
1572 if (candidate.isCandidate())
1573 startPos = candidate;
1574 Position endPos = selection.end();
1575 candidate = endPos.upstream();
1576 if (candidate.isCandidate())
1579 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1580 // because we don't yet notify the FrameSelection of text removal.
1581 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1582 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1583 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1584 if (startRenderer->view() == view && endRenderer->view() == view)
1585 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1589 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1591 if (caretVisibility() == visibility)
1594 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1596 m_caretPaint = false;
1597 invalidateCaretRect();
1599 CaretBase::setCaretVisibility(visibility);
1604 bool FrameSelection::shouldBlinkCaret() const
1606 if (!caretIsVisible() || !isCaret())
1609 if (m_frame->settings() && m_frame->settings()->caretBrowsingEnabled())
1612 Node* root = rootEditableElement();
1616 Element* focusedElement = root->document().focusedElement();
1617 if (!focusedElement)
1620 return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode());
1623 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1625 ASSERT(caretIsVisible());
1627 bool caretPaint = m_caretPaint;
1628 if (isCaretBlinkingSuspended() && caretPaint)
1630 m_caretPaint = !caretPaint;
1631 invalidateCaretRect();
1634 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1636 m_frame->document()->updateRenderTreeIfNeeded();
1638 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1639 textControl->selectionChanged(userTriggered == UserTriggered);
1642 // Helper function that tells whether a particular node is an element that has an entire
1643 // LocalFrame and FrameView, a <frame>, <iframe>, or <object>.
1644 static bool isFrameElement(const Node* n)
1648 RenderObject* renderer = n->renderer();
1649 if (!renderer || !renderer->isWidget())
1651 Widget* widget = toRenderWidget(renderer)->widget();
1652 return widget && widget->isFrameView();
1655 void FrameSelection::setFocusedNodeIfNeeded()
1657 if (isNone() || !isFocused())
1660 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1661 if (caretBrowsing) {
1662 if (Element* anchor = enclosingAnchorElement(base())) {
1663 m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
1668 if (Element* target = rootEditableElement()) {
1669 // Walk up the DOM tree to search for a node to focus.
1671 // We don't want to set focus on a subframe when selecting in a parent frame,
1672 // so add the !isFrameElement check here. There's probably a better way to make this
1673 // work in the long term, but this is the safest fix at this time.
1674 if (target->isMouseFocusable() && !isFrameElement(target)) {
1675 m_frame->page()->focusController().setFocusedElement(target, m_frame);
1678 target = target->parentOrShadowHostElement();
1680 m_frame->document()->setFocusedElement(nullptr);
1684 m_frame->page()->focusController().setFocusedElement(0, m_frame);
1687 static String extractSelectedText(const FrameSelection& selection, TextIteratorBehavior behavior)
1689 // We remove '\0' characters because they are not visibly rendered to the user.
1690 return plainText(selection.toNormalizedRange().get(), behavior).replace(0, "");
1693 String FrameSelection::selectedText() const
1695 return extractSelectedText(*this, TextIteratorDefaultBehavior);
1698 String FrameSelection::selectedTextForClipboard() const
1700 if (m_frame->settings() && m_frame->settings()->selectionIncludesAltImageText())
1701 return extractSelectedText(*this, TextIteratorEmitsImageAltText);
1702 return selectedText();
1705 FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
1707 m_frame->document()->updateRenderTreeIfNeeded();
1709 FrameView* view = m_frame->view();
1710 RenderView* renderView = m_frame->contentRenderer();
1712 if (!view || !renderView)
1715 LayoutRect selectionRect = renderView->selectionBounds(clipToVisibleContent);
1716 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1719 static inline HTMLFormElement* associatedFormElement(HTMLElement& element)
1721 if (isHTMLFormElement(element))
1722 return &toHTMLFormElement(element);
1723 return element.formOwner();
1726 // Scans logically forward from "start", including any child frames.
1727 static HTMLFormElement* scanForForm(Node* start)
1732 HTMLElement* element = start->isHTMLElement() ? toHTMLElement(start) : Traversal<HTMLElement>::next(*start);
1733 for (; element; element = Traversal<HTMLElement>::next(*element)) {
1734 if (HTMLFormElement* form = associatedFormElement(*element))
1737 if (isHTMLFrameElementBase(*element)) {
1738 Node* childDocument = toHTMLFrameElementBase(*element).contentDocument();
1739 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1746 // We look for either the form containing the current focus, or for one immediately after it
1747 HTMLFormElement* FrameSelection::currentForm() const
1749 // Start looking either at the active (first responder) node, or where the selection is.
1750 Node* start = m_frame->document()->focusedElement();
1752 start = this->start().deprecatedNode();
1756 // Try walking up the node tree to find a form element.
1757 for (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*start); element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
1758 if (HTMLFormElement* form = associatedFormElement(*element))
1762 // Try walking forward in the node tree to find a form element.
1763 return scanForForm(start);
1766 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1770 switch (selectionType()) {
1773 case CaretSelection:
1774 rect = absoluteCaretBounds();
1776 case RangeSelection:
1777 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1781 Position start = this->start();
1782 ASSERT(start.deprecatedNode());
1783 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1784 // FIXME: This code only handles scrolling the startContainer's layer, but
1785 // the selection rect could intersect more than just that.
1786 // See <rdar://problem/4799899>.
1787 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1792 void FrameSelection::setSelectionFromNone()
1794 // Put a caret inside the body if the entire frame is editable (either the
1795 // entire WebView is editable or designMode is on for this document).
1797 Document* document = m_frame->document();
1798 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1799 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1802 Node* node = document->documentElement();
1805 Node* body = isHTMLBodyElement(*node) ? node : Traversal<HTMLBodyElement>::next(*node);
1807 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
1810 bool FrameSelection::dispatchSelectStart()
1812 Node* selectStartTarget = m_selection.extent().containerNode();
1813 if (!selectStartTarget)
1816 return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
1819 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
1821 m_shouldShowBlockCursor = shouldShowBlockCursor;
1823 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1828 void FrameSelection::didChangeVisibleSelection()
1830 ASSERT(m_observingVisibleSelection);
1831 // Invalidate the logical range when the underlying VisibleSelection has changed.
1832 m_logicalRange = nullptr;
1833 m_selection.clearChangeObserver();
1834 m_observingVisibleSelection = false;
1837 void FrameSelection::startObservingVisibleSelectionChange()
1839 ASSERT(!m_observingVisibleSelection);
1840 m_selection.setChangeObserver(*this);
1841 m_observingVisibleSelection = true;
1844 void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary()
1846 if (m_observingVisibleSelection) {
1847 m_selection.clearChangeObserver();
1848 m_observingVisibleSelection = false;
1854 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
1856 m_selection.formatForDebugger(buffer, length);
1859 void FrameSelection::showTreeForThis() const
1861 m_selection.showTreeForThis();
1870 void showTree(const WebCore::FrameSelection& sel)
1872 sel.showTreeForThis();
1875 void showTree(const WebCore::FrameSelection* sel)
1878 sel->showTreeForThis();