2 * Copyright (C) 2004, 2008, 2009, 2010, 2014-2015 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 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 INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "FrameSelection.h"
29 #include "AXObjectCache.h"
30 #include "CharacterData.h"
31 #include "DeleteSelectionCommand.h"
35 #include "EditorClient.h"
37 #include "ElementIterator.h"
39 #include "EventNames.h"
40 #include "FloatQuad.h"
41 #include "FocusController.h"
43 #include "FrameTree.h"
44 #include "FrameView.h"
45 #include "GraphicsContext.h"
46 #include "HTMLBodyElement.h"
47 #include "HTMLFormElement.h"
48 #include "HTMLFrameElement.h"
49 #include "HTMLIFrameElement.h"
50 #include "HTMLNames.h"
51 #include "HTMLSelectElement.h"
52 #include "HitTestRequest.h"
53 #include "HitTestResult.h"
54 #include "InlineTextBox.h"
56 #include "RenderText.h"
57 #include "RenderTextControl.h"
58 #include "RenderTheme.h"
59 #include "RenderView.h"
60 #include "RenderWidget.h"
61 #include "RenderedPosition.h"
63 #include "SpatialNavigation.h"
64 #include "StyleProperties.h"
65 #include "TypingCommand.h"
66 #include "VisibleUnits.h"
68 #include <wtf/text/CString.h>
72 #include "ChromeClient.h"
74 #include "RenderLayer.h"
75 #include "RenderObject.h"
76 #include "RenderStyle.h"
81 using namespace HTMLNames;
83 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
85 return LayoutUnit::min();
88 CaretBase::CaretBase(CaretVisibility visibility)
89 : m_caretRectNeedsUpdate(true)
90 , m_caretVisibility(visibility)
94 DragCaretController::DragCaretController()
99 bool DragCaretController::isContentRichlyEditable() const
101 return isRichlyEditablePosition(m_position.deepEquivalent());
104 IntRect DragCaretController::caretRectInRootViewCoordinates() const
109 if (auto* document = m_position.deepEquivalent().document()) {
110 if (auto* documentView = document->view())
111 return documentView->contentsToRootView(m_position.absoluteCaretBounds());
117 static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame)
119 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
122 FrameSelection::FrameSelection(Frame* frame)
124 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
125 , m_granularity(CharacterGranularity)
126 , m_caretBlinkTimer(*this, &FrameSelection::caretBlinkTimerFired)
127 , m_appearanceUpdateTimer(*this, &FrameSelection::appearanceUpdateTimerFired)
128 , m_caretInsidePositionFixed(false)
129 , m_absCaretBoundsDirty(true)
131 , m_isCaretBlinkingSuspended(false)
132 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
133 , m_shouldShowBlockCursor(false)
134 , m_pendingSelectionUpdate(false)
135 , m_shouldRevealSelection(false)
136 , m_alwaysAlignCursorOnScrollWhenRevealingSelection(false)
138 , m_updateAppearanceEnabled(false)
139 , m_caretBlinks(true)
142 if (shouldAlwaysUseDirectionalSelection(m_frame))
143 m_selection.setIsDirectional(true);
146 Element* FrameSelection::rootEditableElementOrDocumentElement() const
148 Element* selectionRoot = m_selection.rootEditableElement();
149 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
152 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
154 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()),
155 defaultSetSelectionOptions(userTriggered), AXTextStateChangeIntent(), align);
158 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
160 const bool selectionHasDirection = true;
161 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
164 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
166 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), defaultSetSelectionOptions(userTriggered));
169 void FrameSelection::moveTo(const Range* range)
171 VisibleSelection selection = range ? VisibleSelection(range->startPosition(), range->endPosition()) : VisibleSelection();
172 setSelection(selection);
175 void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
177 const bool selectionHasDirection = true;
178 setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
181 void FrameSelection::moveWithoutValidationTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus, const AXTextStateChangeIntent& intent)
183 VisibleSelection newSelection;
184 newSelection.setWithoutValidation(base, extent);
185 newSelection.setIsDirectional(selectionHasDirection);
186 AXTextStateChangeIntent newIntent = intent.type == AXTextStateChangeTypeUnknown ? AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }) : intent;
187 setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus), newIntent);
190 void DragCaretController::setCaretPosition(const VisiblePosition& position)
192 if (Node* node = m_position.deepEquivalent().deprecatedNode())
193 invalidateCaretRect(node);
194 m_position = position;
195 setCaretRectNeedsUpdate();
196 Document* document = nullptr;
197 if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
198 invalidateCaretRect(node);
199 document = &node->document();
201 if (m_position.isNull() || m_position.isOrphan())
204 updateCaretRect(document, m_position);
207 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
209 RenderedPosition base(visibleBase);
210 RenderedPosition extent(visibleExtent);
212 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
215 if (base.atLeftBoundaryOfBidiRun()) {
216 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
217 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
218 visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
224 if (base.atRightBoundaryOfBidiRun()) {
225 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
226 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
227 visibleBase = base.positionAtRightBoundaryOfBiDiRun();
233 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
234 visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
238 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
239 visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
244 void FrameSelection::setSelectionByMouseIfDifferent(const VisibleSelection& passedNewSelection, TextGranularity granularity,
245 EndPointsAdjustmentMode endpointsAdjustmentMode)
247 VisibleSelection newSelection = passedNewSelection;
248 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
250 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
251 VisiblePosition newBase = base;
252 VisiblePosition extent = newSelection.visibleExtent();
253 VisiblePosition newExtent = extent;
254 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
255 adjustEndpointsAtBidiBoundary(newBase, newExtent);
257 if (newBase != base || newExtent != extent) {
258 m_originalBase = base;
259 newSelection.setBase(newBase);
260 newSelection.setExtent(newExtent);
261 } else if (m_originalBase.isNotNull()) {
262 if (m_selection.base() == newSelection.base())
263 newSelection.setBase(m_originalBase);
264 m_originalBase.clear();
267 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
268 if (m_selection == newSelection || !shouldChangeSelection(newSelection))
272 AXTextStateChangeIntent intent;
273 if (AXObjectCache::accessibilityEnabled() && newSelection.isCaret())
274 intent = AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
276 intent = AXTextStateChangeIntent();
277 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent, AlignCursorOnScrollIfNeeded, granularity);
280 bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelection& newSelectionPossiblyWithoutDirection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
282 bool closeTyping = options & CloseTyping;
283 bool shouldClearTypingStyle = options & ClearTypingStyle;
285 VisibleSelection newSelection = newSelectionPossiblyWithoutDirection;
286 if (shouldAlwaysUseDirectionalSelection(m_frame))
287 newSelection.setIsDirectional(true);
290 m_selection = newSelection;
294 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
295 // if document->frame() == m_frame we can get into an infinite loop
296 if (Document* newSelectionDocument = newSelection.base().document()) {
297 if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
298 if (newSelectionFrame != m_frame && newSelectionDocument != m_frame->document()) {
299 newSelectionFrame->selection().setSelection(newSelection, options, AXTextStateChangeIntent(), align, granularity);
300 // It's possible that during the above set selection, this FrameSelection has been modified by
301 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
302 // the frame is about to be destroyed. If this is the case, clear our selection.
303 if (newSelectionFrame->hasOneRef() && m_selection.isNoneOrOrphaned())
310 m_granularity = granularity;
313 TypingCommand::closeTyping(m_frame);
315 if (shouldClearTypingStyle)
318 VisibleSelection oldSelection = m_selection;
319 bool didMutateSelection = oldSelection != newSelection;
320 if (didMutateSelection)
321 m_frame->editor().selectionWillChange();
323 m_selection = newSelection;
325 // Selection offsets should increase when LF is inserted before the caret in InsertLineBreakCommand. See <https://webkit.org/b/56061>.
326 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(newSelection.start()))
327 textControl->selectionChanged(options & FireSelectEvent);
329 if (!didMutateSelection)
332 setCaretRectNeedsUpdate();
334 if (!newSelection.isNone() && !(options & DoNotSetFocus))
335 setFocusedElementIfNeeded();
337 // Always clear the x position used for vertical arrow navigation.
338 // It will be restored by the vertical arrow navigation code if necessary.
339 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
340 selectFrameElementInParentIfFullySelected();
341 m_frame->editor().respondToChangedSelection(oldSelection, options);
342 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
347 void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, AXTextStateChangeIntent intent, CursorAlignOnScroll align, TextGranularity granularity)
349 RefPtr<Frame> protectedFrame(m_frame);
350 if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity))
353 Document* document = m_frame->document();
357 m_shouldRevealSelection = options & RevealSelection;
358 m_alwaysAlignCursorOnScrollWhenRevealingSelection = align == AlignCursorOnScrollAlways;
360 m_pendingSelectionUpdate = true;
362 if (document->hasPendingStyleRecalc())
365 FrameView* frameView = document->view();
366 if (frameView && frameView->layoutPending())
369 updateAndRevealSelection(intent);
371 if (options & IsUserTriggered) {
372 if (auto* client = m_frame->editor().client())
373 client->didEndUserTriggeredSelectionChanges();
377 static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame)
379 #if ENABLE(TEXT_CARET)
380 frame.document()->updateLayoutIgnorePendingStylesheets();
382 frame.document()->updateStyleIfNeeded();
386 void FrameSelection::setNeedsSelectionUpdate()
388 m_pendingSelectionUpdate = true;
389 if (RenderView* view = m_frame->contentRenderer())
390 view->clearSelection();
393 void FrameSelection::updateAndRevealSelection(const AXTextStateChangeIntent& intent)
395 if (!m_pendingSelectionUpdate)
398 m_pendingSelectionUpdate = false;
402 if (m_shouldRevealSelection) {
403 ScrollAlignment alignment;
405 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
406 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
408 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
410 revealSelection(SelectionRevealMode::Reveal, alignment, RevealExtent);
413 notifyAccessibilityForSelectionChange(intent);
416 void FrameSelection::updateDataDetectorsForSelection()
418 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
419 m_frame->editor().scanSelectionForTelephoneNumbers();
423 static bool removingNodeRemovesPosition(Node& node, const Position& position)
425 if (!position.anchorNode())
428 if (position.anchorNode() == &node)
431 if (!is<Element>(node))
434 return downcast<Element>(node).containsIncludingShadowDOM(position.anchorNode());
437 void DragCaretController::nodeWillBeRemoved(Node& node)
439 if (!hasCaret() || !node.isConnected())
442 if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
445 if (RenderView* view = node.document().renderView())
446 view->clearSelection();
451 void FrameSelection::nodeWillBeRemoved(Node& node)
453 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
454 // the selection in the document that created the fragment needs no adjustment.
455 if (isNone() || !node.isConnected())
458 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
459 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
462 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
464 bool clearRenderTreeSelection = false;
465 bool clearDOMTreeSelection = false;
467 if (startRemoved || endRemoved) {
468 Position start = m_selection.start();
469 Position end = m_selection.end();
471 updatePositionForNodeRemoval(start, node);
473 updatePositionForNodeRemoval(end, node);
475 if (start.isNotNull() && end.isNotNull()) {
476 if (m_selection.isBaseFirst())
477 m_selection.setWithoutValidation(start, end);
479 m_selection.setWithoutValidation(end, start);
481 clearDOMTreeSelection = true;
483 clearRenderTreeSelection = true;
484 } else if (baseRemoved || extentRemoved) {
485 // The base and/or extent are about to be removed, but the start and end aren't.
486 // Change the base and extent to the start and end, but don't re-validate the
487 // selection, since doing so could move the start and end into the node
488 // that is about to be removed.
489 if (m_selection.isBaseFirst())
490 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
492 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
493 } else if (RefPtr<Range> range = m_selection.firstRange()) {
494 auto compareNodeResult = range->compareNode(node);
495 if (!compareNodeResult.hasException()) {
496 auto compareResult = compareNodeResult.releaseReturnValue();
497 if (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE) {
498 // If we did nothing here, when this node's renderer was destroyed, the rect that it
499 // occupied would be invalidated, but, selection gaps that change as a result of
500 // the removal wouldn't be invalidated.
501 // FIXME: Don't do so much unnecessary invalidation.
502 clearRenderTreeSelection = true;
507 if (clearRenderTreeSelection) {
508 if (auto* renderView = node.document().renderView()) {
509 renderView->clearSelection();
511 // Trigger a selection update so the selection will be set again.
512 m_pendingSelectionUpdate = true;
513 renderView->setNeedsLayout();
517 if (clearDOMTreeSelection)
518 setSelection(VisibleSelection(), DoNotSetFocus);
521 static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
523 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
526 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
527 ASSERT(position.offsetInContainerNode() >= 0);
528 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
529 // Replacing text can be viewed as a deletion followed by insertion.
530 if (positionOffset >= offset && positionOffset <= offset + oldLength)
531 position.moveToOffset(offset);
533 // Adjust the offset if the position is after the end of the deleted contents
534 // (positionOffset > offset + oldLength) to avoid having a stale offset.
535 if (positionOffset > offset + oldLength)
536 position.moveToOffset(positionOffset - oldLength + newLength);
538 ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length());
541 void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
543 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
544 if (isNone() || !node || !node->isConnected())
547 Position base = m_selection.base();
548 Position extent = m_selection.extent();
549 Position start = m_selection.start();
550 Position end = m_selection.end();
551 updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength);
552 updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength);
553 updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength);
554 updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength);
556 if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) {
557 VisibleSelection newSelection;
559 newSelection.setWithoutValidation(base, extent);
560 else if (m_selection.isDirectional() && !m_selection.isBaseFirst())
561 newSelection.setWithoutValidation(end, start);
563 newSelection.setWithoutValidation(start, end);
565 setSelection(newSelection, DoNotSetFocus);
569 TextDirection FrameSelection::directionOfEnclosingBlock()
571 return WebCore::directionOfEnclosingBlock(m_selection.extent());
574 TextDirection FrameSelection::directionOfSelection()
576 InlineBox* startBox = nullptr;
577 InlineBox* endBox = nullptr;
579 // Cache the VisiblePositions because visibleStart() and visibleEnd()
580 // can cause layout, which has the potential to invalidate lineboxes.
581 VisiblePosition startPosition = m_selection.visibleStart();
582 VisiblePosition endPosition = m_selection.visibleEnd();
583 if (startPosition.isNotNull())
584 startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
585 if (endPosition.isNotNull())
586 endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
587 if (startBox && endBox && startBox->direction() == endBox->direction())
588 return startBox->direction();
590 return directionOfEnclosingBlock();
593 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
595 if (alter != AlterationExtend)
598 Position start = m_selection.start();
599 Position end = m_selection.end();
601 bool baseIsStart = true;
603 if (m_selection.isDirectional()) {
604 // Make base and extent match start and end so we extend the user-visible selection.
605 // This only matters for cases where base and extend point to different positions than
606 // start and end (e.g. after a double-click to select a word).
607 if (m_selection.isBaseFirst())
614 if (directionOfSelection() == LTR)
619 case DirectionForward:
623 if (directionOfSelection() == LTR)
628 case DirectionBackward:
634 m_selection.setBase(start);
635 m_selection.setExtent(end);
637 m_selection.setBase(end);
638 m_selection.setExtent(start);
642 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
644 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
645 // base/extent always point to the same nodes as start/end, but which points
646 // to which depends on the value of isBaseFirst. Then this can be changed
647 // to just return m_sel.extent().
648 if (m_frame && m_frame->editor().behavior().shouldAlwaysExtendSelectionFromExtentEndpoint())
649 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
651 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
654 VisiblePosition FrameSelection::startForPlatform() const
656 return positionForPlatform(true);
659 VisiblePosition FrameSelection::endForPlatform() const
661 return positionForPlatform(false);
664 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
666 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
668 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
669 // In order to skip spaces when moving right, we advance one
670 // word further and then move one word back. Given the
671 // semantics of previousWordPosition() this will put us at the
672 // beginning of the word following.
673 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
674 if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
675 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
677 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
678 if (movingBackwardsMovedPositionToStartOfCurrentWord)
679 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
681 return positionAfterCurrentWord;
684 #if ENABLE(USERSELECT_ALL)
685 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
687 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
688 pos = isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary);
692 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
694 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
696 // The difference between modifyExtendingRight and modifyExtendingForward is:
697 // modifyExtendingForward always extends forward logically.
698 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
699 // it extends forward logically if the enclosing block is LTR direction,
700 // but it extends backward logically if the enclosing block is RTL direction.
701 switch (granularity) {
702 case CharacterGranularity:
703 if (directionOfEnclosingBlock() == LTR)
704 pos = pos.next(CannotCrossEditingBoundary);
706 pos = pos.previous(CannotCrossEditingBoundary);
708 case WordGranularity:
709 if (directionOfEnclosingBlock() == LTR)
710 pos = nextWordPositionForPlatform(pos);
712 pos = previousWordPosition(pos);
715 if (directionOfEnclosingBlock() == LTR)
716 pos = modifyExtendingForward(granularity);
718 pos = modifyExtendingBackward(granularity);
720 case SentenceGranularity:
721 case LineGranularity:
722 case ParagraphGranularity:
723 case SentenceBoundary:
724 case ParagraphBoundary:
725 case DocumentBoundary:
726 // FIXME: implement all of the above?
727 pos = modifyExtendingForward(granularity);
729 case DocumentGranularity:
730 ASSERT_NOT_REACHED();
733 #if ENABLE(USERSELECT_ALL)
734 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
739 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
741 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
742 switch (granularity) {
743 case CharacterGranularity:
744 pos = pos.next(CannotCrossEditingBoundary);
746 case WordGranularity:
747 pos = nextWordPositionForPlatform(pos);
749 case SentenceGranularity:
750 pos = nextSentencePosition(pos);
752 case LineGranularity:
753 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
755 case ParagraphGranularity:
756 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
758 case DocumentGranularity:
759 ASSERT_NOT_REACHED();
761 case SentenceBoundary:
762 pos = endOfSentence(endForPlatform());
765 pos = logicalEndOfLine(endForPlatform());
767 case ParagraphBoundary:
768 pos = endOfParagraph(endForPlatform());
770 case DocumentBoundary:
771 pos = endForPlatform();
772 if (isEditablePosition(pos.deepEquivalent()))
773 pos = endOfEditableContent(pos);
775 pos = endOfDocument(pos);
778 #if ENABLE(USERSELECT_ALL)
779 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
784 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity, bool* reachedBoundary)
787 *reachedBoundary = false;
789 switch (granularity) {
790 case CharacterGranularity:
792 if (directionOfSelection() == LTR)
793 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
795 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
797 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true, reachedBoundary);
799 case WordGranularity: {
800 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
801 VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
802 pos = rightWordPosition(currentPosition, skipsSpaceWhenMovingRight);
804 *reachedBoundary = pos == currentPosition;
807 case SentenceGranularity:
808 case LineGranularity:
809 case ParagraphGranularity:
810 case SentenceBoundary:
811 case ParagraphBoundary:
812 case DocumentBoundary:
813 // FIXME: Implement all of the above.
814 pos = modifyMovingForward(granularity, reachedBoundary);
817 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
819 case DocumentGranularity:
820 ASSERT_NOT_REACHED();
826 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity, bool* reachedBoundary)
829 *reachedBoundary = false;
830 VisiblePosition currentPosition;
831 switch (granularity) {
832 case WordGranularity:
833 case SentenceGranularity:
834 currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
836 case LineGranularity:
837 case ParagraphGranularity:
838 case SentenceBoundary:
839 case ParagraphBoundary:
840 case DocumentBoundary:
841 currentPosition = endForPlatform();
847 // FIXME: Stay in editable content for the less common granularities.
848 switch (granularity) {
849 case CharacterGranularity:
851 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
853 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary, reachedBoundary);
855 case WordGranularity:
856 pos = nextWordPositionForPlatform(currentPosition);
858 case SentenceGranularity:
859 pos = nextSentencePosition(currentPosition);
861 case LineGranularity: {
862 // down-arrowing from a range selection that ends at the start of a line needs
863 // to leave the selection at that line start (no need to call nextLinePosition!)
864 pos = currentPosition;
865 if (!isRange() || !isStartOfLine(pos))
866 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
869 case ParagraphGranularity:
870 pos = nextParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
872 case DocumentGranularity:
873 ASSERT_NOT_REACHED();
875 case SentenceBoundary:
876 pos = endOfSentence(currentPosition);
879 pos = logicalEndOfLine(endForPlatform(), reachedBoundary);
881 case ParagraphBoundary:
882 pos = endOfParagraph(currentPosition);
884 case DocumentBoundary:
885 pos = currentPosition;
886 if (isEditablePosition(pos.deepEquivalent()))
887 pos = endOfEditableContent(pos);
889 pos = endOfDocument(pos);
892 switch (granularity) {
893 case WordGranularity:
894 case SentenceGranularity:
895 case LineGranularity:
896 case ParagraphGranularity:
897 case SentenceBoundary:
898 case ParagraphBoundary:
899 case DocumentBoundary:
901 *reachedBoundary = pos == currentPosition;
909 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
911 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
913 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
914 // modifyExtendingBackward always extends backward logically.
915 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
916 // it extends backward logically if the enclosing block is LTR direction,
917 // but it extends forward logically if the enclosing block is RTL direction.
918 switch (granularity) {
919 case CharacterGranularity:
920 if (directionOfEnclosingBlock() == LTR)
921 pos = pos.previous(CannotCrossEditingBoundary);
923 pos = pos.next(CannotCrossEditingBoundary);
925 case WordGranularity:
926 if (directionOfEnclosingBlock() == LTR)
927 pos = previousWordPosition(pos);
929 pos = nextWordPositionForPlatform(pos);
932 if (directionOfEnclosingBlock() == LTR)
933 pos = modifyExtendingBackward(granularity);
935 pos = modifyExtendingForward(granularity);
937 case SentenceGranularity:
938 case LineGranularity:
939 case ParagraphGranularity:
940 case SentenceBoundary:
941 case ParagraphBoundary:
942 case DocumentBoundary:
943 pos = modifyExtendingBackward(granularity);
945 case DocumentGranularity:
946 ASSERT_NOT_REACHED();
949 #if ENABLE(USERSELECT_ALL)
950 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
955 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
957 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
959 // Extending a selection backward by word or character from just after a table selects
960 // the table. This "makes sense" from the user perspective, esp. when deleting.
961 // It was done here instead of in VisiblePosition because we want VPs to iterate
963 switch (granularity) {
964 case CharacterGranularity:
965 pos = pos.previous(CannotCrossEditingBoundary);
967 case WordGranularity:
968 pos = previousWordPosition(pos);
970 case SentenceGranularity:
971 pos = previousSentencePosition(pos);
973 case LineGranularity:
974 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
976 case ParagraphGranularity:
977 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
979 case SentenceBoundary:
980 pos = startOfSentence(startForPlatform());
983 pos = logicalStartOfLine(startForPlatform());
985 case ParagraphBoundary:
986 pos = startOfParagraph(startForPlatform());
988 case DocumentBoundary:
989 pos = startForPlatform();
990 if (isEditablePosition(pos.deepEquivalent()))
991 pos = startOfEditableContent(pos);
993 pos = startOfDocument(pos);
995 case DocumentGranularity:
996 ASSERT_NOT_REACHED();
999 #if ENABLE(USERSELECT_ALL)
1000 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
1005 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity, bool* reachedBoundary)
1007 if (reachedBoundary)
1008 *reachedBoundary = false;
1009 VisiblePosition pos;
1010 switch (granularity) {
1011 case CharacterGranularity:
1013 if (directionOfSelection() == LTR)
1014 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
1016 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
1018 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true, reachedBoundary);
1020 case WordGranularity: {
1021 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
1022 VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
1023 pos = leftWordPosition(currentPosition, skipsSpaceWhenMovingRight);
1024 if (reachedBoundary)
1025 *reachedBoundary = pos == currentPosition;
1028 case SentenceGranularity:
1029 case LineGranularity:
1030 case ParagraphGranularity:
1031 case SentenceBoundary:
1032 case ParagraphBoundary:
1033 case DocumentBoundary:
1034 // FIXME: Implement all of the above.
1035 pos = modifyMovingBackward(granularity, reachedBoundary);
1038 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
1040 case DocumentGranularity:
1041 ASSERT_NOT_REACHED();
1047 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity, bool* reachedBoundary)
1049 if (reachedBoundary)
1050 *reachedBoundary = false;
1051 VisiblePosition currentPosition;
1052 switch (granularity) {
1053 case WordGranularity:
1054 case SentenceGranularity:
1055 currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
1057 case LineGranularity:
1058 case ParagraphGranularity:
1059 case SentenceBoundary:
1060 case ParagraphBoundary:
1061 case DocumentBoundary:
1062 currentPosition = startForPlatform();
1067 VisiblePosition pos;
1068 switch (granularity) {
1069 case CharacterGranularity:
1071 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
1073 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary, reachedBoundary);
1075 case WordGranularity:
1076 pos = previousWordPosition(currentPosition);
1078 case SentenceGranularity:
1079 pos = previousSentencePosition(currentPosition);
1081 case LineGranularity:
1082 pos = previousLinePosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
1084 case ParagraphGranularity:
1085 pos = previousParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
1087 case SentenceBoundary:
1088 pos = startOfSentence(currentPosition);
1091 pos = logicalStartOfLine(startForPlatform(), reachedBoundary);
1093 case ParagraphBoundary:
1094 pos = startOfParagraph(currentPosition);
1096 case DocumentBoundary:
1097 pos = currentPosition;
1098 if (isEditablePosition(pos.deepEquivalent()))
1099 pos = startOfEditableContent(pos);
1101 pos = startOfDocument(pos);
1103 case DocumentGranularity:
1104 ASSERT_NOT_REACHED();
1107 switch (granularity) {
1108 case WordGranularity:
1109 case SentenceGranularity:
1110 case LineGranularity:
1111 case ParagraphGranularity:
1112 case SentenceBoundary:
1113 case ParagraphBoundary:
1114 case DocumentBoundary:
1115 if (reachedBoundary)
1116 *reachedBoundary = pos == currentPosition;
1124 static bool isBoundary(TextGranularity granularity)
1126 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
1129 AXTextStateChangeIntent FrameSelection::textSelectionIntent(EAlteration alter, SelectionDirection direction, TextGranularity granularity)
1131 AXTextStateChangeIntent intent = AXTextStateChangeIntent();
1133 if (alter == FrameSelection::AlterationMove) {
1134 intent.type = AXTextStateChangeTypeSelectionMove;
1135 flip = isRange() && directionOfSelection() == RTL;
1137 intent.type = AXTextStateChangeTypeSelectionExtend;
1138 switch (granularity) {
1139 case CharacterGranularity:
1140 intent.selection.granularity = AXTextSelectionGranularityCharacter;
1142 case WordGranularity:
1143 intent.selection.granularity = AXTextSelectionGranularityWord;
1145 case SentenceGranularity:
1146 case SentenceBoundary:
1147 intent.selection.granularity = AXTextSelectionGranularitySentence;
1149 case LineGranularity:
1151 intent.selection.granularity = AXTextSelectionGranularityLine;
1153 case ParagraphGranularity:
1154 case ParagraphBoundary:
1155 intent.selection.granularity = AXTextSelectionGranularityParagraph;
1157 case DocumentGranularity:
1158 case DocumentBoundary:
1159 intent.selection.granularity = AXTextSelectionGranularityDocument;
1162 bool boundary = false;
1163 switch (granularity) {
1164 case CharacterGranularity:
1165 case WordGranularity:
1166 case SentenceGranularity:
1167 case LineGranularity:
1168 case ParagraphGranularity:
1169 case DocumentGranularity:
1171 case SentenceBoundary:
1173 case ParagraphBoundary:
1174 case DocumentBoundary:
1178 switch (direction) {
1179 case DirectionRight:
1180 case DirectionForward:
1182 intent.selection.direction = flip ? AXTextSelectionDirectionBeginning : AXTextSelectionDirectionEnd;
1184 intent.selection.direction = flip ? AXTextSelectionDirectionPrevious : AXTextSelectionDirectionNext;
1187 case DirectionBackward:
1189 intent.selection.direction = flip ? AXTextSelectionDirectionEnd : AXTextSelectionDirectionBeginning;
1191 intent.selection.direction = flip ? AXTextSelectionDirectionNext : AXTextSelectionDirectionPrevious;
1197 static AXTextSelection textSelectionWithDirectionAndGranularity(SelectionDirection direction, TextGranularity granularity)
1199 // FIXME: Account for BIDI in DirectionRight & DirectionLeft. (In a RTL block, Right would map to Previous/Beginning and Left to Next/End.)
1200 AXTextSelectionDirection intentDirection = AXTextSelectionDirectionUnknown;
1201 switch (direction) {
1202 case DirectionForward:
1203 intentDirection = AXTextSelectionDirectionNext;
1205 case DirectionRight:
1206 intentDirection = AXTextSelectionDirectionNext;
1208 case DirectionBackward:
1209 intentDirection = AXTextSelectionDirectionPrevious;
1212 intentDirection = AXTextSelectionDirectionPrevious;
1215 AXTextSelectionGranularity intentGranularity = AXTextSelectionGranularityUnknown;
1216 switch (granularity) {
1217 case CharacterGranularity:
1218 intentGranularity = AXTextSelectionGranularityCharacter;
1220 case WordGranularity:
1221 intentGranularity = AXTextSelectionGranularityWord;
1223 case SentenceGranularity:
1224 case SentenceBoundary: // FIXME: Boundary should affect direction.
1225 intentGranularity = AXTextSelectionGranularitySentence;
1227 case LineGranularity:
1228 intentGranularity = AXTextSelectionGranularityLine;
1230 case ParagraphGranularity:
1231 case ParagraphBoundary: // FIXME: Boundary should affect direction.
1232 intentGranularity = AXTextSelectionGranularityParagraph;
1234 case DocumentGranularity:
1235 case DocumentBoundary: // FIXME: Boundary should affect direction.
1236 intentGranularity = AXTextSelectionGranularityDocument;
1239 intentGranularity = AXTextSelectionGranularityLine;
1240 switch (direction) {
1241 case DirectionForward:
1242 intentDirection = AXTextSelectionDirectionEnd;
1244 case DirectionRight:
1245 intentDirection = AXTextSelectionDirectionEnd;
1247 case DirectionBackward:
1248 intentDirection = AXTextSelectionDirectionBeginning;
1251 intentDirection = AXTextSelectionDirectionBeginning;
1256 return { intentDirection, intentGranularity, false };
1259 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
1261 if (userTriggered == UserTriggered) {
1262 FrameSelection trialFrameSelection;
1263 trialFrameSelection.setSelection(m_selection);
1264 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
1266 bool change = shouldChangeSelection(trialFrameSelection.selection());
1270 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
1274 willBeModified(alter, direction);
1276 bool reachedBoundary = false;
1277 bool wasRange = m_selection.isRange();
1278 Position originalStartPosition = m_selection.start();
1279 VisiblePosition position;
1280 switch (direction) {
1281 case DirectionRight:
1282 if (alter == AlterationMove)
1283 position = modifyMovingRight(granularity, &reachedBoundary);
1285 position = modifyExtendingRight(granularity);
1287 case DirectionForward:
1288 if (alter == AlterationExtend)
1289 position = modifyExtendingForward(granularity);
1291 position = modifyMovingForward(granularity, &reachedBoundary);
1294 if (alter == AlterationMove)
1295 position = modifyMovingLeft(granularity, &reachedBoundary);
1297 position = modifyExtendingLeft(granularity);
1299 case DirectionBackward:
1300 if (alter == AlterationExtend)
1301 position = modifyExtendingBackward(granularity);
1303 position = modifyMovingBackward(granularity, &reachedBoundary);
1307 if (reachedBoundary && !isRange() && userTriggered == UserTriggered && m_frame && AXObjectCache::accessibilityEnabled()) {
1308 notifyAccessibilityForSelectionChange({ AXTextStateChangeTypeSelectionBoundary, textSelectionWithDirectionAndGranularity(direction, granularity) });
1312 if (position.isNull())
1315 if (isSpatialNavigationEnabled(m_frame))
1316 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
1319 if (m_frame && AXObjectCache::accessibilityEnabled()) {
1320 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1321 cache->setTextSelectionIntent(textSelectionIntent(alter, direction, granularity));
1324 // Some of the above operations set an xPosForVerticalArrowNavigation.
1325 // Setting a selection will clear it, so save it to possibly restore later.
1326 // Note: the START position type is arbitrary because it is unused, it would be
1327 // the requested position type if there were no xPosForVerticalArrowNavigation set.
1328 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
1329 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1332 case AlterationMove:
1333 moveTo(position, userTriggered);
1335 case AlterationExtend:
1337 if (!m_selection.isCaret()
1338 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
1339 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1340 // Don't let the selection go across the base position directly. Needed to match mac
1341 // behavior when, for instance, word-selecting backwards starting with the caret in
1342 // the middle of a word and then word-selecting forward, leaving the caret in the
1343 // same place where it was, instead of directly selecting to the end of the word.
1344 VisibleSelection newSelection = m_selection;
1345 newSelection.setExtent(position);
1346 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1347 position = m_selection.base();
1350 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1351 // base in place and moving the extent. Matches NSTextView.
1352 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1353 setExtent(position, userTriggered);
1355 TextDirection textDirection = directionOfEnclosingBlock();
1356 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1357 setEnd(position, userTriggered);
1359 setStart(position, userTriggered);
1364 if (granularity == LineGranularity || granularity == ParagraphGranularity)
1365 m_xPosForVerticalArrowNavigation = x;
1367 if (userTriggered == UserTriggered)
1368 m_granularity = CharacterGranularity;
1370 setCaretRectNeedsUpdate();
1375 // FIXME: Maybe baseline would be better?
1376 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1378 IntRect rect = c.absoluteCaretBounds();
1381 y = rect.y() + rect.height() / 2;
1385 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1387 if (!verticalDistance)
1390 if (userTriggered == UserTriggered) {
1391 FrameSelection trialFrameSelection;
1392 trialFrameSelection.setSelection(m_selection);
1393 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1395 bool change = shouldChangeSelection(trialFrameSelection.selection());
1400 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1402 VisiblePosition pos;
1403 LayoutUnit xPos = 0;
1405 case AlterationMove:
1406 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1407 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1408 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1410 case AlterationExtend:
1411 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1412 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1413 m_selection.setAffinity(DOWNSTREAM);
1418 if (!absoluteCaretY(pos, startY))
1420 if (direction == DirectionUp)
1424 VisiblePosition result;
1425 VisiblePosition next;
1426 for (VisiblePosition p = pos; ; p = next) {
1427 if (direction == DirectionUp)
1428 next = previousLinePosition(p, xPos);
1430 next = nextLinePosition(p, xPos);
1432 if (next.isNull() || next == p)
1435 if (!absoluteCaretY(next, nextY))
1437 if (direction == DirectionUp)
1439 if (nextY - startY > static_cast<int>(verticalDistance))
1441 if (nextY >= lastY) {
1447 if (result.isNull())
1451 case AlterationMove:
1452 moveTo(result, userTriggered, align);
1454 case AlterationExtend:
1455 setExtent(result, userTriggered);
1459 if (userTriggered == UserTriggered)
1460 m_granularity = CharacterGranularity;
1462 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1467 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1477 pos = m_selection.start();
1480 pos = m_selection.end();
1483 pos = m_selection.base();
1486 pos = m_selection.extent();
1490 Frame* frame = pos.anchorNode()->document().frame();
1494 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1495 VisiblePosition visiblePosition(pos, m_selection.affinity());
1496 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1497 // after the selection is created and before this function is called.
1498 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1499 m_xPosForVerticalArrowNavigation = x;
1501 x = m_xPosForVerticalArrowNavigation;
1506 void FrameSelection::clear()
1508 m_granularity = CharacterGranularity;
1509 setSelection(VisibleSelection());
1512 void FrameSelection::prepareForDestruction()
1514 m_granularity = CharacterGranularity;
1516 #if ENABLE(TEXT_CARET)
1517 m_caretBlinkTimer.stop();
1520 RenderView* view = m_frame->contentRenderer();
1522 view->clearSelection();
1524 setSelectionWithoutUpdatingAppearance(VisibleSelection(), defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, CharacterGranularity);
1525 m_previousCaretNode = nullptr;
1528 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1530 if (m_selection.isBaseFirst())
1531 setBase(pos, trigger);
1533 setExtent(pos, trigger);
1536 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1538 if (m_selection.isBaseFirst())
1539 setExtent(pos, trigger);
1541 setBase(pos, trigger);
1544 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1546 const bool selectionHasDirection = true;
1547 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1550 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1552 const bool selectionHasDirection = true;
1553 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1556 void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1558 const bool selectionHasDirection = true;
1559 setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1562 void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1564 const bool selectionHasDirection = true;
1565 setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1568 void CaretBase::clearCaretRect()
1570 m_caretLocalRect = LayoutRect();
1573 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
1575 document->updateLayoutIgnorePendingStylesheets();
1576 m_caretRectNeedsUpdate = false;
1577 RenderBlock* renderer;
1578 m_caretLocalRect = localCaretRectInRendererForCaretPainting(caretPosition, renderer);
1579 return !m_caretLocalRect.isEmpty();
1582 RenderBlock* FrameSelection::caretRendererWithoutUpdatingLayout() const
1584 return rendererForCaretPainting(m_selection.start().deprecatedNode());
1587 RenderBlock* DragCaretController::caretRenderer() const
1589 return rendererForCaretPainting(m_position.deepEquivalent().deprecatedNode());
1592 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1594 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1597 IntRect FrameSelection::absoluteCaretBounds(bool* insideFixed)
1601 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
1602 recomputeCaretRect();
1604 *insideFixed = m_caretInsidePositionFixed;
1605 return m_absCaretBounds;
1608 static void repaintCaretForLocalRect(Node* node, const LayoutRect& rect)
1610 if (auto* caretPainter = rendererForCaretPainting(node))
1611 caretPainter->repaintRectangle(rect);
1614 bool FrameSelection::recomputeCaretRect()
1616 if (!shouldUpdateCaretRect())
1622 FrameView* v = m_frame->document()->view();
1626 LayoutRect oldRect = localCaretRectWithoutUpdate();
1628 RefPtr<Node> caretNode = m_previousCaretNode;
1629 if (shouldUpdateCaretRect()) {
1630 if (!isNonOrphanedCaret(m_selection))
1633 VisiblePosition visibleStart = m_selection.visibleStart();
1634 if (updateCaretRect(m_frame->document(), visibleStart)) {
1635 caretNode = visibleStart.deepEquivalent().deprecatedNode();
1636 m_absCaretBoundsDirty = true;
1640 LayoutRect newRect = localCaretRectWithoutUpdate();
1642 if (caretNode == m_previousCaretNode && oldRect == newRect && !m_absCaretBoundsDirty)
1645 IntRect oldAbsCaretBounds = m_absCaretBounds;
1647 m_absCaretBounds = absoluteBoundsForLocalCaretRect(rendererForCaretPainting(caretNode.get()), newRect, &isInsideFixed);
1648 m_caretInsidePositionFixed = isInsideFixed;
1650 if (m_absCaretBoundsDirty && m_selection.isCaret()) // We should be able to always assert this condition.
1651 ASSERT(m_absCaretBounds == m_selection.visibleStart().absoluteCaretBounds());
1653 m_absCaretBoundsDirty = false;
1655 if (caretNode == m_previousCaretNode && oldAbsCaretBounds == m_absCaretBounds)
1658 #if ENABLE(TEXT_CARET)
1659 if (RenderView* view = m_frame->document()->renderView()) {
1660 bool previousOrNewCaretNodeIsContentEditable = m_selection.isContentEditable() || (m_previousCaretNode && m_previousCaretNode->isContentEditable());
1661 if (shouldRepaintCaret(view, previousOrNewCaretNodeIsContentEditable)) {
1662 if (m_previousCaretNode)
1663 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect);
1664 m_previousCaretNode = caretNode;
1665 repaintCaretForLocalRect(caretNode.get(), newRect);
1672 bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
1675 Frame* frame = &view->frameView().frame(); // The frame where the selection started.
1676 bool caretBrowsing = frame && frame->settings().caretBrowsingEnabled();
1677 return (caretBrowsing || isContentEditable);
1680 void FrameSelection::invalidateCaretRect()
1685 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1688 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
1690 // EDIT FIXME: This is an unfortunate hack.
1691 // Basically, we can't trust this layout position since we
1692 // can't guarantee that the check to see if we are in unrendered
1693 // content will work at this point. We may have to wait for
1694 // a layout and re-render of the document to happen. So, resetting this
1695 // flag will cause another caret layout to happen the first time
1696 // that we try to paint the caret after this call. That one will work since
1697 // it happens after the document has accounted for any editing
1698 // changes which may have been done.
1699 // And, we need to leave this layout here so the caret moves right
1700 // away after clicking.
1701 m_caretRectNeedsUpdate = true;
1703 if (caretRectChanged)
1706 if (RenderView* view = node->document().renderView()) {
1707 if (shouldRepaintCaret(view, isEditableNode(*node)))
1708 repaintCaretForLocalRect(node, localCaretRectWithoutUpdate());
1712 void FrameSelection::paintCaret(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1714 if (m_selection.isCaret() && m_caretPaint)
1715 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1718 void CaretBase::paintCaret(Node* node, GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1720 #if ENABLE(TEXT_CARET)
1721 if (m_caretVisibility == Hidden)
1724 LayoutRect drawingRect = localCaretRectWithoutUpdate();
1725 if (auto* renderer = rendererForCaretPainting(node))
1726 renderer->flipForWritingMode(drawingRect);
1727 drawingRect.moveBy(roundedIntPoint(paintOffset));
1728 LayoutRect caret = intersection(drawingRect, clipRect);
1729 if (caret.isEmpty())
1732 Color caretColor = Color::black;
1733 Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
1734 if (element && element->renderer()) {
1735 auto computeCaretColor = [] (const RenderStyle& elementStyle, const RenderStyle* rootEditableStyle) {
1736 // CSS value "auto" is treated as an invalid color.
1737 if (!elementStyle.caretColor().isValid() && rootEditableStyle) {
1738 auto rootEditableBackgroundColor = rootEditableStyle->visitedDependentColor(CSSPropertyBackgroundColor);
1739 auto elementBackgroundColor = elementStyle.visitedDependentColor(CSSPropertyBackgroundColor);
1740 auto disappearsIntoBackground = rootEditableBackgroundColor.blend(elementBackgroundColor) == rootEditableBackgroundColor;
1741 if (disappearsIntoBackground)
1742 return rootEditableStyle->visitedDependentColor(CSSPropertyCaretColor);
1744 return elementStyle.visitedDependentColor(CSSPropertyCaretColor);
1746 auto* rootEditableElement = node->rootEditableElement();
1747 auto* rootEditableStyle = rootEditableElement && rootEditableElement->renderer() ? &rootEditableElement->renderer()->style() : nullptr;
1748 caretColor = computeCaretColor(element->renderer()->style(), rootEditableStyle);
1751 context.fillRect(caret, caretColor);
1754 UNUSED_PARAM(context);
1755 UNUSED_PARAM(paintOffset);
1756 UNUSED_PARAM(clipRect);
1760 void FrameSelection::debugRenderer(RenderObject* renderer, bool selected) const
1762 if (is<Element>(*renderer->node())) {
1763 Element& element = downcast<Element>(*renderer->node());
1764 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element.localName().string().utf8().data());
1765 } else if (is<RenderText>(*renderer)) {
1766 RenderText& textRenderer = downcast<RenderText>(*renderer);
1767 if (!textRenderer.textLength() || !textRenderer.firstTextBox()) {
1768 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1772 static const int max = 36;
1773 String text = textRenderer.text();
1774 int textLength = text.length();
1777 if (renderer->node() == m_selection.start().containerNode())
1778 offset = m_selection.start().computeOffsetInContainerNode();
1779 else if (renderer->node() == m_selection.end().containerNode())
1780 offset = m_selection.end().computeOffsetInContainerNode();
1783 InlineTextBox* box = textRenderer.findNextInlineTextBox(offset, pos);
1784 text = text.substring(box->start(), box->len());
1790 // text is shorter than max
1791 if (textLength < max) {
1794 } else if (pos - mid < 0) {
1795 // too few characters to left
1796 show = text.left(max - 3) + "...";
1798 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1799 // enough characters on each side
1800 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1803 // too few characters on right
1804 show = "..." + text.right(max - 3);
1805 caret = pos - (textLength - show.length());
1808 show.replace('\n', ' ');
1809 show.replace('\r', ' ');
1810 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1811 fprintf(stderr, " ");
1812 for (int i = 0; i < caret; i++)
1813 fprintf(stderr, " ");
1814 fprintf(stderr, "^\n");
1816 if ((int)text.length() > max)
1817 text = text.left(max - 3) + "...";
1819 text = text.left(max);
1820 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1825 bool FrameSelection::contains(const LayoutPoint& point) const
1827 // Treat a collapsed selection like no selection.
1831 RenderView* renderView = m_frame->contentRenderer();
1835 HitTestResult result(point);
1836 renderView->hitTest(HitTestRequest(), result);
1837 Node* innerNode = result.innerNode();
1838 if (!innerNode || !innerNode->renderer())
1841 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint(), nullptr));
1842 if (visiblePos.isNull())
1845 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1848 Position start(m_selection.visibleStart().deepEquivalent());
1849 Position end(m_selection.visibleEnd().deepEquivalent());
1850 Position p(visiblePos.deepEquivalent());
1852 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1855 // Workaround for the fact that it's hard to delete a frame.
1856 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1857 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1858 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1859 // mouse or the keyboard after setting the selection.
1860 void FrameSelection::selectFrameElementInParentIfFullySelected()
1862 // Find the parent frame; if there is none, then we have nothing to do.
1863 Frame* parent = m_frame->tree().parent();
1866 Page* page = m_frame->page();
1870 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1873 if (!isStartOfDocument(selection().visibleStart()))
1875 if (!isEndOfDocument(selection().visibleEnd()))
1878 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1879 Element* ownerElement = m_frame->ownerElement();
1882 ContainerNode* ownerElementParent = ownerElement->parentNode();
1883 if (!ownerElementParent)
1886 // 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.
1887 if (!ownerElementParent->hasEditableStyle())
1890 // Create compute positions before and after the element.
1891 unsigned ownerElementNodeIndex = ownerElement->computeNodeIndex();
1892 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1893 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1895 // Focus on the parent frame, and then select from before this element to after.
1896 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1897 if (parent->selection().shouldChangeSelection(newSelection)) {
1898 page->focusController().setFocusedFrame(parent);
1899 parent->selection().setSelection(newSelection);
1903 void FrameSelection::selectAll()
1905 Document* document = m_frame->document();
1907 Element* focusedElement = document->focusedElement();
1908 if (is<HTMLSelectElement>(focusedElement)) {
1909 HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*focusedElement);
1910 if (selectElement.canSelectAll()) {
1911 selectElement.selectAll();
1917 Node* selectStartTarget = nullptr;
1918 if (m_selection.isContentEditable()) {
1919 root = highestEditableRoot(m_selection.start());
1920 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1921 selectStartTarget = shadowRoot->shadowHost();
1923 selectStartTarget = root.get();
1925 if (m_selection.isNone() && focusedElement) {
1926 if (is<HTMLTextFormControlElement>(*focusedElement)) {
1927 downcast<HTMLTextFormControlElement>(*focusedElement).select();
1930 root = focusedElement->nonBoundaryShadowTreeRootNode();
1932 root = m_selection.nonBoundaryShadowTreeRootNode();
1935 selectStartTarget = root->shadowHost();
1937 root = document->documentElement();
1938 selectStartTarget = document->bodyOrFrameset();
1944 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
1947 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1949 if (shouldChangeSelection(newSelection)) {
1950 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionExtend, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityAll, false });
1951 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent);
1955 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping, EUserTriggered userTriggered)
1959 ASSERT(&range->startContainer().document() == &range->endContainer().document());
1961 VisibleSelection newSelection(*range, affinity);
1964 // FIXME: Why do we need this check only in iOS?
1965 if (newSelection.isNone())
1969 if (userTriggered == UserTriggered) {
1970 FrameSelection trialFrameSelection;
1971 trialFrameSelection.setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0));
1973 if (!shouldChangeSelection(trialFrameSelection.selection()))
1977 setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0) | (userTriggered == UserTriggered ? IsUserTriggered : 0));
1981 void FrameSelection::focusedOrActiveStateChanged()
1983 bool activeAndFocused = isFocusedAndActive();
1984 Ref<Document> document(*m_frame->document());
1986 document->updateStyleIfNeeded();
1988 #if USE(UIKIT_EDITING)
1989 // Caret blinking (blinks | does not blink)
1990 if (activeAndFocused)
1991 setSelectionFromNone();
1992 setCaretVisible(activeAndFocused);
1994 // Because RenderObject::selectionBackgroundColor() and
1995 // RenderObject::selectionForegroundColor() check if the frame is active,
1996 // we have to update places those colors were painted.
1997 if (RenderView* view = document->renderView())
1998 view->repaintSelection();
2000 // Caret appears in the active frame.
2001 if (activeAndFocused)
2002 setSelectionFromNone();
2003 setCaretVisibility(activeAndFocused ? Visible : Hidden);
2005 // Because StyleResolver::checkOneSelector() and
2006 // RenderTheme::isFocused() check if the frame is active, we have to
2007 // update style and theme state that depended on those.
2008 if (Element* element = document->focusedElement()) {
2009 element->invalidateStyleForSubtree();
2010 if (RenderObject* renderer = element->renderer())
2011 if (renderer && renderer->style().hasAppearance())
2012 renderer->theme().stateChanged(*renderer, ControlStates::FocusState);
2017 void FrameSelection::pageActivationChanged()
2019 focusedOrActiveStateChanged();
2022 void FrameSelection::setFocused(bool flag)
2024 if (m_focused == flag)
2028 focusedOrActiveStateChanged();
2031 bool FrameSelection::isFocusedAndActive() const
2033 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
2036 #if ENABLE(TEXT_CARET)
2037 inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame)
2039 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
2043 void FrameSelection::updateAppearance()
2046 if (!m_updateAppearanceEnabled)
2050 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
2051 // the FrameSelection will paint a blinking caret as usual).
2052 VisibleSelection oldSelection = selection();
2054 #if ENABLE(TEXT_CARET)
2055 bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
2056 bool caretRectChangedOrCleared = recomputeCaretRect();
2058 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2059 bool shouldBlink = !paintBlockCursor && caretIsVisible() && isCaret() && (oldSelection.isContentEditable() || caretBrowsing);
2061 // If the caret moved, stop the blink timer so we can restart with a
2062 // black caret in the new location.
2063 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame))
2064 m_caretBlinkTimer.stop();
2066 // Start blinking with a black caret. Be sure not to restart if we're
2067 // already blinking in the right location.
2068 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
2069 if (Seconds blinkInterval = RenderTheme::singleton().caretBlinkInterval())
2070 m_caretBlinkTimer.startRepeating(blinkInterval);
2072 if (!m_caretPaint) {
2073 m_caretPaint = true;
2074 invalidateCaretRect();
2079 RenderView* view = m_frame->contentRenderer();
2083 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
2084 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
2085 #if ENABLE(TEXT_CARET)
2086 VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : oldSelection.visibleEnd();
2087 VisibleSelection selection(oldSelection.visibleStart(), endVisiblePosition);
2089 VisibleSelection selection(oldSelection.visibleStart(), oldSelection.visibleEnd());
2092 if (!selection.isRange()) {
2093 view->clearSelection();
2097 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
2098 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
2099 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
2100 // and will fill the gap before 'bar'.
2101 Position startPos = selection.start();
2102 Position candidate = startPos.downstream();
2103 if (candidate.isCandidate())
2104 startPos = candidate;
2105 Position endPos = selection.end();
2106 candidate = endPos.upstream();
2107 if (candidate.isCandidate())
2110 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
2111 // because we don't yet notify the FrameSelection of text removal.
2112 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
2113 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
2114 int startOffset = startPos.deprecatedEditingOffset();
2115 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
2116 int endOffset = endPos.deprecatedEditingOffset();
2117 ASSERT(startOffset >= 0 && endOffset >= 0);
2118 view->setSelection(startRenderer, startOffset, endRenderer, endOffset);
2122 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
2124 if (caretVisibility() == visibility)
2127 // FIXME: We shouldn't trigger a synchronous layout here.
2129 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
2131 #if ENABLE(TEXT_CARET)
2133 m_caretPaint = false;
2134 invalidateCaretRect();
2136 CaretBase::setCaretVisibility(visibility);
2142 void FrameSelection::caretBlinkTimerFired()
2144 #if ENABLE(TEXT_CARET)
2145 ASSERT(caretIsVisible());
2147 bool caretPaint = m_caretPaint;
2148 if (isCaretBlinkingSuspended() && caretPaint)
2150 m_caretPaint = !caretPaint;
2151 invalidateCaretRect();
2155 // Helper function that tells whether a particular node is an element that has an entire
2156 // Frame and FrameView, a <frame>, <iframe>, or <object>.
2157 static bool isFrameElement(const Node* n)
2161 RenderObject* renderer = n->renderer();
2162 if (!is<RenderWidget>(renderer))
2164 Widget* widget = downcast<RenderWidget>(*renderer).widget();
2165 return widget && widget->isFrameView();
2168 void FrameSelection::setFocusedElementIfNeeded()
2170 if (isNone() || !isFocused())
2173 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2174 if (caretBrowsing) {
2175 if (Element* anchor = enclosingAnchorElement(m_selection.base())) {
2176 m_frame->page()->focusController().setFocusedElement(anchor, *m_frame);
2181 if (Element* target = m_selection.rootEditableElement()) {
2182 // Walk up the DOM tree to search for an element to focus.
2184 // We don't want to set focus on a subframe when selecting in a parent frame,
2185 // so add the !isFrameElement check here. There's probably a better way to make this
2186 // work in the long term, but this is the safest fix at this time.
2187 if (target->isMouseFocusable() && !isFrameElement(target)) {
2188 m_frame->page()->focusController().setFocusedElement(target, *m_frame);
2191 target = target->parentOrShadowHostElement();
2193 m_frame->document()->setFocusedElement(nullptr);
2197 m_frame->page()->focusController().setFocusedElement(nullptr, *m_frame);
2200 void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext& p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
2202 #if ENABLE(TEXT_CARET)
2203 if (m_position.deepEquivalent().deprecatedNode()->document().frame() == frame)
2204 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
2206 UNUSED_PARAM(frame);
2208 UNUSED_PARAM(paintOffset);
2209 UNUSED_PARAM(clipRect);
2213 RefPtr<MutableStyleProperties> FrameSelection::copyTypingStyle() const
2215 if (!m_typingStyle || !m_typingStyle->style())
2217 return m_typingStyle->style()->mutableCopy();
2220 bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
2223 if (m_frame->selectionChangeCallbacksDisabled())
2226 return m_frame->editor().client()->shouldDeleteRange(selection.toNormalizedRange().get());
2229 FloatRect FrameSelection::selectionBounds(bool clipToVisibleContent) const
2231 if (!m_frame->document())
2232 return LayoutRect();
2234 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
2235 RenderView* root = m_frame->contentRenderer();
2236 FrameView* view = m_frame->view();
2238 return LayoutRect();
2240 LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent);
2241 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect)) : selectionRect;
2244 void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
2246 RenderView* root = m_frame->contentRenderer();
2250 Vector<FloatRect> textRects;
2251 getTextRectangles(textRects, textRectHeight);
2253 FloatRect visibleContentRect = m_frame->view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
2255 for (const auto& rect : textRects) {
2256 FloatRect intersectionRect = intersection(rect, visibleContentRect);
2257 if (!intersectionRect.isEmpty())
2258 rectangles.append(intersectionRect);
2262 void FrameSelection::getTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
2264 RefPtr<Range> range = toNormalizedRange();
2268 Vector<FloatQuad> quads;
2269 range->absoluteTextQuads(quads, textRectHeight == TextRectangleHeight::SelectionHeight);
2271 for (const auto& quad : quads)
2272 rectangles.append(quad.boundingBox());
2275 // Scans logically forward from "start", including any child frames.
2276 static HTMLFormElement* scanForForm(Element* start)
2281 auto descendants = descendantsOfType<HTMLElement>(start->document());
2282 for (auto it = descendants.from(*start), end = descendants.end(); it != end; ++it) {
2283 HTMLElement& element = *it;
2284 if (is<HTMLFormElement>(element))
2285 return &downcast<HTMLFormElement>(element);
2286 if (is<HTMLFormControlElement>(element))
2287 return downcast<HTMLFormControlElement>(element).form();
2288 if (is<HTMLFrameElementBase>(element)) {
2289 Document* contentDocument = downcast<HTMLFrameElementBase>(element).contentDocument();
2290 if (!contentDocument)
2292 if (HTMLFormElement* frameResult = scanForForm(contentDocument->documentElement()))
2299 // We look for either the form containing the current focus, or for one immediately after it
2300 HTMLFormElement* FrameSelection::currentForm() const
2302 // Start looking either at the active (first responder) node, or where the selection is.
2303 Element* start = m_frame->document()->focusedElement();
2305 start = m_selection.start().element();
2309 if (auto form = lineageOfType<HTMLFormElement>(*start).first())
2311 if (auto formControl = lineageOfType<HTMLFormControlElement>(*start).first())
2312 return formControl->form();
2314 // Try walking forward in the node tree to find a form element.
2315 return scanForForm(start);
2318 void FrameSelection::revealSelection(SelectionRevealMode revealMode, const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
2320 if (revealMode == SelectionRevealMode::DoNotReveal)
2324 bool insideFixed = false;
2325 switch (m_selection.selectionType()) {
2326 case VisibleSelection::NoSelection:
2328 case VisibleSelection::CaretSelection:
2329 rect = absoluteCaretBounds(&insideFixed);
2331 case VisibleSelection::RangeSelection:
2332 rect = revealExtentOption == RevealExtent ? VisiblePosition(m_selection.extent()).absoluteCaretBounds() : enclosingIntRect(selectionBounds(false));
2336 Position start = m_selection.start();
2337 ASSERT(start.deprecatedNode());
2338 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
2340 if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
2341 if (!m_scrollingSuppressCount) {
2342 layer->setAdjustForIOSCaretWhenScrolling(true);
2343 layer->scrollRectToVisible(revealMode, rect, insideFixed, alignment, alignment);
2344 layer->setAdjustForIOSCaretWhenScrolling(false);
2346 if (m_frame->page())
2347 m_frame->page()->chrome().client().notifyRevealedSelectionByScrollingFrame(*m_frame);
2351 // FIXME: This code only handles scrolling the startContainer's layer, but
2352 // the selection rect could intersect more than just that.
2353 // See <rdar://problem/4799899>.
2354 if (start.deprecatedNode()->renderer()->scrollRectToVisible(revealMode, rect, insideFixed, alignment, alignment))
2360 void FrameSelection::setSelectionFromNone()
2362 // Put a caret inside the body if the entire frame is editable (either the
2363 // entire WebView is editable or designMode is on for this document).
2365 Document* document = m_frame->document();
2367 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2368 if (!isNone() || !(document->hasEditableStyle() || caretBrowsing))
2371 if (!document || !(isNone() || isStartOfDocument(VisiblePosition(m_selection.start(), m_selection.affinity()))) || !document->hasEditableStyle())
2375 if (auto* body = document->body())
2376 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
2379 bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
2382 if (m_frame->selectionChangeCallbacksDisabled())
2385 return m_frame->editor().shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
2388 bool FrameSelection::dispatchSelectStart()
2390 Node* selectStartTarget = m_selection.extent().containerNode();
2391 if (!selectStartTarget)
2394 return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
2397 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
2399 m_shouldShowBlockCursor = shouldShowBlockCursor;
2401 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2406 void FrameSelection::updateAppearanceAfterLayout()
2408 m_appearanceUpdateTimer.stop();
2409 updateAppearanceAfterLayoutOrStyleChange();
2412 void FrameSelection::scheduleAppearanceUpdateAfterStyleChange()
2414 m_appearanceUpdateTimer.startOneShot(0_s);
2417 void FrameSelection::appearanceUpdateTimerFired()
2419 updateAppearanceAfterLayoutOrStyleChange();
2422 void FrameSelection::updateAppearanceAfterLayoutOrStyleChange()
2424 if (auto* client = m_frame->editor().client())
2425 client->updateEditorStateAfterLayoutIfEditabilityChanged();
2427 setCaretRectNeedsUpdate();
2428 updateAndRevealSelection(AXTextStateChangeIntent());
2429 updateDataDetectorsForSelection();
2432 #if ENABLE(TREE_DEBUGGING)
2434 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
2436 m_selection.formatForDebugger(buffer, length);
2439 void FrameSelection::showTreeForThis() const
2441 m_selection.showTreeForThis();
2447 void FrameSelection::expandSelectionToElementContainingCaretSelection()
2449 RefPtr<Range> range = elementRangeContainingCaretSelection();
2452 VisibleSelection selection(*range, DOWNSTREAM);
2453 setSelection(selection);
2456 RefPtr<Range> FrameSelection::elementRangeContainingCaretSelection() const
2458 if (m_selection.isNone())
2461 VisibleSelection selection = m_selection;
2462 if (selection.isNone())
2465 VisiblePosition visiblePos(selection.start(), VP_DEFAULT_AFFINITY);
2466 if (visiblePos.isNull())
2469 Node* node = visiblePos.deepEquivalent().deprecatedNode();
2470 Element* element = deprecatedEnclosingBlockFlowElement(node);
2474 Position startPos = createLegacyEditingPosition(element, 0);
2475 Position endPos = createLegacyEditingPosition(element, element->countChildNodes());
2477 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY);
2478 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY);
2479 if (startVisiblePos.isNull() || endVisiblePos.isNull())
2482 selection.setBase(startVisiblePos);
2483 selection.setExtent(endVisiblePos);
2485 return selection.toNormalizedRange();
2488 void FrameSelection::expandSelectionToWordContainingCaretSelection()
2490 VisibleSelection selection(wordSelectionContainingCaretSelection(m_selection));
2491 if (selection.isCaretOrRange())
2492 setSelection(selection);
2495 RefPtr<Range> FrameSelection::wordRangeContainingCaretSelection()
2497 return wordSelectionContainingCaretSelection(m_selection).toNormalizedRange();
2500 void FrameSelection::expandSelectionToStartOfWordContainingCaretSelection()
2502 if (m_selection.isNone() || isStartOfDocument(m_selection.start()))
2505 VisiblePosition s1(m_selection.start());
2506 VisiblePosition e1(m_selection.end());
2508 VisibleSelection expanded(wordSelectionContainingCaretSelection(m_selection));
2509 VisiblePosition s2(expanded.start());
2511 // Don't allow the start to become greater after the expansion.
2512 if (s2.isNull() || s2 > s1)
2518 UChar FrameSelection::characterInRelationToCaretSelection(int amount) const
2520 if (m_selection.isNone())
2523 VisibleSelection selection = m_selection;
2524 ASSERT(selection.isCaretOrRange());
2526 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY);
2529 int count = abs(amount);
2530 for (int i = 0; i < count; i++)
2531 visiblePosition = visiblePosition.previous();
2532 return visiblePosition.characterBefore();
2534 for (int i = 0; i < amount; i++)
2535 visiblePosition = visiblePosition.next();
2536 return visiblePosition.characterAfter();
2539 UChar FrameSelection::characterBeforeCaretSelection() const
2541 if (m_selection.isNone())
2544 VisibleSelection selection = m_selection;
2545 ASSERT(selection.isCaretOrRange());
2547 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY);
2548 return visiblePosition.characterBefore();
2551 UChar FrameSelection::characterAfterCaretSelection() const
2553 if (m_selection.isNone())
2556 VisibleSelection selection = m_selection;
2557 ASSERT(selection.isCaretOrRange());
2559 VisiblePosition visiblePosition(selection.end(), VP_DEFAULT_AFFINITY);
2560 return visiblePosition.characterAfter();
2563 int FrameSelection::wordOffsetInRange(const Range *range) const
2568 VisibleSelection selection = m_selection;
2569 if (!selection.isCaret())
2572 // FIXME: This will only work in cases where the selection remains in
2573 // the same node after it is expanded. Improve to handle more complicated
2575 int result = selection.start().deprecatedEditingOffset() - range->startOffset();
2581 bool FrameSelection::spaceFollowsWordInRange(const Range *range) const
2585 Node& node = range->endContainer();
2586 int endOffset = range->endOffset();
2587 VisiblePosition pos(createLegacyEditingPosition(&node, endOffset), VP_DEFAULT_AFFINITY);
2588 return isSpaceOrNewline(pos.characterAfter());
2591 bool FrameSelection::selectionAtDocumentStart() const
2593 VisibleSelection selection = m_selection;
2594 if (selection.isNone())
2597 Position startPos(selection.start());
2598 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2602 return isStartOfDocument(pos);
2605 bool FrameSelection::selectionAtSentenceStart() const
2607 VisibleSelection selection = m_selection;
2608 if (selection.isNone())
2611 return actualSelectionAtSentenceStart(selection);
2614 bool FrameSelection::selectionAtWordStart() const
2616 VisibleSelection selection = m_selection;
2617 if (selection.isNone())
2620 Position startPos(selection.start());
2621 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2625 if (isStartOfParagraph(pos))
2629 unsigned previousCount = 0;
2630 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) {
2632 if (isStartOfParagraph(pos)) {
2633 if (previousCount == 1)
2637 UChar c(pos.characterAfter());
2639 result = isSpaceOrNewline(c) || c == 0xA0 || (u_ispunct(c) && c != ',' && c != '-' && c != '\'');
2647 RefPtr<Range> FrameSelection::rangeByMovingCurrentSelection(int amount) const
2649 return rangeByAlteringCurrentSelection(AlterationMove, amount);
2652 RefPtr<Range> FrameSelection::rangeByExtendingCurrentSelection(int amount) const
2654 return rangeByAlteringCurrentSelection(AlterationExtend, amount);
2657 void FrameSelection::selectRangeOnElement(unsigned location, unsigned length, Node& node)
2659 RefPtr<Range> resultRange = m_frame->document()->createRange();
2660 resultRange->setStart(node, location);
2661 resultRange->setEnd(node, location + length);
2662 VisibleSelection selection = VisibleSelection(*resultRange, SEL_DEFAULT_AFFINITY);
2663 setSelection(selection, true);
2666 VisibleSelection FrameSelection::wordSelectionContainingCaretSelection(const VisibleSelection& selection)
2668 if (selection.isNone())
2669 return VisibleSelection();
2671 ASSERT(selection.isCaretOrRange());
2672 FrameSelection frameSelection;
2673 frameSelection.setSelection(selection);
2675 Position startPosBeforeExpansion(selection.start());
2676 Position endPosBeforeExpansion(selection.end());
2677 VisiblePosition startVisiblePosBeforeExpansion(startPosBeforeExpansion, VP_DEFAULT_AFFINITY);
2678 VisiblePosition endVisiblePosBeforeExpansion(endPosBeforeExpansion, VP_DEFAULT_AFFINITY);
2679 if (endVisiblePosBeforeExpansion.isNull())
2680 return VisibleSelection();
2682 if (isEndOfParagraph(endVisiblePosBeforeExpansion)) {
2683 UChar c(endVisiblePosBeforeExpansion.characterBefore());
2684 if (isSpaceOrNewline(c) || c == 0xA0) {
2685 // End of paragraph with space.
2686 return VisibleSelection();
2690 // If at end of paragraph, move backwards one character.
2691 // This has the effect of selecting the word on the line (which is
2692 // what we want, rather than selecting past the end of the line).
2693 if (isEndOfParagraph(endVisiblePosBeforeExpansion) && !isStartOfParagraph(endVisiblePosBeforeExpansion))
2694 frameSelection.modify(FrameSelection::AlterationMove, DirectionBackward, CharacterGranularity);
2696 VisibleSelection newSelection = frameSelection.selection();
2697 newSelection.expandUsingGranularity(WordGranularity);
2698 frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AXTextStateChangeIntent(), AlignCursorOnScrollIfNeeded, frameSelection.granularity());
2700 Position startPos(frameSelection.selection().start());
2701 Position endPos(frameSelection.selection().end());
2703 // Expansion cannot be allowed to change selection so that it is no longer
2704 // touches (or contains) the original, unexpanded selection.
2705 // Enforce this on the way into these additional calculations to give them
2706 // the best chance to yield a suitable answer.
2707 if (startPos > startPosBeforeExpansion)
2708 startPos = startPosBeforeExpansion;
2709 if (endPos < endPosBeforeExpansion)
2710 endPos = endPosBeforeExpansion;
2712 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY);
2713 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY);
2715 if (startVisiblePos.isNull() || endVisiblePos.isNull()) {
2716 // Start or end is nil
2717 return VisibleSelection();
2720 if (isEndOfLine(endVisiblePosBeforeExpansion)) {
2721 VisiblePosition previous(endVisiblePos.previous());
2722 if (previous == endVisiblePos) {
2724 return VisibleSelection();
2726 UChar c(previous.characterAfter());
2727 if (isSpaceOrNewline(c) || c == 0xA0) {
2728 // Space at end of line
2729 return VisibleSelection();
2733 // Expansion has selected past end of line.
2734 // Try repositioning backwards.
2735 if (isEndOfLine(startVisiblePos) && isStartOfLine(endVisiblePos)) {
2736 VisiblePosition previous(startVisiblePos.previous());
2737 if (isEndOfLine(previous)) {
2739 return VisibleSelection();
2741 UChar c(previous.characterAfter());
2742 if (isSpaceOrNewline(c) || c == 0xA0) {
2743 // Space at end of line
2744 return VisibleSelection();
2746 frameSelection.moveTo(startVisiblePos);
2747 frameSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, WordGranularity);
2748 startPos = frameSelection.selection().start();
2749 endPos = frameSelection.selection().end();
2750 startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY);
2751 endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY);
2752 if (startVisiblePos.isNull() || endVisiblePos.isNull()) {
2753 // Start or end is nil
2754 return VisibleSelection();
2758 // Now loop backwards until we find a non-space.
2759 while (endVisiblePos != startVisiblePos) {
2760 VisiblePosition previous(endVisiblePos.previous());
2761 UChar c(previous.characterAfter());
2762 if (!isSpaceOrNewline(c) && c != 0xA0)
2764 endVisiblePos = previous;
2767 // Expansion cannot be allowed to change selection so that it is no longer
2768 // touches (or contains) the original, unexpanded selection.
2769 // Enforce this on the way out of the function to preserve the invariant.
2770 if (startVisiblePos > startVisiblePosBeforeExpansion)
2771 startVisiblePos = startVisiblePosBeforeExpansion;
2772 if (endVisiblePos < endVisiblePosBeforeExpansion)
2773 endVisiblePos = endVisiblePosBeforeExpansion;
2775 return VisibleSelection(startVisiblePos, endVisiblePos);
2778 bool FrameSelection::actualSelectionAtSentenceStart(const VisibleSelection& sel) const
2780 Position startPos(sel.start());
2781 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2785 if (isStartOfParagraph(pos))
2789 bool sawSpace = false;
2790 unsigned previousCount = 0;
2791 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) {
2793 if (isStartOfParagraph(pos)) {
2794 if (previousCount == 1 || (previousCount == 2 && sawSpace))
2798 UChar c(pos.characterAfter());
2800 if (isSpaceOrNewline(c) || c == 0xA0) {
2804 result = (c == '.' || c == '!' || c == '?');
2813 RefPtr<Range> FrameSelection::rangeByAlteringCurrentSelection(EAlteration alteration, int amount) const
2815 if (m_selection.isNone())
2819 return toNormalizedRange();
2821 FrameSelection frameSelection;
2822 frameSelection.setSelection(m_selection);
2823 SelectionDirection direction = amount > 0 ? DirectionForward : DirectionBackward;
2824 for (int i = 0; i < abs(amount); i++)
2825 frameSelection.modify(alteration, direction, CharacterGranularity);
2826 return frameSelection.toNormalizedRange();
2829 void FrameSelection::clearCurrentSelection()
2831 setSelection(VisibleSelection());
2834 void FrameSelection::setCaretBlinks(bool caretBlinks)
2836 if (m_caretBlinks == caretBlinks)
2838 #if ENABLE(TEXT_CARET)
2839 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2841 m_caretPaint = false;
2842 invalidateCaretRect();
2846 setFocusedElementIfNeeded();
2847 m_caretBlinks = caretBlinks;
2851 void FrameSelection::setCaretColor(const Color& caretColor)
2853 if (m_caretColor != caretColor) {
2854 m_caretColor = caretColor;
2855 if (caretIsVisible() && m_caretBlinks && isCaret())
2856 invalidateCaretRect();
2859 #endif // PLATFORM(IOS)
2863 #if ENABLE(TREE_DEBUGGING)
2865 void showTree(const WebCore::FrameSelection& sel)
2867 sel.showTreeForThis();
2870 void showTree(const WebCore::FrameSelection* sel)
2873 sel->showTreeForThis();