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 "bindings/v8/ExceptionState.h"
31 #include "core/HTMLNames.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/LocalDOMWindow.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()
111 // Oilpan: No need to clear out VisibleSelection observer;
112 // it is finalized as a part object of FrameSelection.
113 stopObservingVisibleSelectionChangeIfNecessary();
117 Element* FrameSelection::rootEditableElementOrDocumentElement() const
119 Element* selectionRoot = m_selection.rootEditableElement();
120 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
123 Node* FrameSelection::rootEditableElementOrTreeScopeRootNode() const
125 Element* selectionRoot = m_selection.rootEditableElement();
127 return selectionRoot;
129 Node* node = m_selection.base().containerNode();
130 return node ? &node->treeScope().rootNode() : 0;
133 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
135 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
136 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
139 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
141 const bool selectionHasDirection = true;
142 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
143 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
146 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
148 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
149 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
152 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
154 RenderedPosition base(visibleBase);
155 RenderedPosition extent(visibleExtent);
157 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
160 if (base.atLeftBoundaryOfBidiRun()) {
161 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
162 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
163 visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun());
169 if (base.atRightBoundaryOfBidiRun()) {
170 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
171 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
172 visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun());
178 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
179 visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun());
183 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
184 visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun());
189 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
190 EndPointsAdjustmentMode endpointsAdjustmentMode)
192 VisibleSelection newSelection = passedNewSelection;
193 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
195 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
196 VisiblePosition newBase = base;
197 VisiblePosition extent = newSelection.visibleExtent();
198 VisiblePosition newExtent = extent;
199 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
200 adjustEndpointsAtBidiBoundary(newBase, newExtent);
202 if (newBase != base || newExtent != extent) {
203 m_originalBase = base;
204 newSelection.setBase(newBase);
205 newSelection.setExtent(newExtent);
206 } else if (m_originalBase.isNotNull()) {
207 if (m_selection.base() == newSelection.base())
208 newSelection.setBase(m_originalBase);
209 m_originalBase.clear();
212 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
213 if (m_selection == newSelection)
216 setSelection(newSelection, granularity);
219 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
221 bool closeTyping = options & CloseTyping;
222 bool shouldClearTypingStyle = options & ClearTypingStyle;
223 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
225 VisibleSelection s = validateSelection(newSelection);
226 if (shouldAlwaysUseDirectionalSelection(m_frame))
227 s.setIsDirectional(true);
234 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
235 // if document->frame() == m_frame we can get into an infinite loop
236 if (s.base().anchorNode()) {
237 Document& document = *s.base().document();
238 if (document.frame() && document.frame() != m_frame && document != m_frame->document()) {
239 RefPtr<LocalFrame> guard = document.frame();
240 document.frame()->selection().setSelection(s, options, align, granularity);
241 // It's possible that during the above set selection, this FrameSelection has been modified by
242 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
243 // the frame is about to be destroyed. If this is the case, clear our selection.
244 if (guard->hasOneRef() && !m_selection.isNonOrphanedCaretOrRange())
250 m_granularity = granularity;
253 TypingCommand::closeTyping(m_frame);
255 if (shouldClearTypingStyle)
258 if (m_selection == s) {
259 // Even if selection was not changed, selection offsets may have been changed.
260 m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
261 notifyRendererOfSelectionChange(userTriggered);
265 VisibleSelection oldSelection = m_selection;
268 setCaretRectNeedsUpdate();
270 if (!s.isNone() && !(options & DoNotSetFocus))
271 setFocusedNodeIfNeeded();
273 if (!(options & DoNotUpdateAppearance)) {
274 m_frame->document()->updateLayoutIgnorePendingStylesheets();
276 // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html
277 DisableCompositingQueryAsserts disabler;
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 m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange));
302 static bool removingNodeRemovesPosition(Node& node, const Position& position)
304 if (!position.anchorNode())
307 if (position.anchorNode() == node)
310 if (!node.isElementNode())
313 Element& element = toElement(node);
314 return element.containsIncludingShadowDOM(position.anchorNode());
317 void FrameSelection::nodeWillBeRemoved(Node& node)
319 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
320 // the selection in the document that created the fragment needs no adjustment.
321 if (isNone() || !node.inActiveDocument())
324 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
325 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
328 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
330 ASSERT(node.document().isActive());
332 bool clearRenderTreeSelection = false;
333 bool clearDOMTreeSelection = false;
335 if (startRemoved || endRemoved) {
336 Position start = m_selection.start();
337 Position end = m_selection.end();
339 updatePositionForNodeRemoval(start, node);
341 updatePositionForNodeRemoval(end, node);
343 if (start.isNotNull() && end.isNotNull()) {
344 if (m_selection.isBaseFirst())
345 m_selection.setWithoutValidation(start, end);
347 m_selection.setWithoutValidation(end, start);
349 clearDOMTreeSelection = true;
351 clearRenderTreeSelection = true;
352 } else if (baseRemoved || extentRemoved) {
353 // The base and/or extent are about to be removed, but the start and end aren't.
354 // Change the base and extent to the start and end, but don't re-validate the
355 // selection, since doing so could move the start and end into the node
356 // that is about to be removed.
357 if (m_selection.isBaseFirst())
358 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
360 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
361 } else if (RefPtrWillBeRawPtr<Range> range = m_selection.firstRange()) {
362 TrackExceptionState exceptionState;
363 Range::CompareResults compareResult = range->compareNode(&node, exceptionState);
364 if (!exceptionState.hadException() && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
365 // If we did nothing here, when this node's renderer was destroyed, the rect that it
366 // occupied would be invalidated, but, selection gaps that change as a result of
367 // the removal wouldn't be invalidated.
368 // FIXME: Don't do so much unnecessary invalidation.
369 clearRenderTreeSelection = true;
373 if (clearRenderTreeSelection)
374 m_selection.start().document()->renderView()->clearSelection();
376 if (clearDOMTreeSelection)
377 setSelection(VisibleSelection(), DoNotSetFocus);
380 static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
382 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
385 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
386 ASSERT(position.offsetInContainerNode() >= 0);
387 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
388 // Replacing text can be viewed as a deletion followed by insertion.
389 if (positionOffset >= offset && positionOffset <= offset + oldLength)
390 positionOffset = offset;
392 // Adjust the offset if the position is after the end of the deleted contents
393 // (positionOffset > offset + oldLength) to avoid having a stale offset.
394 if (positionOffset > offset + oldLength)
395 positionOffset = positionOffset - oldLength + newLength;
397 ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length());
398 // CharacterNode in VisibleSelection must be Text node, because Comment
399 // and ProcessingInstruction node aren't visible.
400 return Position(toText(node), positionOffset);
403 void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
405 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
406 if (isNone() || !node || !node->inDocument())
409 Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength);
410 Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength);
411 Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength);
412 Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength);
413 updateSelectionIfNeeded(base, extent, start, end);
416 static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset)
418 if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor)
421 ASSERT(position.offsetInContainerNode() >= 0);
422 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
424 if (position.anchorNode() == &oldNode)
425 return Position(toText(oldNode.previousSibling()), positionOffset + offset);
427 if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset)
428 return Position(toText(oldNode.previousSibling()), offset);
433 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset)
435 if (isNone() || !oldNode.inDocument())
437 Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset);
438 Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset);
439 Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset);
440 Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset);
441 updateSelectionIfNeeded(base, extent, start, end);
444 static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode)
446 if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor)
448 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
449 ASSERT(position.offsetInContainerNode() >= 0);
450 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
451 unsigned oldLength = oldNode.length();
452 if (positionOffset <= oldLength)
454 return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
457 void FrameSelection::didSplitTextNode(const Text& oldNode)
459 if (isNone() || !oldNode.inDocument())
461 Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode);
462 Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode);
463 Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode);
464 Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode);
465 updateSelectionIfNeeded(base, extent, start, end);
468 void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end)
470 if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end())
472 VisibleSelection newSelection;
473 newSelection.setWithoutValidation(base, extent);
474 setSelection(newSelection, DoNotSetFocus);
477 TextDirection FrameSelection::directionOfEnclosingBlock()
479 return WebCore::directionOfEnclosingBlock(m_selection.extent());
482 TextDirection FrameSelection::directionOfSelection()
484 InlineBox* startBox = 0;
485 InlineBox* endBox = 0;
487 // Cache the VisiblePositions because visibleStart() and visibleEnd()
488 // can cause layout, which has the potential to invalidate lineboxes.
489 VisiblePosition startPosition = m_selection.visibleStart();
490 VisiblePosition endPosition = m_selection.visibleEnd();
491 if (startPosition.isNotNull())
492 startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
493 if (endPosition.isNotNull())
494 endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
495 if (startBox && endBox && startBox->direction() == endBox->direction())
496 return startBox->direction();
498 return directionOfEnclosingBlock();
501 void FrameSelection::didChangeFocus()
503 // Hits in virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html
504 DisableCompositingQueryAsserts disabler;
508 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
510 if (alter != AlterationExtend)
513 Position start = m_selection.start();
514 Position end = m_selection.end();
516 bool baseIsStart = true;
518 if (m_selection.isDirectional()) {
519 // Make base and extent match start and end so we extend the user-visible selection.
520 // This only matters for cases where base and extend point to different positions than
521 // start and end (e.g. after a double-click to select a word).
522 if (m_selection.isBaseFirst())
529 if (directionOfSelection() == LTR)
534 case DirectionForward:
538 if (directionOfSelection() == LTR)
543 case DirectionBackward:
549 m_selection.setBase(start);
550 m_selection.setExtent(end);
552 m_selection.setBase(end);
553 m_selection.setExtent(start);
557 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
559 Settings* settings = m_frame ? m_frame->settings() : 0;
560 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
561 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
562 // Linux and Windows always extend selections from the extent endpoint.
563 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
564 // base/extent always point to the same nodes as start/end, but which points
565 // to which depends on the value of isBaseFirst. Then this can be changed
566 // to just return m_sel.extent().
567 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
570 VisiblePosition FrameSelection::startForPlatform() const
572 return positionForPlatform(true);
575 VisiblePosition FrameSelection::endForPlatform() const
577 return positionForPlatform(false);
580 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
582 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
584 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
585 // In order to skip spaces when moving right, we advance one
586 // word further and then move one word back. Given the
587 // semantics of previousWordPosition() this will put us at the
588 // beginning of the word following.
589 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
590 if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
591 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
593 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
594 if (movingBackwardsMovedPositionToStartOfCurrentWord)
595 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
597 return positionAfterCurrentWord;
600 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
602 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
603 pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
606 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
608 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
610 // The difference between modifyExtendingRight and modifyExtendingForward is:
611 // modifyExtendingForward always extends forward logically.
612 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
613 // it extends forward logically if the enclosing block is LTR direction,
614 // but it extends backward logically if the enclosing block is RTL direction.
615 switch (granularity) {
616 case CharacterGranularity:
617 if (directionOfEnclosingBlock() == LTR)
618 pos = pos.next(CanSkipOverEditingBoundary);
620 pos = pos.previous(CanSkipOverEditingBoundary);
622 case WordGranularity:
623 if (directionOfEnclosingBlock() == LTR)
624 pos = nextWordPositionForPlatform(pos);
626 pos = previousWordPosition(pos);
629 if (directionOfEnclosingBlock() == LTR)
630 pos = modifyExtendingForward(granularity);
632 pos = modifyExtendingBackward(granularity);
634 case SentenceGranularity:
635 case LineGranularity:
636 case ParagraphGranularity:
637 case SentenceBoundary:
638 case ParagraphBoundary:
639 case DocumentBoundary:
640 // FIXME: implement all of the above?
641 pos = modifyExtendingForward(granularity);
644 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
648 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
650 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
651 switch (granularity) {
652 case CharacterGranularity:
653 pos = pos.next(CanSkipOverEditingBoundary);
655 case WordGranularity:
656 pos = nextWordPositionForPlatform(pos);
658 case SentenceGranularity:
659 pos = nextSentencePosition(pos);
661 case LineGranularity:
662 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
664 case ParagraphGranularity:
665 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
667 case SentenceBoundary:
668 pos = endOfSentence(endForPlatform());
671 pos = logicalEndOfLine(endForPlatform());
673 case ParagraphBoundary:
674 pos = endOfParagraph(endForPlatform());
676 case DocumentBoundary:
677 pos = endForPlatform();
678 if (isEditablePosition(pos.deepEquivalent()))
679 pos = endOfEditableContent(pos);
681 pos = endOfDocument(pos);
684 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
688 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
691 switch (granularity) {
692 case CharacterGranularity:
694 if (directionOfSelection() == LTR)
695 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
697 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
699 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
701 case WordGranularity: {
702 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
703 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
706 case SentenceGranularity:
707 case LineGranularity:
708 case ParagraphGranularity:
709 case SentenceBoundary:
710 case ParagraphBoundary:
711 case DocumentBoundary:
712 // FIXME: Implement all of the above.
713 pos = modifyMovingForward(granularity);
716 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
722 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
725 // FIXME: Stay in editable content for the less common granularities.
726 switch (granularity) {
727 case CharacterGranularity:
729 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
731 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary);
733 case WordGranularity:
734 pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
736 case SentenceGranularity:
737 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
739 case LineGranularity: {
740 // down-arrowing from a range selection that ends at the start of a line needs
741 // to leave the selection at that line start (no need to call nextLinePosition!)
742 pos = endForPlatform();
743 if (!isRange() || !isStartOfLine(pos))
744 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
747 case ParagraphGranularity:
748 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
750 case SentenceBoundary:
751 pos = endOfSentence(endForPlatform());
754 pos = logicalEndOfLine(endForPlatform());
756 case ParagraphBoundary:
757 pos = endOfParagraph(endForPlatform());
759 case DocumentBoundary:
760 pos = endForPlatform();
761 if (isEditablePosition(pos.deepEquivalent()))
762 pos = endOfEditableContent(pos);
764 pos = endOfDocument(pos);
770 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
772 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
774 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
775 // modifyExtendingBackward always extends backward logically.
776 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
777 // it extends backward logically if the enclosing block is LTR direction,
778 // but it extends forward logically if the enclosing block is RTL direction.
779 switch (granularity) {
780 case CharacterGranularity:
781 if (directionOfEnclosingBlock() == LTR)
782 pos = pos.previous(CanSkipOverEditingBoundary);
784 pos = pos.next(CanSkipOverEditingBoundary);
786 case WordGranularity:
787 if (directionOfEnclosingBlock() == LTR)
788 pos = previousWordPosition(pos);
790 pos = nextWordPositionForPlatform(pos);
793 if (directionOfEnclosingBlock() == LTR)
794 pos = modifyExtendingBackward(granularity);
796 pos = modifyExtendingForward(granularity);
798 case SentenceGranularity:
799 case LineGranularity:
800 case ParagraphGranularity:
801 case SentenceBoundary:
802 case ParagraphBoundary:
803 case DocumentBoundary:
804 pos = modifyExtendingBackward(granularity);
807 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
811 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
813 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
815 // Extending a selection backward by word or character from just after a table selects
816 // the table. This "makes sense" from the user perspective, esp. when deleting.
817 // It was done here instead of in VisiblePosition because we want VPs to iterate
819 switch (granularity) {
820 case CharacterGranularity:
821 pos = pos.previous(CanSkipOverEditingBoundary);
823 case WordGranularity:
824 pos = previousWordPosition(pos);
826 case SentenceGranularity:
827 pos = previousSentencePosition(pos);
829 case LineGranularity:
830 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
832 case ParagraphGranularity:
833 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
835 case SentenceBoundary:
836 pos = startOfSentence(startForPlatform());
839 pos = logicalStartOfLine(startForPlatform());
841 case ParagraphBoundary:
842 pos = startOfParagraph(startForPlatform());
844 case DocumentBoundary:
845 pos = startForPlatform();
846 if (isEditablePosition(pos.deepEquivalent()))
847 pos = startOfEditableContent(pos);
849 pos = startOfDocument(pos);
852 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
856 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
859 switch (granularity) {
860 case CharacterGranularity:
862 if (directionOfSelection() == LTR)
863 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
865 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
867 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
869 case WordGranularity: {
870 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
871 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
874 case SentenceGranularity:
875 case LineGranularity:
876 case ParagraphGranularity:
877 case SentenceBoundary:
878 case ParagraphBoundary:
879 case DocumentBoundary:
880 // FIXME: Implement all of the above.
881 pos = modifyMovingBackward(granularity);
884 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
890 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
893 switch (granularity) {
894 case CharacterGranularity:
896 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
898 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary);
900 case WordGranularity:
901 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
903 case SentenceGranularity:
904 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
906 case LineGranularity:
907 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
909 case ParagraphGranularity:
910 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
912 case SentenceBoundary:
913 pos = startOfSentence(startForPlatform());
916 pos = logicalStartOfLine(startForPlatform());
918 case ParagraphBoundary:
919 pos = startOfParagraph(startForPlatform());
921 case DocumentBoundary:
922 pos = startForPlatform();
923 if (isEditablePosition(pos.deepEquivalent()))
924 pos = startOfEditableContent(pos);
926 pos = startOfDocument(pos);
932 static bool isBoundary(TextGranularity granularity)
934 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
937 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
939 if (userTriggered == UserTriggered) {
940 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
941 trialFrameSelection->setSelection(m_selection);
942 trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered);
944 if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
948 willBeModified(alter, direction);
950 bool wasRange = m_selection.isRange();
951 VisiblePosition originalStartPosition = m_selection.visibleStart();
952 VisiblePosition position;
955 if (alter == AlterationMove)
956 position = modifyMovingRight(granularity);
958 position = modifyExtendingRight(granularity);
960 case DirectionForward:
961 if (alter == AlterationExtend)
962 position = modifyExtendingForward(granularity);
964 position = modifyMovingForward(granularity);
967 if (alter == AlterationMove)
968 position = modifyMovingLeft(granularity);
970 position = modifyExtendingLeft(granularity);
972 case DirectionBackward:
973 if (alter == AlterationExtend)
974 position = modifyExtendingBackward(granularity);
976 position = modifyMovingBackward(granularity);
980 if (position.isNull())
983 if (isSpatialNavigationEnabled(m_frame))
984 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
987 // Some of the above operations set an xPosForVerticalArrowNavigation.
988 // Setting a selection will clear it, so save it to possibly restore later.
989 // Note: the START position type is arbitrary because it is unused, it would be
990 // the requested position type if there were no xPosForVerticalArrowNavigation set.
991 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
992 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
996 moveTo(position, userTriggered);
998 case AlterationExtend:
1000 if (!m_selection.isCaret()
1001 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
1002 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1003 // Don't let the selection go across the base position directly. Needed to match mac
1004 // behavior when, for instance, word-selecting backwards starting with the caret in
1005 // the middle of a word and then word-selecting forward, leaving the caret in the
1006 // same place where it was, instead of directly selecting to the end of the word.
1007 VisibleSelection newSelection = m_selection;
1008 newSelection.setExtent(position);
1009 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1010 position = m_selection.visibleBase();
1013 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1014 // base in place and moving the extent. Matches NSTextView.
1015 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1016 setExtent(position, userTriggered);
1018 TextDirection textDirection = directionOfEnclosingBlock();
1019 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1020 setEnd(position, userTriggered);
1022 setStart(position, userTriggered);
1027 if (granularity == LineGranularity || granularity == ParagraphGranularity)
1028 m_xPosForVerticalArrowNavigation = x;
1030 if (userTriggered == UserTriggered)
1031 m_granularity = CharacterGranularity;
1033 setCaretRectNeedsUpdate();
1038 // FIXME: Maybe baseline would be better?
1039 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1041 IntRect rect = c.absoluteCaretBounds();
1044 y = rect.y() + rect.height() / 2;
1048 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1050 if (!verticalDistance)
1053 if (userTriggered == UserTriggered) {
1054 OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
1055 trialFrameSelection->setSelection(m_selection);
1056 trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered);
1059 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1061 VisiblePosition pos;
1062 LayoutUnit xPos = 0;
1064 case AlterationMove:
1065 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1066 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1067 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1069 case AlterationExtend:
1070 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1071 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1072 m_selection.setAffinity(DOWNSTREAM);
1077 if (!absoluteCaretY(pos, startY))
1079 if (direction == DirectionUp)
1083 VisiblePosition result;
1084 VisiblePosition next;
1085 for (VisiblePosition p = pos; ; p = next) {
1086 if (direction == DirectionUp)
1087 next = previousLinePosition(p, xPos);
1089 next = nextLinePosition(p, xPos);
1091 if (next.isNull() || next == p)
1094 if (!absoluteCaretY(next, nextY))
1096 if (direction == DirectionUp)
1098 if (nextY - startY > static_cast<int>(verticalDistance))
1100 if (nextY >= lastY) {
1106 if (result.isNull())
1110 case AlterationMove:
1111 moveTo(result, userTriggered, align);
1113 case AlterationExtend:
1114 setExtent(result, userTriggered);
1118 if (userTriggered == UserTriggered)
1119 m_granularity = CharacterGranularity;
1121 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1126 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1136 pos = m_selection.start();
1139 pos = m_selection.end();
1142 pos = m_selection.base();
1145 pos = m_selection.extent();
1149 LocalFrame* frame = pos.document()->frame();
1153 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1154 VisiblePosition visiblePosition(pos, m_selection.affinity());
1155 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1156 // after the selection is created and before this function is called.
1157 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1158 m_xPosForVerticalArrowNavigation = x;
1160 x = m_xPosForVerticalArrowNavigation;
1165 void FrameSelection::clear()
1167 m_granularity = CharacterGranularity;
1168 setSelection(VisibleSelection());
1171 void FrameSelection::prepareForDestruction()
1173 m_granularity = CharacterGranularity;
1175 m_caretBlinkTimer.stop();
1177 RenderView* view = m_frame->contentRenderer();
1179 view->clearSelection();
1181 setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
1182 m_previousCaretNode.clear();
1185 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1187 if (m_selection.isBaseFirst())
1188 setBase(pos, trigger);
1190 setExtent(pos, trigger);
1193 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1195 if (m_selection.isBaseFirst())
1196 setExtent(pos, trigger);
1198 setBase(pos, trigger);
1201 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1203 const bool selectionHasDirection = true;
1204 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1207 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1209 const bool selectionHasDirection = true;
1210 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1213 RenderObject* FrameSelection::caretRenderer() const
1215 return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1218 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1220 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1223 LayoutRect FrameSelection::localCaretRect()
1225 if (shouldUpdateCaretRect()) {
1226 if (!isNonOrphanedCaret(m_selection))
1228 else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
1229 m_absCaretBoundsDirty = true;
1232 return localCaretRectWithoutUpdate();
1235 IntRect FrameSelection::absoluteCaretBounds()
1237 recomputeCaretRect();
1238 return m_absCaretBounds;
1241 bool FrameSelection::recomputeCaretRect()
1243 if (!shouldUpdateCaretRect())
1246 if (!m_frame || !m_frame->document()->view())
1249 LayoutRect oldRect = localCaretRectWithoutUpdate();
1250 LayoutRect newRect = localCaretRect();
1251 if (oldRect == newRect && !m_absCaretBoundsDirty)
1254 IntRect oldAbsCaretBounds = m_absCaretBounds;
1255 m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1256 m_absCaretBoundsDirty = false;
1258 if (oldAbsCaretBounds == m_absCaretBounds)
1261 if (RenderView* view = m_frame->document()->renderView()) {
1262 if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable()))
1263 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect);
1264 Node* node = m_selection.start().deprecatedNode();
1265 m_previousCaretNode = node;
1266 if (shouldRepaintCaret(view, isContentEditable()))
1267 repaintCaretForLocalRect(node, newRect);
1273 void FrameSelection::invalidateCaretRect()
1278 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1281 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1283 if (m_selection.isCaret() && m_caretPaint)
1284 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1287 bool FrameSelection::contains(const LayoutPoint& point)
1289 Document* document = m_frame->document();
1291 // Treat a collapsed selection like no selection.
1294 if (!document->renderView())
1297 HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent);
1298 HitTestResult result(point);
1299 document->renderView()->hitTest(request, result);
1300 Node* innerNode = result.innerNode();
1301 if (!innerNode || !innerNode->renderer())
1304 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1305 if (visiblePos.isNull())
1308 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1311 Position start(m_selection.visibleStart().deepEquivalent());
1312 Position end(m_selection.visibleEnd().deepEquivalent());
1313 Position p(visiblePos.deepEquivalent());
1315 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1318 // Workaround for the fact that it's hard to delete a frame.
1319 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1320 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1321 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1322 // mouse or the keyboard after setting the selection.
1323 void FrameSelection::selectFrameElementInParentIfFullySelected()
1325 // Find the parent frame; if there is none, then we have nothing to do.
1326 Frame* parent = m_frame->tree().parent();
1329 Page* page = m_frame->page();
1333 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1336 if (!isStartOfDocument(selection().visibleStart()))
1338 if (!isEndOfDocument(selection().visibleEnd()))
1341 // FIXME: This is not yet implemented for cross-process frame relationships.
1342 if (!parent->isLocalFrame())
1345 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1346 // FIXME: Doesn't work for OOPI.
1347 Element* ownerElement = m_frame->deprecatedLocalOwner();
1350 ContainerNode* ownerElementParent = ownerElement->parentNode();
1351 if (!ownerElementParent)
1354 // 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.
1355 if (!ownerElementParent->rendererIsEditable())
1358 // Create compute positions before and after the element.
1359 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1360 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1361 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1363 // Focus on the parent frame, and then select from before this element to after.
1364 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1365 page->focusController().setFocusedFrame(parent);
1366 toLocalFrame(parent)->selection().setSelection(newSelection);
1369 void FrameSelection::selectAll()
1371 Document* document = m_frame->document();
1373 if (isHTMLSelectElement(document->focusedElement())) {
1374 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement());
1375 if (selectElement->canSelectAll()) {
1376 selectElement->selectAll();
1381 RefPtrWillBeRawPtr<Node> root = nullptr;
1382 Node* selectStartTarget = 0;
1383 if (isContentEditable()) {
1384 root = highestEditableRoot(m_selection.start());
1385 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1386 selectStartTarget = shadowRoot->shadowHost();
1388 selectStartTarget = root.get();
1390 root = m_selection.nonBoundaryShadowTreeRootNode();
1392 selectStartTarget = root->shadowHost();
1394 root = document->documentElement();
1395 selectStartTarget = document->body();
1401 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)))
1404 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1405 setSelection(newSelection);
1406 selectFrameElementInParentIfFullySelected();
1407 notifyRendererOfSelectionChange(UserTriggered);
1410 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options)
1412 if (!range || !range->startContainer() || !range->endContainer())
1414 ASSERT(range->startContainer()->document() == range->endContainer()->document());
1416 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1417 // they start at the beginning of the next line instead
1418 m_logicalRange = nullptr;
1419 stopObservingVisibleSelectionChangeIfNecessary();
1421 VisibleSelection newSelection(range, affinity, directional == Directional);
1422 setSelection(newSelection, options);
1424 m_logicalRange = range->cloneRange();
1425 startObservingVisibleSelectionChange();
1430 PassRefPtrWillBeRawPtr<Range> FrameSelection::firstRange() const
1433 return m_logicalRange->cloneRange();
1434 return m_selection.firstRange();
1437 bool FrameSelection::isInPasswordField() const
1439 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1440 return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPasswordField();
1443 void FrameSelection::notifyAccessibilityForSelectionChange()
1445 if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) {
1446 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1447 cache->selectionChanged(m_selection.start().containerNode());
1451 void FrameSelection::focusedOrActiveStateChanged()
1453 bool activeAndFocused = isFocusedAndActive();
1455 RefPtrWillBeRawPtr<Document> document = m_frame->document();
1456 document->updateRenderTreeIfNeeded();
1458 // Because RenderObject::selectionBackgroundColor() and
1459 // RenderObject::selectionForegroundColor() check if the frame is active,
1460 // we have to update places those colors were painted.
1461 if (RenderView* view = document->renderView())
1462 view->repaintSelection();
1464 // Caret appears in the active frame.
1465 if (activeAndFocused)
1466 setSelectionFromNone();
1468 m_frame->spellChecker().spellCheckAfterBlur();
1469 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1471 // Update for caps lock state
1472 m_frame->eventHandler().capsLockStateMayHaveChanged();
1474 // We may have lost active status even though the focusElement hasn't changed
1475 // give the element a chance to recalc style if its affected by focus.
1476 if (Element* element = document->focusedElement())
1477 element->focusStateChanged();
1479 // Secure keyboard entry is set by the active frame.
1480 if (document->useSecureKeyboardEntryWhenActive())
1481 setUseSecureKeyboardEntry(activeAndFocused);
1484 void FrameSelection::pageActivationChanged()
1486 focusedOrActiveStateChanged();
1489 void FrameSelection::updateSecureKeyboardEntryIfActive()
1491 if (m_frame->document() && isFocusedAndActive())
1492 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1495 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1498 enableSecureTextInput();
1500 disableSecureTextInput();
1503 void FrameSelection::setFocused(bool flag)
1505 if (m_focused == flag)
1509 focusedOrActiveStateChanged();
1512 bool FrameSelection::isFocusedAndActive() const
1514 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
1517 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame)
1519 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
1522 void FrameSelection::updateAppearance()
1524 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
1525 // the FrameSelection will paint a blinking caret as usual).
1526 VisiblePosition forwardPosition;
1527 if (m_shouldShowBlockCursor && m_selection.isCaret()) {
1528 forwardPosition = modifyExtendingForward(CharacterGranularity);
1529 m_caretPaint = forwardPosition.isNull();
1532 bool caretRectChangedOrCleared = recomputeCaretRect();
1533 bool shouldBlink = shouldBlinkCaret() && forwardPosition.isNull();
1535 // If the caret moved, stop the blink timer so we can restart with a
1536 // black caret in the new location.
1537 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) {
1538 m_caretBlinkTimer.stop();
1539 if (!shouldBlink && m_caretPaint) {
1540 m_caretPaint = false;
1541 invalidateCaretRect();
1545 // Start blinking with a black caret. Be sure not to restart if we're
1546 // already blinking in the right location.
1547 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1548 if (double blinkInterval = RenderTheme::theme().caretBlinkInterval())
1549 m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE);
1551 if (!m_caretPaint) {
1552 m_caretPaint = true;
1553 invalidateCaretRect();
1557 RenderView* view = m_frame->contentRenderer();
1561 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1562 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1563 VisibleSelection selection(m_selection.visibleStart(), forwardPosition.isNotNull() ? forwardPosition : m_selection.visibleEnd());
1565 if (!selection.isRange()) {
1566 view->clearSelection();
1570 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1571 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1572 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1573 // and will fill the gap before 'bar'.
1574 Position startPos = selection.start();
1575 Position candidate = startPos.downstream();
1576 if (candidate.isCandidate())
1577 startPos = candidate;
1578 Position endPos = selection.end();
1579 candidate = endPos.upstream();
1580 if (candidate.isCandidate())
1583 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1584 // because we don't yet notify the FrameSelection of text removal.
1585 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1586 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1587 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1588 if (startRenderer->view() == view && endRenderer->view() == view)
1589 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1593 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1595 if (caretVisibility() == visibility)
1598 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1600 m_caretPaint = false;
1601 invalidateCaretRect();
1603 CaretBase::setCaretVisibility(visibility);
1608 bool FrameSelection::shouldBlinkCaret() const
1610 if (!caretIsVisible() || !isCaret())
1613 if (m_frame->settings() && m_frame->settings()->caretBrowsingEnabled())
1616 Node* root = rootEditableElement();
1620 Element* focusedElement = root->document().focusedElement();
1621 if (!focusedElement)
1624 return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode());
1627 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1629 ASSERT(caretIsVisible());
1631 bool caretPaint = m_caretPaint;
1632 if (isCaretBlinkingSuspended() && caretPaint)
1634 m_caretPaint = !caretPaint;
1635 invalidateCaretRect();
1638 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1640 m_frame->document()->updateRenderTreeIfNeeded();
1642 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1643 textControl->selectionChanged(userTriggered == UserTriggered);
1646 // Helper function that tells whether a particular node is an element that has an entire
1647 // LocalFrame and FrameView, a <frame>, <iframe>, or <object>.
1648 static bool isFrameElement(const Node* n)
1652 RenderObject* renderer = n->renderer();
1653 if (!renderer || !renderer->isWidget())
1655 Widget* widget = toRenderWidget(renderer)->widget();
1656 return widget && widget->isFrameView();
1659 void FrameSelection::setFocusedNodeIfNeeded()
1661 if (isNone() || !isFocused())
1664 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1665 if (caretBrowsing) {
1666 if (Element* anchor = enclosingAnchorElement(base())) {
1667 m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
1672 if (Element* target = rootEditableElement()) {
1673 // Walk up the DOM tree to search for a node to focus.
1675 // We don't want to set focus on a subframe when selecting in a parent frame,
1676 // so add the !isFrameElement check here. There's probably a better way to make this
1677 // work in the long term, but this is the safest fix at this time.
1678 if (target->isMouseFocusable() && !isFrameElement(target)) {
1679 m_frame->page()->focusController().setFocusedElement(target, m_frame);
1682 target = target->parentOrShadowHostElement();
1684 m_frame->document()->setFocusedElement(nullptr);
1688 m_frame->page()->focusController().setFocusedElement(0, m_frame);
1691 static String extractSelectedText(const FrameSelection& selection, TextIteratorBehavior behavior)
1693 // We remove '\0' characters because they are not visibly rendered to the user.
1694 return plainText(selection.toNormalizedRange().get(), behavior).replace(0, "");
1697 String FrameSelection::selectedText() const
1699 return extractSelectedText(*this, TextIteratorDefaultBehavior);
1702 String FrameSelection::selectedTextForClipboard() const
1704 if (m_frame->settings() && m_frame->settings()->selectionIncludesAltImageText())
1705 return extractSelectedText(*this, TextIteratorEmitsImageAltText);
1706 return selectedText();
1709 FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
1711 m_frame->document()->updateRenderTreeIfNeeded();
1713 FrameView* view = m_frame->view();
1714 RenderView* renderView = m_frame->contentRenderer();
1716 if (!view || !renderView)
1719 LayoutRect selectionRect = renderView->selectionBounds(clipToVisibleContent);
1720 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1723 static inline HTMLFormElement* associatedFormElement(HTMLElement& element)
1725 if (isHTMLFormElement(element))
1726 return &toHTMLFormElement(element);
1727 return element.formOwner();
1730 // Scans logically forward from "start", including any child frames.
1731 static HTMLFormElement* scanForForm(Node* start)
1736 HTMLElement* element = start->isHTMLElement() ? toHTMLElement(start) : Traversal<HTMLElement>::next(*start);
1737 for (; element; element = Traversal<HTMLElement>::next(*element)) {
1738 if (HTMLFormElement* form = associatedFormElement(*element))
1741 if (isHTMLFrameElementBase(*element)) {
1742 Node* childDocument = toHTMLFrameElementBase(*element).contentDocument();
1743 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1750 // We look for either the form containing the current focus, or for one immediately after it
1751 HTMLFormElement* FrameSelection::currentForm() const
1753 // Start looking either at the active (first responder) node, or where the selection is.
1754 Node* start = m_frame->document()->focusedElement();
1756 start = this->start().deprecatedNode();
1760 // Try walking up the node tree to find a form element.
1761 for (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*start); element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
1762 if (HTMLFormElement* form = associatedFormElement(*element))
1766 // Try walking forward in the node tree to find a form element.
1767 return scanForForm(start);
1770 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1774 switch (selectionType()) {
1777 case CaretSelection:
1778 rect = absoluteCaretBounds();
1780 case RangeSelection:
1781 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1785 Position start = this->start();
1786 ASSERT(start.deprecatedNode());
1787 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1788 // FIXME: This code only handles scrolling the startContainer's layer, but
1789 // the selection rect could intersect more than just that.
1790 // See <rdar://problem/4799899>.
1791 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1796 void FrameSelection::setSelectionFromNone()
1798 // Put a caret inside the body if the entire frame is editable (either the
1799 // entire WebView is editable or designMode is on for this document).
1801 Document* document = m_frame->document();
1802 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1803 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1806 Node* node = document->documentElement();
1809 Node* body = isHTMLBodyElement(*node) ? node : Traversal<HTMLBodyElement>::next(*node);
1811 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
1814 bool FrameSelection::dispatchSelectStart()
1816 Node* selectStartTarget = m_selection.extent().containerNode();
1817 if (!selectStartTarget)
1820 return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
1823 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
1825 m_shouldShowBlockCursor = shouldShowBlockCursor;
1827 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1832 void FrameSelection::didChangeVisibleSelection()
1834 ASSERT(m_observingVisibleSelection);
1835 // Invalidate the logical range when the underlying VisibleSelection has changed.
1836 m_logicalRange = nullptr;
1837 m_selection.clearChangeObserver();
1838 m_observingVisibleSelection = false;
1841 VisibleSelection FrameSelection::validateSelection(const VisibleSelection& selection)
1843 if (!m_frame || selection.isNone())
1846 Position base = selection.base();
1847 Position extent = selection.extent();
1848 bool isBaseValid = base.document() == m_frame->document();
1849 bool isExtentValid = extent.document() == m_frame->document();
1851 if (isBaseValid && isExtentValid)
1854 VisibleSelection newSelection;
1856 newSelection.setWithoutValidation(base, base);
1857 } else if (isExtentValid) {
1858 newSelection.setWithoutValidation(extent, extent);
1860 return newSelection;
1863 void FrameSelection::startObservingVisibleSelectionChange()
1865 ASSERT(!m_observingVisibleSelection);
1866 m_selection.setChangeObserver(*this);
1867 m_observingVisibleSelection = true;
1870 void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary()
1872 if (m_observingVisibleSelection) {
1873 m_selection.clearChangeObserver();
1874 m_observingVisibleSelection = false;
1880 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
1882 m_selection.formatForDebugger(buffer, length);
1885 void FrameSelection::showTreeForThis() const
1887 m_selection.showTreeForThis();
1892 void FrameSelection::trace(Visitor* visitor)
1894 visitor->trace(m_selection);
1895 visitor->trace(m_originalBase);
1896 visitor->trace(m_logicalRange);
1897 visitor->trace(m_previousCaretNode);
1898 visitor->trace(m_typingStyle);
1899 VisibleSelection::ChangeObserver::trace(visitor);
1906 void showTree(const WebCore::FrameSelection& sel)
1908 sel.showTreeForThis();
1911 void showTree(const WebCore::FrameSelection* sel)
1914 sel->showTreeForThis();