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"
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "core/HTMLNames.h"
31 #include "core/InputTypeNames.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/events/Event.h"
49 #include "core/frame/LocalDOMWindow.h"
50 #include "core/frame/LocalFrame.h"
51 #include "core/html/HTMLBodyElement.h"
52 #include "core/html/HTMLFormElement.h"
53 #include "core/html/HTMLFrameElementBase.h"
54 #include "core/html/HTMLInputElement.h"
55 #include "core/html/HTMLSelectElement.h"
56 #include "core/page/EditorClient.h"
57 #include "core/page/EventHandler.h"
58 #include "core/page/FocusController.h"
59 #include "core/page/FrameTree.h"
60 #include "core/frame/FrameView.h"
61 #include "core/page/Page.h"
62 #include "core/frame/Settings.h"
63 #include "core/page/SpatialNavigation.h"
64 #include "core/rendering/HitTestRequest.h"
65 #include "core/rendering/HitTestResult.h"
66 #include "core/rendering/InlineTextBox.h"
67 #include "core/rendering/RenderLayer.h"
68 #include "core/rendering/RenderText.h"
69 #include "core/rendering/RenderTheme.h"
70 #include "core/rendering/RenderView.h"
71 #include "core/rendering/RenderWidget.h"
72 #include "platform/SecureTextInput.h"
73 #include "platform/geometry/FloatQuad.h"
74 #include "platform/graphics/GraphicsContext.h"
75 #include "wtf/text/CString.h"
82 using namespace HTMLNames;
84 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
86 return LayoutUnit::min();
89 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
91 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
94 FrameSelection::FrameSelection(LocalFrame* frame)
96 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
97 , m_observingVisibleSelection(false)
98 , m_granularity(CharacterGranularity)
99 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
100 , m_caretRectDirty(true)
101 , m_shouldPaintCaret(true)
102 , m_isCaretBlinkingSuspended(false)
103 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
104 , m_shouldShowBlockCursor(false)
106 if (shouldAlwaysUseDirectionalSelection(m_frame))
107 m_selection.setIsDirectional(true);
110 FrameSelection::~FrameSelection()
113 // Oilpan: No need to clear out VisibleSelection observer;
114 // it is finalized as a part object of FrameSelection.
115 stopObservingVisibleSelectionChangeIfNecessary();
119 Element* FrameSelection::rootEditableElementOrDocumentElement() const
121 Element* selectionRoot = m_selection.rootEditableElement();
122 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
125 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const
127 Element* selectionRoot = m_selection.rootEditableElement();
129 return selectionRoot;
131 Node* node = m_selection.base().containerNode();
132 return node ? &node->treeScope().rootNode() : 0;
135 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
137 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
138 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
141 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
143 const bool selectionHasDirection = true;
144 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
145 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
148 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
150 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
151 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
154 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
156 RenderedPosition base(visibleBase);
157 RenderedPosition extent(visibleExtent);
159 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
162 if (base.atLeftBoundaryOfBidiRun()) {
163 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
164 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
165 visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun());
171 if (base.atRightBoundaryOfBidiRun()) {
172 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
173 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
174 visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun());
180 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
181 visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun());
185 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
186 visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun());
191 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
192 EndPointsAdjustmentMode endpointsAdjustmentMode)
194 VisibleSelection newSelection = passedNewSelection;
195 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
197 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
198 VisiblePosition newBase = base;
199 VisiblePosition extent = newSelection.visibleExtent();
200 VisiblePosition newExtent = extent;
201 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
202 adjustEndpointsAtBidiBoundary(newBase, newExtent);
204 if (newBase != base || newExtent != extent) {
205 m_originalBase = base;
206 newSelection.setBase(newBase);
207 newSelection.setExtent(newExtent);
208 } else if (m_originalBase.isNotNull()) {
209 if (m_selection.base() == newSelection.base())
210 newSelection.setBase(m_originalBase);
211 m_originalBase.clear();
214 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
215 if (m_selection == newSelection)
218 setSelection(newSelection, granularity);
221 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
223 bool closeTyping = options & CloseTyping;
224 bool shouldClearTypingStyle = options & ClearTypingStyle;
225 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
227 VisibleSelection s = validateSelection(newSelection);
228 if (shouldAlwaysUseDirectionalSelection(m_frame))
229 s.setIsDirectional(true);
236 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
237 // if document->frame() == m_frame we can get into an infinite loop
238 if (s.base().anchorNode()) {
239 Document& document = *s.base().document();
240 if (document.frame() && document.frame() != m_frame && document != m_frame->document()) {
241 RefPtrWillBeRawPtr<LocalFrame> guard(document.frame());
242 document.frame()->selection().setSelection(s, options, align, granularity);
243 // It's possible that during the above set selection, this FrameSelection has been modified by
244 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
245 // the frame is about to be destroyed. If this is the case, clear our selection.
246 if (!guard->host() && !m_selection.isNonOrphanedCaretOrRange())
252 m_granularity = granularity;
255 TypingCommand::closeTyping(m_frame);
257 if (shouldClearTypingStyle)
260 if (m_selection == s) {
261 // Even if selection was not changed, selection offsets may have been changed.
262 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
263 notifyRendererOfSelectionChange(userTriggered);
267 VisibleSelection oldSelection = m_selection;
270 setCaretRectNeedsUpdate();
272 if (!s.isNone() && !(options & DoNotSetFocus))
273 setFocusedNodeIfNeeded();
275 if (!(options & DoNotUpdateAppearance)) {
276 // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html
277 DisableCompositingQueryAsserts disabler;
278 updateAppearance(ResetCaretBlink);
281 // Always clear the x position used for vertical arrow navigation.
282 // It will be restored by the vertical arrow navigation code if necessary.
283 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
284 selectFrameElementInParentIfFullySelected();
285 notifyRendererOfSelectionChange(userTriggered);
286 m_frame->editor().respondToChangedSelection(oldSelection, options);
287 if (userTriggered == UserTriggered) {
288 ScrollAlignment alignment;
290 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
291 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
293 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
295 revealSelection(alignment, RevealExtent);
298 notifyAccessibilityForSelectionChange();
299 notifyCompositorForSelectionChange();
300 notifyEventHandlerForSelectionChange();
301 m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange));
304 static bool removingNodeRemovesPosition(Node& node, const Position& position)
306 if (!position.anchorNode())
309 if (position.anchorNode() == node)
312 if (!node.isElementNode())
315 Element& element = toElement(node);
316 return element.containsIncludingShadowDOM(position.anchorNode());
319 void FrameSelection::nodeWillBeRemoved(Node& node)
321 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
322 // the selection in the document that created the fragment needs no adjustment.
323 if (isNone() || !node.inActiveDocument())
326 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
327 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
330 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
332 ASSERT(node.document().isActive());
334 bool clearRenderTreeSelection = false;
335 bool clearDOMTreeSelection = false;
337 if (startRemoved || endRemoved) {
338 Position start = m_selection.start();
339 Position end = m_selection.end();
341 updatePositionForNodeRemoval(start, node);
343 updatePositionForNodeRemoval(end, node);
345 if (start.isNotNull() && end.isNotNull()) {
346 if (m_selection.isBaseFirst())
347 m_selection.setWithoutValidation(start, end);
349 m_selection.setWithoutValidation(end, start);
351 clearDOMTreeSelection = true;
353 clearRenderTreeSelection = true;
354 } else if (baseRemoved || extentRemoved) {
355 // The base and/or extent are about to be removed, but the start and end aren't.
356 // Change the base and extent to the start and end, but don't re-validate the
357 // selection, since doing so could move the start and end into the node
358 // that is about to be removed.
359 if (m_selection.isBaseFirst())
360 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
362 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
363 } else if (m_selection.intersectsNode(&node)) {
364 // If we did nothing here, when this node's renderer was destroyed, the rect that it
365 // occupied would be invalidated, but, selection gaps that change as a result of
366 // the removal wouldn't be invalidated.
367 // FIXME: Don't do so much unnecessary invalidation.
368 clearRenderTreeSelection = true;
371 if (clearRenderTreeSelection)
372 m_selection.start().document()->renderView()->clearSelection();
374 if (clearDOMTreeSelection)
375 setSelection(VisibleSelection(), DoNotSetFocus);
378 static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
380 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
383 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
384 ASSERT(position.offsetInContainerNode() >= 0);
385 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
386 // Replacing text can be viewed as a deletion followed by insertion.
387 if (positionOffset >= offset && positionOffset <= offset + oldLength)
388 positionOffset = offset;
390 // Adjust the offset if the position is after the end of the deleted contents
391 // (positionOffset > offset + oldLength) to avoid having a stale offset.
392 if (positionOffset > offset + oldLength)
393 positionOffset = positionOffset - oldLength + newLength;
395 ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length());
396 // CharacterNode in VisibleSelection must be Text node, because Comment
397 // and ProcessingInstruction node aren't visible.
398 return Position(toText(node), positionOffset);
401 void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
403 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
404 if (isNone() || !node || !node->inDocument())
407 Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength);
408 Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength);
409 Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength);
410 Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength);
411 updateSelectionIfNeeded(base, extent, start, end);
414 static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset)
416 if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor)
419 ASSERT(position.offsetInContainerNode() >= 0);
420 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
422 if (position.anchorNode() == &oldNode)
423 return Position(toText(oldNode.previousSibling()), positionOffset + offset);
425 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset)
426 return Position(toText(oldNode.previousSibling()), offset);
431 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset)
433 if (isNone() || !oldNode.inDocument())
435 Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset);
436 Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset);
437 Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset);
438 Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset);
439 updateSelectionIfNeeded(base, extent, start, end);
442 static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode)
444 if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor)
446 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
447 ASSERT(position.offsetInContainerNode() >= 0);
448 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
449 unsigned oldLength = oldNode.length();
450 if (positionOffset <= oldLength)
452 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
455 void FrameSelection::didSplitTextNode(const Text& oldNode)
457 if (isNone() || !oldNode.inDocument())
459 Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode);
460 Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode);
461 Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode);
462 Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode);
463 updateSelectionIfNeeded(base, extent, start, end);
466 void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end)
468 if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end())
470 VisibleSelection newSelection;
471 if (m_selection.isBaseFirst())
472 newSelection.setWithoutValidation(start, end);
474 newSelection.setWithoutValidation(end, start);
475 setSelection(newSelection, DoNotSetFocus);
478 TextDirection FrameSelection::directionOfEnclosingBlock()
480 return blink::directionOfEnclosingBlock(m_selection.extent());
483 TextDirection FrameSelection::directionOfSelection()
485 InlineBox* startBox = 0;
486 InlineBox* endBox = 0;
488 // Cache the VisiblePositions because visibleStart() and visibleEnd()
489 // can cause layout, which has the potential to invalidate lineboxes.
490 VisiblePosition startPosition = m_selection.visibleStart();
491 VisiblePosition endPosition = m_selection.visibleEnd();
492 if (startPosition.isNotNull())
493 startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
494 if (endPosition.isNotNull())
495 endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
496 if (startBox && endBox && startBox->direction() == endBox->direction())
497 return startBox->direction();
499 return directionOfEnclosingBlock();
502 void FrameSelection::didChangeFocus()
504 // Hits in virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html
505 DisableCompositingQueryAsserts disabler;
509 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
511 if (alter != AlterationExtend)
514 Position start = m_selection.start();
515 Position end = m_selection.end();
517 bool baseIsStart = true;
519 if (m_selection.isDirectional()) {
520 // Make base and extent match start and end so we extend the user-visible selection.
521 // This only matters for cases where base and extend point to different positions than
522 // start and end (e.g. after a double-click to select a word).
523 if (m_selection.isBaseFirst())
530 if (directionOfSelection() == LTR)
535 case DirectionForward:
539 if (directionOfSelection() == LTR)
544 case DirectionBackward:
550 m_selection.setBase(start);
551 m_selection.setExtent(end);
553 m_selection.setBase(end);
554 m_selection.setExtent(start);
558 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
560 Settings* settings = m_frame ? m_frame->settings() : 0;
561 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
562 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
563 // Linux and Windows always extend selections from the extent endpoint.
564 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
565 // base/extent always point to the same nodes as start/end, but which points
566 // to which depends on the value of isBaseFirst. Then this can be changed
567 // to just return m_sel.extent().
568 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
571 VisiblePosition FrameSelection::startForPlatform() const
573 return positionForPlatform(true);
576 VisiblePosition FrameSelection::endForPlatform() const
578 return positionForPlatform(false);
581 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
583 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
585 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
586 // In order to skip spaces when moving right, we advance one
587 // word further and then move one word back. Given the
588 // semantics of previousWordPosition() this will put us at the
589 // beginning of the word following.
590 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
591 if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
592 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
594 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
595 if (movingBackwardsMovedPositionToStartOfCurrentWord)
596 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
598 return positionAfterCurrentWord;
601 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
603 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
604 pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
607 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
609 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
611 // The difference between modifyExtendingRight and modifyExtendingForward is:
612 // modifyExtendingForward always extends forward logically.
613 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
614 // it extends forward logically if the enclosing block is LTR direction,
615 // but it extends backward logically if the enclosing block is RTL direction.
616 switch (granularity) {
617 case CharacterGranularity:
618 if (directionOfEnclosingBlock() == LTR)
619 pos = pos.next(CanSkipOverEditingBoundary);
621 pos = pos.previous(CanSkipOverEditingBoundary);
623 case WordGranularity:
624 if (directionOfEnclosingBlock() == LTR)
625 pos = nextWordPositionForPlatform(pos);
627 pos = previousWordPosition(pos);
630 if (directionOfEnclosingBlock() == LTR)
631 pos = modifyExtendingForward(granularity);
633 pos = modifyExtendingBackward(granularity);
635 case SentenceGranularity:
636 case LineGranularity:
637 case ParagraphGranularity:
638 case SentenceBoundary:
639 case ParagraphBoundary:
640 case DocumentBoundary:
641 // FIXME: implement all of the above?
642 pos = modifyExtendingForward(granularity);
645 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
649 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
651 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
652 switch (granularity) {
653 case CharacterGranularity:
654 pos = pos.next(CanSkipOverEditingBoundary);
656 case WordGranularity:
657 pos = nextWordPositionForPlatform(pos);
659 case SentenceGranularity:
660 pos = nextSentencePosition(pos);
662 case LineGranularity:
663 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
665 case ParagraphGranularity:
666 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
668 case SentenceBoundary:
669 pos = endOfSentence(endForPlatform());
672 pos = logicalEndOfLine(endForPlatform());
674 case ParagraphBoundary:
675 pos = endOfParagraph(endForPlatform());
677 case DocumentBoundary:
678 pos = endForPlatform();
679 if (isEditablePosition(pos.deepEquivalent()))
680 pos = endOfEditableContent(pos);
682 pos = endOfDocument(pos);
685 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
689 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
692 switch (granularity) {
693 case CharacterGranularity:
695 if (directionOfSelection() == LTR)
696 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
698 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
700 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
702 case WordGranularity: {
703 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
704 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
707 case SentenceGranularity:
708 case LineGranularity:
709 case ParagraphGranularity:
710 case SentenceBoundary:
711 case ParagraphBoundary:
712 case DocumentBoundary:
713 // FIXME: Implement all of the above.
714 pos = modifyMovingForward(granularity);
717 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
723 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
726 // FIXME: Stay in editable content for the less common granularities.
727 switch (granularity) {
728 case CharacterGranularity:
730 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
732 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary);
734 case WordGranularity:
735 pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
737 case SentenceGranularity:
738 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
740 case LineGranularity: {
741 // down-arrowing from a range selection that ends at the start of a line needs
742 // to leave the selection at that line start (no need to call nextLinePosition!)
743 pos = endForPlatform();
744 if (!isRange() || !isStartOfLine(pos))
745 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
748 case ParagraphGranularity:
749 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
751 case SentenceBoundary:
752 pos = endOfSentence(endForPlatform());
755 pos = logicalEndOfLine(endForPlatform());
757 case ParagraphBoundary:
758 pos = endOfParagraph(endForPlatform());
760 case DocumentBoundary:
761 pos = endForPlatform();
762 if (isEditablePosition(pos.deepEquivalent()))
763 pos = endOfEditableContent(pos);
765 pos = endOfDocument(pos);
771 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
773 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
775 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
776 // modifyExtendingBackward always extends backward logically.
777 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
778 // it extends backward logically if the enclosing block is LTR direction,
779 // but it extends forward logically if the enclosing block is RTL direction.
780 switch (granularity) {
781 case CharacterGranularity:
782 if (directionOfEnclosingBlock() == LTR)
783 pos = pos.previous(CanSkipOverEditingBoundary);
785 pos = pos.next(CanSkipOverEditingBoundary);
787 case WordGranularity:
788 if (directionOfEnclosingBlock() == LTR)
789 pos = previousWordPosition(pos);
791 pos = nextWordPositionForPlatform(pos);
794 if (directionOfEnclosingBlock() == LTR)
795 pos = modifyExtendingBackward(granularity);
797 pos = modifyExtendingForward(granularity);
799 case SentenceGranularity:
800 case LineGranularity:
801 case ParagraphGranularity:
802 case SentenceBoundary:
803 case ParagraphBoundary:
804 case DocumentBoundary:
805 pos = modifyExtendingBackward(granularity);
808 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
812 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
814 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
816 // Extending a selection backward by word or character from just after a table selects
817 // the table. This "makes sense" from the user perspective, esp. when deleting.
818 // It was done here instead of in VisiblePosition because we want VPs to iterate
820 switch (granularity) {
821 case CharacterGranularity:
822 pos = pos.previous(CanSkipOverEditingBoundary);
824 case WordGranularity:
825 pos = previousWordPosition(pos);
827 case SentenceGranularity:
828 pos = previousSentencePosition(pos);
830 case LineGranularity:
831 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
833 case ParagraphGranularity:
834 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
836 case SentenceBoundary:
837 pos = startOfSentence(startForPlatform());
840 pos = logicalStartOfLine(startForPlatform());
842 case ParagraphBoundary:
843 pos = startOfParagraph(startForPlatform());
845 case DocumentBoundary:
846 pos = startForPlatform();
847 if (isEditablePosition(pos.deepEquivalent()))
848 pos = startOfEditableContent(pos);
850 pos = startOfDocument(pos);
853 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
857 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
860 switch (granularity) {
861 case CharacterGranularity:
863 if (directionOfSelection() == LTR)
864 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
866 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
868 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
870 case WordGranularity: {
871 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
872 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
875 case SentenceGranularity:
876 case LineGranularity:
877 case ParagraphGranularity:
878 case SentenceBoundary:
879 case ParagraphBoundary:
880 case DocumentBoundary:
881 // FIXME: Implement all of the above.
882 pos = modifyMovingBackward(granularity);
885 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
891 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
894 switch (granularity) {
895 case CharacterGranularity:
897 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
899 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary);
901 case WordGranularity:
902 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
904 case SentenceGranularity:
905 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
907 case LineGranularity:
908 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
910 case ParagraphGranularity:
911 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
913 case SentenceBoundary:
914 pos = startOfSentence(startForPlatform());
917 pos = logicalStartOfLine(startForPlatform());
919 case ParagraphBoundary:
920 pos = startOfParagraph(startForPlatform());
922 case DocumentBoundary:
923 pos = startForPlatform();
924 if (isEditablePosition(pos.deepEquivalent()))
925 pos = startOfEditableContent(pos);
927 pos = startOfDocument(pos);
933 static bool isBoundary(TextGranularity granularity)
935 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
938 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
940 if (userTriggered == UserTriggered) {
941 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
942 trialFrameSelection->setSelection(m_selection);
943 trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered);
945 if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
949 willBeModified(alter, direction);
951 bool wasRange = m_selection.isRange();
952 VisiblePosition originalStartPosition = m_selection.visibleStart();
953 VisiblePosition position;
956 if (alter == AlterationMove)
957 position = modifyMovingRight(granularity);
959 position = modifyExtendingRight(granularity);
961 case DirectionForward:
962 if (alter == AlterationExtend)
963 position = modifyExtendingForward(granularity);
965 position = modifyMovingForward(granularity);
968 if (alter == AlterationMove)
969 position = modifyMovingLeft(granularity);
971 position = modifyExtendingLeft(granularity);
973 case DirectionBackward:
974 if (alter == AlterationExtend)
975 position = modifyExtendingBackward(granularity);
977 position = modifyMovingBackward(granularity);
981 if (position.isNull())
984 if (isSpatialNavigationEnabled(m_frame))
985 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
988 // Some of the above operations set an xPosForVerticalArrowNavigation.
989 // Setting a selection will clear it, so save it to possibly restore later.
990 // Note: the START position type is arbitrary because it is unused, it would be
991 // the requested position type if there were no xPosForVerticalArrowNavigation set.
992 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
993 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
997 moveTo(position, userTriggered);
999 case AlterationExtend:
1001 if (!m_selection.isCaret()
1002 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
1003 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1004 // Don't let the selection go across the base position directly. Needed to match mac
1005 // behavior when, for instance, word-selecting backwards starting with the caret in
1006 // the middle of a word and then word-selecting forward, leaving the caret in the
1007 // same place where it was, instead of directly selecting to the end of the word.
1008 VisibleSelection newSelection = m_selection;
1009 newSelection.setExtent(position);
1010 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1011 position = m_selection.visibleBase();
1014 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1015 // base in place and moving the extent. Matches NSTextView.
1016 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1017 setExtent(position, userTriggered);
1019 TextDirection textDirection = directionOfEnclosingBlock();
1020 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1021 setEnd(position, userTriggered);
1023 setStart(position, userTriggered);
1028 if (granularity == LineGranularity || granularity == ParagraphGranularity)
1029 m_xPosForVerticalArrowNavigation = x;
1031 if (userTriggered == UserTriggered)
1032 m_granularity = CharacterGranularity;
1034 setCaretRectNeedsUpdate();
1039 // FIXME: Maybe baseline would be better?
1040 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1042 IntRect rect = c.absoluteCaretBounds();
1045 y = rect.y() + rect.height() / 2;
1049 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1051 if (!verticalDistance)
1054 if (userTriggered == UserTriggered) {
1055 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
1056 trialFrameSelection->setSelection(m_selection);
1057 trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered);
1060 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1062 VisiblePosition pos;
1063 LayoutUnit xPos = 0;
1065 case AlterationMove:
1066 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1067 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1068 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1070 case AlterationExtend:
1071 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1072 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1073 m_selection.setAffinity(DOWNSTREAM);
1078 if (!absoluteCaretY(pos, startY))
1080 if (direction == DirectionUp)
1084 VisiblePosition result;
1085 VisiblePosition next;
1086 for (VisiblePosition p = pos; ; p = next) {
1087 if (direction == DirectionUp)
1088 next = previousLinePosition(p, xPos);
1090 next = nextLinePosition(p, xPos);
1092 if (next.isNull() || next == p)
1095 if (!absoluteCaretY(next, nextY))
1097 if (direction == DirectionUp)
1099 if (nextY - startY > static_cast<int>(verticalDistance))
1101 if (nextY >= lastY) {
1107 if (result.isNull())
1111 case AlterationMove:
1112 moveTo(result, userTriggered, align);
1114 case AlterationExtend:
1115 setExtent(result, userTriggered);
1119 if (userTriggered == UserTriggered)
1120 m_granularity = CharacterGranularity;
1122 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1127 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1137 pos = m_selection.start();
1140 pos = m_selection.end();
1143 pos = m_selection.base();
1146 pos = m_selection.extent();
1150 LocalFrame* frame = pos.document()->frame();
1154 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1155 VisiblePosition visiblePosition(pos, m_selection.affinity());
1156 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1157 // after the selection is created and before this function is called.
1158 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1159 m_xPosForVerticalArrowNavigation = x;
1161 x = m_xPosForVerticalArrowNavigation;
1166 void FrameSelection::clear()
1168 m_granularity = CharacterGranularity;
1169 setSelection(VisibleSelection());
1172 void FrameSelection::prepareForDestruction()
1174 m_granularity = CharacterGranularity;
1176 m_caretBlinkTimer.stop();
1178 RenderView* view = m_frame->contentRenderer();
1180 view->clearSelection();
1182 setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
1183 m_previousCaretNode.clear();
1186 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1188 if (m_selection.isBaseFirst())
1189 setBase(pos, trigger);
1191 setExtent(pos, trigger);
1194 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1196 if (m_selection.isBaseFirst())
1197 setExtent(pos, trigger);
1199 setBase(pos, trigger);
1202 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1204 const bool selectionHasDirection = true;
1205 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1208 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1210 const bool selectionHasDirection = true;
1211 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1214 RenderBlock* FrameSelection::caretRenderer() const
1216 return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1219 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1221 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1224 static bool isTextFormControl(const VisibleSelection& selection)
1226 return enclosingTextFormControl(selection.start());
1229 IntRect FrameSelection::absoluteCaretBounds()
1231 ASSERT(m_frame->document()->lifecycle().state() != DocumentLifecycle::InPaintInvalidation);
1232 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1233 if (!isNonOrphanedCaret(m_selection)) {
1236 if (isTextFormControl(m_selection))
1237 updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start().isCandidate() ? m_selection.start() : Position(), m_selection.affinity()));
1239 updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity()));
1241 return absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1244 static LayoutRect localCaretRect(const VisibleSelection& m_selection, const PositionWithAffinity& caretPosition, RenderObject*& renderer)
1247 if (!isNonOrphanedCaret(m_selection))
1248 return LayoutRect();
1250 return localCaretRectOfPosition(caretPosition, renderer);
1253 void FrameSelection::invalidateCaretRect()
1255 if (!m_caretRectDirty)
1257 m_caretRectDirty = false;
1259 RenderObject* renderer = nullptr;
1260 LayoutRect newRect = localCaretRect(m_selection, PositionWithAffinity(m_selection.start(), m_selection.affinity()), renderer);
1261 Node* newNode = renderer ? renderer->node() : nullptr;
1263 if (!m_caretBlinkTimer.isActive() && newNode == m_previousCaretNode && newRect == m_previousCaretRect)
1266 RenderView* view = m_frame->document()->renderView();
1267 if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable()))
1268 invalidateLocalCaretRect(m_previousCaretNode.get(), m_previousCaretRect);
1269 if (newNode && shouldRepaintCaret(view, newNode->isContentEditable()))
1270 invalidateLocalCaretRect(newNode, newRect);
1272 m_previousCaretNode = newNode;
1273 m_previousCaretRect = newRect;
1276 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1278 if (m_selection.isCaret() && m_shouldPaintCaret) {
1279 updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start(), m_selection.affinity()));
1280 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);
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 Frame* 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 // FIXME: This is not yet implemented for cross-process frame relationships.
1339 if (!parent->isLocalFrame())
1342 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1343 // FIXME: Doesn't work for OOPI.
1344 HTMLFrameOwnerElement* ownerElement = m_frame->deprecatedLocalOwner();
1347 ContainerNode* ownerElementParent = ownerElement->parentNode();
1348 if (!ownerElementParent)
1351 // 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.
1352 if (!ownerElementParent->hasEditableStyle())
1355 // Create compute positions before and after the element.
1356 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1357 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1358 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1360 // Focus on the parent frame, and then select from before this element to after.
1361 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1362 page->focusController().setFocusedFrame(parent);
1363 toLocalFrame(parent)->selection().setSelection(newSelection);
1366 void FrameSelection::selectAll()
1368 Document* document = m_frame->document();
1370 if (isHTMLSelectElement(document->focusedElement())) {
1371 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement());
1372 if (selectElement->canSelectAll()) {
1373 selectElement->selectAll();
1378 RefPtrWillBeRawPtr<Node> root = nullptr;
1379 Node* selectStartTarget = 0;
1380 if (isContentEditable()) {
1381 root = highestEditableRoot(m_selection.start());
1382 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1383 selectStartTarget = shadowRoot->shadowHost();
1385 selectStartTarget = root.get();
1387 root = m_selection.nonBoundaryShadowTreeRootNode();
1389 selectStartTarget = root->shadowHost();
1391 root = document->documentElement();
1392 selectStartTarget = document->body();
1398 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)))
1401 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1402 setSelection(newSelection);
1403 selectFrameElementInParentIfFullySelected();
1404 notifyRendererOfSelectionChange(UserTriggered);
1407 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options)
1409 if (!range || !range->startContainer() || !range->endContainer())
1411 ASSERT(range->startContainer()->document() == range->endContainer()->document());
1413 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1414 // they start at the beginning of the next line instead
1415 m_logicalRange = nullptr;
1416 stopObservingVisibleSelectionChangeIfNecessary();
1418 VisibleSelection newSelection(range, affinity, directional == Directional);
1419 setSelection(newSelection, options);
1421 m_logicalRange = range->cloneRange();
1422 startObservingVisibleSelectionChange();
1427 PassRefPtrWillBeRawPtr<Range> FrameSelection::firstRange() const
1430 return m_logicalRange->cloneRange();
1431 return m_selection.firstRange();
1434 bool FrameSelection::isInPasswordField() const
1436 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1437 return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->type() == InputTypeNames::password;
1440 void FrameSelection::notifyAccessibilityForSelectionChange()
1442 if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) {
1443 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1444 cache->selectionChanged(m_selection.start().containerNode());
1448 void FrameSelection::notifyCompositorForSelectionChange()
1450 if (!RuntimeEnabledFeatures::compositedSelectionUpdateEnabled())
1453 scheduleVisualUpdate();
1456 void FrameSelection::notifyEventHandlerForSelectionChange()
1458 m_frame->eventHandler().notifySelectionChanged();
1461 void FrameSelection::focusedOrActiveStateChanged()
1463 bool activeAndFocused = isFocusedAndActive();
1465 RefPtrWillBeRawPtr<Document> document = m_frame->document();
1466 document->updateRenderTreeIfNeeded();
1468 // Because RenderObject::selectionBackgroundColor() and
1469 // RenderObject::selectionForegroundColor() check if the frame is active,
1470 // we have to update places those colors were painted.
1471 if (RenderView* view = document->renderView())
1472 view->invalidatePaintForSelection();
1474 // Caret appears in the active frame.
1475 if (activeAndFocused)
1476 setSelectionFromNone();
1478 m_frame->spellChecker().spellCheckAfterBlur();
1479 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1481 // Update for caps lock state
1482 m_frame->eventHandler().capsLockStateMayHaveChanged();
1484 // We may have lost active status even though the focusElement hasn't changed
1485 // give the element a chance to recalc style if its affected by focus.
1486 if (Element* element = document->focusedElement())
1487 element->focusStateChanged();
1489 // Secure keyboard entry is set by the active frame.
1490 if (document->useSecureKeyboardEntryWhenActive())
1491 setUseSecureKeyboardEntry(activeAndFocused);
1494 void FrameSelection::pageActivationChanged()
1496 focusedOrActiveStateChanged();
1499 void FrameSelection::updateSecureKeyboardEntryIfActive()
1501 if (m_frame->document() && isFocusedAndActive())
1502 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1505 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1508 enableSecureTextInput();
1510 disableSecureTextInput();
1513 void FrameSelection::setFocused(bool flag)
1515 if (m_focused == flag)
1519 focusedOrActiveStateChanged();
1522 bool FrameSelection::isFocusedAndActive() const
1524 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
1527 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame)
1529 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
1532 void FrameSelection::updateAppearance(ResetCaretBlinkOption option)
1534 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
1535 // the FrameSelection will paint a blinking caret as usual).
1536 bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
1538 bool shouldBlink = !paintBlockCursor && shouldBlinkCaret();
1540 bool willNeedCaretRectUpdate = false;
1542 // If the caret moved, stop the blink timer so we can restart with a
1543 // black caret in the new location.
1544 if (option == ResetCaretBlink || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) {
1545 m_caretBlinkTimer.stop();
1547 m_shouldPaintCaret = false;
1548 willNeedCaretRectUpdate = true;
1551 // Start blinking with a black caret. Be sure not to restart if we're
1552 // already blinking in the right location.
1553 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1554 if (double blinkInterval = RenderTheme::theme().caretBlinkInterval())
1555 m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE);
1557 m_shouldPaintCaret = true;
1558 willNeedCaretRectUpdate = true;
1561 if (willNeedCaretRectUpdate)
1562 setCaretRectNeedsUpdate();
1564 RenderView* view = m_frame->contentRenderer();
1568 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1569 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1571 VisibleSelection selection;
1572 if (isTextFormControl(m_selection)) {
1573 Position endPosition = paintBlockCursor ? m_selection.extent().next() : m_selection.end();
1574 selection.setWithoutValidation(m_selection.start(), endPosition);
1576 VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : m_selection.visibleEnd();
1577 selection = VisibleSelection(m_selection.visibleStart(), endVisiblePosition);
1580 if (!selection.isRange()) {
1581 view->clearSelection();
1585 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1587 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1588 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1589 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1590 // and will fill the gap before 'bar'.
1591 Position startPos = selection.start();
1592 Position candidate = startPos.downstream();
1593 if (candidate.isCandidate())
1594 startPos = candidate;
1595 Position endPos = selection.end();
1596 candidate = endPos.upstream();
1597 if (candidate.isCandidate())
1600 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1601 // because we don't yet notify the FrameSelection of text removal.
1602 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1603 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1604 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1605 if (startRenderer->view() == view && endRenderer->view() == view)
1606 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1610 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1612 if (caretVisibility() == visibility)
1615 CaretBase::setCaretVisibility(visibility);
1620 bool FrameSelection::shouldBlinkCaret() const
1622 if (!caretIsVisible() || !isCaret())
1625 if (m_frame->settings() && m_frame->settings()->caretBrowsingEnabled())
1628 Element* root = rootEditableElement();
1632 Element* focusedElement = root->document().focusedElement();
1633 if (!focusedElement)
1636 return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode());
1639 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1641 ASSERT(caretIsVisible());
1643 if (isCaretBlinkingSuspended() && m_shouldPaintCaret)
1645 m_shouldPaintCaret = !m_shouldPaintCaret;
1646 setCaretRectNeedsUpdate();
1649 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1651 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1652 textControl->selectionChanged(userTriggered == UserTriggered);
1655 // Helper function that tells whether a particular node is an element that has an entire
1656 // LocalFrame and FrameView, a <frame>, <iframe>, or <object>.
1657 static bool isFrameElement(const Node* n)
1661 RenderObject* renderer = n->renderer();
1662 if (!renderer || !renderer->isWidget())
1664 Widget* widget = toRenderWidget(renderer)->widget();
1665 return widget && widget->isFrameView();
1668 void FrameSelection::setFocusedNodeIfNeeded()
1670 if (isNone() || !isFocused())
1673 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1674 if (caretBrowsing) {
1675 if (Element* anchor = enclosingAnchorElement(base())) {
1676 m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
1681 if (Element* target = rootEditableElement()) {
1682 // Walk up the DOM tree to search for a node to focus.
1684 // We don't want to set focus on a subframe when selecting in a parent frame,
1685 // so add the !isFrameElement check here. There's probably a better way to make this
1686 // work in the long term, but this is the safest fix at this time.
1687 if (target->isMouseFocusable() && !isFrameElement(target)) {
1688 m_frame->page()->focusController().setFocusedElement(target, m_frame);
1691 target = target->parentOrShadowHostElement();
1693 m_frame->document()->setFocusedElement(nullptr);
1697 m_frame->page()->focusController().setFocusedElement(0, m_frame);
1700 static String extractSelectedText(const FrameSelection& selection, TextIteratorBehavior behavior)
1702 // We remove '\0' characters because they are not visibly rendered to the user.
1703 return plainText(selection.toNormalizedRange().get(), behavior).replace(0, "");
1706 String FrameSelection::selectedText() const
1708 return extractSelectedText(*this, TextIteratorDefaultBehavior);
1711 String FrameSelection::selectedTextForClipboard() const
1713 if (m_frame->settings() && m_frame->settings()->selectionIncludesAltImageText())
1714 return extractSelectedText(*this, TextIteratorEmitsImageAltText);
1715 return selectedText();
1718 FloatRect FrameSelection::bounds() const
1720 m_frame->document()->updateRenderTreeIfNeeded();
1722 FrameView* view = m_frame->view();
1723 RenderView* renderView = m_frame->contentRenderer();
1725 if (!view || !renderView)
1728 LayoutRect selectionRect = renderView->selectionBounds();
1729 return selectionRect;
1732 static inline HTMLFormElement* associatedFormElement(HTMLElement& element)
1734 if (isHTMLFormElement(element))
1735 return &toHTMLFormElement(element);
1736 return element.formOwner();
1739 // Scans logically forward from "start", including any child frames.
1740 static HTMLFormElement* scanForForm(Node* start)
1745 HTMLElement* element = start->isHTMLElement() ? toHTMLElement(start) : Traversal<HTMLElement>::next(*start);
1746 for (; element; element = Traversal<HTMLElement>::next(*element)) {
1747 if (HTMLFormElement* form = associatedFormElement(*element))
1750 if (isHTMLFrameElementBase(*element)) {
1751 Node* childDocument = toHTMLFrameElementBase(*element).contentDocument();
1752 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1759 // We look for either the form containing the current focus, or for one immediately after it
1760 HTMLFormElement* FrameSelection::currentForm() const
1762 // Start looking either at the active (first responder) node, or where the selection is.
1763 Node* start = m_frame->document()->focusedElement();
1765 start = this->start().deprecatedNode();
1769 // Try walking up the node tree to find a form element.
1770 for (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*start); element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
1771 if (HTMLFormElement* form = associatedFormElement(*element))
1775 // Try walking forward in the node tree to find a form element.
1776 return scanForForm(start);
1779 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1783 switch (selectionType()) {
1786 case CaretSelection:
1787 rect = absoluteCaretBounds();
1789 case RangeSelection:
1790 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds());
1794 Position start = this->start();
1795 ASSERT(start.deprecatedNode());
1796 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1797 // FIXME: This code only handles scrolling the startContainer's layer, but
1798 // the selection rect could intersect more than just that.
1799 // See <rdar://problem/4799899>.
1800 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1805 void FrameSelection::setSelectionFromNone()
1807 // Put a caret inside the body if the entire frame is editable (either the
1808 // entire WebView is editable or designMode is on for this document).
1810 Document* document = m_frame->document();
1811 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1812 if (!isNone() || !(document->hasEditableStyle() || caretBrowsing))
1815 Element* documentElement = document->documentElement();
1816 if (!documentElement)
1818 if (HTMLBodyElement* body = Traversal<HTMLBodyElement>::firstChild(*documentElement))
1819 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
1822 bool FrameSelection::dispatchSelectStart()
1824 Node* selectStartTarget = m_selection.extent().containerNode();
1825 if (!selectStartTarget)
1828 return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
1831 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
1833 m_shouldShowBlockCursor = shouldShowBlockCursor;
1835 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1840 void FrameSelection::didChangeVisibleSelection()
1842 ASSERT(m_observingVisibleSelection);
1843 // Invalidate the logical range when the underlying VisibleSelection has changed.
1844 m_logicalRange = nullptr;
1845 m_selection.clearChangeObserver();
1846 m_observingVisibleSelection = false;
1849 VisibleSelection FrameSelection::validateSelection(const VisibleSelection& selection)
1851 if (!m_frame || selection.isNone())
1854 Position base = selection.base();
1855 Position extent = selection.extent();
1856 bool isBaseValid = base.document() == m_frame->document();
1857 bool isExtentValid = extent.document() == m_frame->document();
1859 if (isBaseValid && isExtentValid)
1862 VisibleSelection newSelection;
1864 newSelection.setWithoutValidation(base, base);
1865 } else if (isExtentValid) {
1866 newSelection.setWithoutValidation(extent, extent);
1868 return newSelection;
1871 void FrameSelection::startObservingVisibleSelectionChange()
1873 ASSERT(!m_observingVisibleSelection);
1874 m_selection.setChangeObserver(*this);
1875 m_observingVisibleSelection = true;
1878 void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary()
1880 if (m_observingVisibleSelection) {
1881 m_selection.clearChangeObserver();
1882 m_observingVisibleSelection = false;
1888 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
1890 m_selection.formatForDebugger(buffer, length);
1893 void FrameSelection::showTreeForThis() const
1895 m_selection.showTreeForThis();
1900 void FrameSelection::trace(Visitor* visitor)
1902 visitor->trace(m_frame);
1903 visitor->trace(m_selection);
1904 visitor->trace(m_originalBase);
1905 visitor->trace(m_logicalRange);
1906 visitor->trace(m_previousCaretNode);
1907 visitor->trace(m_typingStyle);
1908 VisibleSelection::ChangeObserver::trace(visitor);
1911 void FrameSelection::setCaretRectNeedsUpdate()
1913 m_caretRectDirty = true;
1915 scheduleVisualUpdate();
1918 void FrameSelection::scheduleVisualUpdate() const
1922 if (Page* page = m_frame->page())
1923 page->animator().scheduleVisualUpdate();
1930 void showTree(const blink::FrameSelection& sel)
1932 sel.showTreeForThis();
1935 void showTree(const blink::FrameSelection* sel)
1938 sel->showTreeForThis();