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"
34 #include "EditorClient.h"
36 #include "ElementIterator.h"
38 #include "EventHandler.h"
39 #include "EventNames.h"
40 #include "ExceptionCode.h"
41 #include "FloatQuad.h"
42 #include "FocusController.h"
44 #include "FrameTree.h"
45 #include "FrameView.h"
46 #include "GraphicsContext.h"
47 #include "HTMLBodyElement.h"
48 #include "HTMLFormElement.h"
49 #include "HTMLFrameElement.h"
50 #include "HTMLIFrameElement.h"
51 #include "HTMLInputElement.h"
52 #include "HTMLNames.h"
53 #include "HTMLSelectElement.h"
54 #include "HitTestRequest.h"
55 #include "HitTestResult.h"
56 #include "InlineTextBox.h"
58 #include "RenderText.h"
59 #include "RenderTextControl.h"
60 #include "RenderTheme.h"
61 #include "RenderView.h"
62 #include "RenderWidget.h"
63 #include "RenderedPosition.h"
65 #include "SpatialNavigation.h"
66 #include "StyleProperties.h"
67 #include "TypingCommand.h"
68 #include "VisibleUnits.h"
69 #include "htmlediting.h"
71 #include <wtf/text/CString.h>
75 #include "ChromeClient.h"
77 #include "RenderLayer.h"
78 #include "RenderObject.h"
79 #include "RenderStyle.h"
86 using namespace HTMLNames;
88 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
90 return LayoutUnit::min();
93 CaretBase::CaretBase(CaretVisibility visibility)
94 : m_caretRectNeedsUpdate(true)
95 , m_caretVisibility(visibility)
99 DragCaretController::DragCaretController()
104 bool DragCaretController::isContentRichlyEditable() const
106 return isRichlyEditablePosition(m_position.deepEquivalent());
109 static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame)
111 return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
114 FrameSelection::FrameSelection(Frame* frame)
116 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
117 , m_granularity(CharacterGranularity)
118 , m_caretBlinkTimer(*this, &FrameSelection::caretBlinkTimerFired)
119 , m_absCaretBoundsDirty(true)
121 , m_isCaretBlinkingSuspended(false)
122 , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
123 , m_shouldShowBlockCursor(false)
124 , m_pendingSelectionUpdate(false)
125 , m_shouldRevealSelection(false)
126 , m_alwaysAlignCursorOnScrollWhenRevealingSelection(false)
128 , m_updateAppearanceEnabled(false)
129 , m_caretBlinks(true)
130 , m_scrollingSuppressCount(0)
133 if (shouldAlwaysUseDirectionalSelection(m_frame))
134 m_selection.setIsDirectional(true);
137 Element* FrameSelection::rootEditableElementOrDocumentElement() const
139 Element* selectionRoot = m_selection.rootEditableElement();
140 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
143 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
145 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()),
146 defaultSetSelectionOptions(userTriggered), AXTextStateChangeIntent(), align);
149 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
151 const bool selectionHasDirection = true;
152 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
155 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
157 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), defaultSetSelectionOptions(userTriggered));
160 void FrameSelection::moveTo(const Range* range)
162 VisibleSelection selection = range ? VisibleSelection(range->startPosition(), range->endPosition()) : VisibleSelection();
163 setSelection(selection);
166 void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
168 const bool selectionHasDirection = true;
169 setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
172 void FrameSelection::moveWithoutValidationTo(const Position& base, const Position& extent, bool selectionHasDirection, bool shouldSetFocus, const AXTextStateChangeIntent& intent)
174 VisibleSelection newSelection;
175 newSelection.setWithoutValidation(base, extent);
176 newSelection.setIsDirectional(selectionHasDirection);
177 AXTextStateChangeIntent newIntent = intent.type == AXTextStateChangeTypeUnknown ? AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false }) : intent;
178 setSelection(newSelection, defaultSetSelectionOptions() | (shouldSetFocus ? 0 : DoNotSetFocus), newIntent);
181 void DragCaretController::setCaretPosition(const VisiblePosition& position)
183 if (Node* node = m_position.deepEquivalent().deprecatedNode())
184 invalidateCaretRect(node);
185 m_position = position;
186 setCaretRectNeedsUpdate();
187 Document* document = nullptr;
188 if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
189 invalidateCaretRect(node);
190 document = &node->document();
192 if (m_position.isNull() || m_position.isOrphan())
195 updateCaretRect(document, m_position);
198 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
200 RenderedPosition base(visibleBase);
201 RenderedPosition extent(visibleExtent);
203 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
206 if (base.atLeftBoundaryOfBidiRun()) {
207 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
208 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
209 visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
215 if (base.atRightBoundaryOfBidiRun()) {
216 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
217 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
218 visibleBase = base.positionAtRightBoundaryOfBiDiRun();
224 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
225 visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
229 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
230 visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
235 void FrameSelection::setSelectionByMouseIfDifferent(const VisibleSelection& passedNewSelection, TextGranularity granularity,
236 EndPointsAdjustmentMode endpointsAdjustmentMode)
238 VisibleSelection newSelection = passedNewSelection;
239 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
241 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
242 VisiblePosition newBase = base;
243 VisiblePosition extent = newSelection.visibleExtent();
244 VisiblePosition newExtent = extent;
245 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
246 adjustEndpointsAtBidiBoundary(newBase, newExtent);
248 if (newBase != base || newExtent != extent) {
249 m_originalBase = base;
250 newSelection.setBase(newBase);
251 newSelection.setExtent(newExtent);
252 } else if (m_originalBase.isNotNull()) {
253 if (m_selection.base() == newSelection.base())
254 newSelection.setBase(m_originalBase);
255 m_originalBase.clear();
258 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
259 if (m_selection == newSelection || !shouldChangeSelection(newSelection))
263 AXTextStateChangeIntent intent;
264 if (AXObjectCache::accessibilityEnabled() && newSelection.isCaret())
265 intent = AXTextStateChangeIntent(AXTextStateChangeTypeSelectionMove, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityUnknown, false });
267 intent = AXTextStateChangeIntent();
268 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent, AlignCursorOnScrollIfNeeded, granularity);
271 bool FrameSelection::setSelectionWithoutUpdatingAppearance(const VisibleSelection& newSelectionPossiblyWithoutDirection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
273 bool closeTyping = options & CloseTyping;
274 bool shouldClearTypingStyle = options & ClearTypingStyle;
276 VisibleSelection newSelection = newSelectionPossiblyWithoutDirection;
277 if (shouldAlwaysUseDirectionalSelection(m_frame))
278 newSelection.setIsDirectional(true);
281 m_selection = newSelection;
285 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
286 // if document->frame() == m_frame we can get into an infinite loop
287 if (Document* newSelectionDocument = newSelection.base().document()) {
288 if (RefPtr<Frame> newSelectionFrame = newSelectionDocument->frame()) {
289 if (newSelectionFrame != m_frame && newSelectionDocument != m_frame->document()) {
290 newSelectionFrame->selection().setSelection(newSelection, options, AXTextStateChangeIntent(), align, granularity);
291 // It's possible that during the above set selection, this FrameSelection has been modified by
292 // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
293 // the frame is about to be destroyed. If this is the case, clear our selection.
294 if (newSelectionFrame->hasOneRef() && m_selection.isNoneOrOrphaned())
301 m_granularity = granularity;
304 TypingCommand::closeTyping(m_frame);
306 if (shouldClearTypingStyle)
309 VisibleSelection oldSelection = m_selection;
310 m_selection = newSelection;
312 // Selection offsets should increase when LF is inserted before the caret in InsertLineBreakCommand. See <https://webkit.org/b/56061>.
313 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(newSelection.start()))
314 textControl->selectionChanged(options & FireSelectEvent);
316 if (oldSelection == newSelection)
319 setCaretRectNeedsUpdate();
321 if (!newSelection.isNone() && !(options & DoNotSetFocus))
322 setFocusedElementIfNeeded();
324 // Always clear the x position used for vertical arrow navigation.
325 // It will be restored by the vertical arrow navigation code if necessary.
326 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
327 selectFrameElementInParentIfFullySelected();
328 m_frame->editor().respondToChangedSelection(oldSelection, options);
329 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
334 void FrameSelection::setSelection(const VisibleSelection& selection, SetSelectionOptions options, AXTextStateChangeIntent intent, CursorAlignOnScroll align, TextGranularity granularity)
336 if (!setSelectionWithoutUpdatingAppearance(selection, options, align, granularity))
339 Document* document = m_frame->document();
343 m_shouldRevealSelection = options & RevealSelection;
344 m_alwaysAlignCursorOnScrollWhenRevealingSelection = align == AlignCursorOnScrollAlways;
346 m_pendingSelectionUpdate = true;
348 if (document->hasPendingStyleRecalc())
351 FrameView* frameView = document->view();
352 if (frameView && frameView->layoutPending())
355 updateAndRevealSelection(intent);
358 static void updateSelectionByUpdatingLayoutOrStyle(Frame& frame)
360 #if ENABLE(TEXT_CARET)
361 frame.document()->updateLayoutIgnorePendingStylesheets();
363 frame.document()->updateStyleIfNeeded();
367 void FrameSelection::setNeedsSelectionUpdate()
369 m_pendingSelectionUpdate = true;
370 if (RenderView* view = m_frame->contentRenderer())
371 view->clearSelection();
374 void FrameSelection::updateAndRevealSelection(const AXTextStateChangeIntent& intent)
376 if (!m_pendingSelectionUpdate)
379 m_pendingSelectionUpdate = false;
383 if (m_shouldRevealSelection) {
384 ScrollAlignment alignment;
386 if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
387 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
389 alignment = m_alwaysAlignCursorOnScrollWhenRevealingSelection ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
391 revealSelection(alignment, RevealExtent);
394 notifyAccessibilityForSelectionChange(intent);
396 if (auto* client = m_frame->editor().client())
397 client->didChangeSelectionAndUpdateLayout();
400 void FrameSelection::updateDataDetectorsForSelection()
402 #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
403 m_frame->editor().scanSelectionForTelephoneNumbers();
407 static bool removingNodeRemovesPosition(Node& node, const Position& position)
409 if (!position.anchorNode())
412 if (position.anchorNode() == &node)
415 if (!is<Element>(node))
418 return downcast<Element>(node).containsIncludingShadowDOM(position.anchorNode());
421 void DragCaretController::nodeWillBeRemoved(Node& node)
423 if (!hasCaret() || !node.inDocument())
426 if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
429 if (RenderView* view = node.document().renderView())
430 view->clearSelection();
435 void FrameSelection::nodeWillBeRemoved(Node& node)
437 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
438 // the selection in the document that created the fragment needs no adjustment.
439 if (isNone() || !node.inDocument())
442 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
443 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
446 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
448 bool clearRenderTreeSelection = false;
449 bool clearDOMTreeSelection = false;
451 if (startRemoved || endRemoved) {
452 Position start = m_selection.start();
453 Position end = m_selection.end();
455 updatePositionForNodeRemoval(start, node);
457 updatePositionForNodeRemoval(end, node);
459 if (start.isNotNull() && end.isNotNull()) {
460 if (m_selection.isBaseFirst())
461 m_selection.setWithoutValidation(start, end);
463 m_selection.setWithoutValidation(end, start);
465 clearDOMTreeSelection = true;
467 clearRenderTreeSelection = true;
468 } else if (baseRemoved || extentRemoved) {
469 // The base and/or extent are about to be removed, but the start and end aren't.
470 // Change the base and extent to the start and end, but don't re-validate the
471 // selection, since doing so could move the start and end into the node
472 // that is about to be removed.
473 if (m_selection.isBaseFirst())
474 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
476 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
477 } else if (RefPtr<Range> range = m_selection.firstRange()) {
478 ExceptionCode ec = 0;
479 Range::CompareResults compareResult = range->compareNode(node, ec);
480 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
481 // If we did nothing here, when this node's renderer was destroyed, the rect that it
482 // occupied would be invalidated, but, selection gaps that change as a result of
483 // the removal wouldn't be invalidated.
484 // FIXME: Don't do so much unnecessary invalidation.
485 clearRenderTreeSelection = true;
489 if (clearRenderTreeSelection) {
490 if (auto* renderView = node.document().renderView()) {
491 renderView->clearSelection();
493 // Trigger a selection update so the selection will be set again.
494 m_pendingSelectionUpdate = true;
495 renderView->setNeedsLayout();
499 if (clearDOMTreeSelection)
500 setSelection(VisibleSelection(), DoNotSetFocus);
503 static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
505 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
508 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
509 ASSERT(position.offsetInContainerNode() >= 0);
510 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
511 // Replacing text can be viewed as a deletion followed by insertion.
512 if (positionOffset >= offset && positionOffset <= offset + oldLength)
513 position.moveToOffset(offset);
515 // Adjust the offset if the position is after the end of the deleted contents
516 // (positionOffset > offset + oldLength) to avoid having a stale offset.
517 if (positionOffset > offset + oldLength)
518 position.moveToOffset(positionOffset - oldLength + newLength);
520 ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length());
523 void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
525 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
526 if (isNone() || !node || !node->inDocument())
529 Position base = m_selection.base();
530 Position extent = m_selection.extent();
531 Position start = m_selection.start();
532 Position end = m_selection.end();
533 updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength);
534 updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength);
535 updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength);
536 updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength);
538 if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) {
539 VisibleSelection newSelection;
541 newSelection.setWithoutValidation(base, extent);
542 else if (m_selection.isDirectional() && !m_selection.isBaseFirst())
543 newSelection.setWithoutValidation(end, start);
545 newSelection.setWithoutValidation(start, end);
547 setSelection(newSelection, DoNotSetFocus);
551 TextDirection FrameSelection::directionOfEnclosingBlock()
553 return WebCore::directionOfEnclosingBlock(m_selection.extent());
556 TextDirection FrameSelection::directionOfSelection()
558 InlineBox* startBox = nullptr;
559 InlineBox* endBox = nullptr;
561 // Cache the VisiblePositions because visibleStart() and visibleEnd()
562 // can cause layout, which has the potential to invalidate lineboxes.
563 VisiblePosition startPosition = m_selection.visibleStart();
564 VisiblePosition endPosition = m_selection.visibleEnd();
565 if (startPosition.isNotNull())
566 startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
567 if (endPosition.isNotNull())
568 endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
569 if (startBox && endBox && startBox->direction() == endBox->direction())
570 return startBox->direction();
572 return directionOfEnclosingBlock();
575 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
577 if (alter != AlterationExtend)
580 Position start = m_selection.start();
581 Position end = m_selection.end();
583 bool baseIsStart = true;
585 if (m_selection.isDirectional()) {
586 // Make base and extent match start and end so we extend the user-visible selection.
587 // This only matters for cases where base and extend point to different positions than
588 // start and end (e.g. after a double-click to select a word).
589 if (m_selection.isBaseFirst())
596 if (directionOfSelection() == LTR)
601 case DirectionForward:
605 if (directionOfSelection() == LTR)
610 case DirectionBackward:
616 m_selection.setBase(start);
617 m_selection.setExtent(end);
619 m_selection.setBase(end);
620 m_selection.setExtent(start);
624 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
626 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
627 // base/extent always point to the same nodes as start/end, but which points
628 // to which depends on the value of isBaseFirst. Then this can be changed
629 // to just return m_sel.extent().
630 if (m_frame && m_frame->editor().behavior().shouldAlwaysExtendSelectionFromExtentEndpoint())
631 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
633 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
636 VisiblePosition FrameSelection::startForPlatform() const
638 return positionForPlatform(true);
641 VisiblePosition FrameSelection::endForPlatform() const
643 return positionForPlatform(false);
646 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
648 VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
650 if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
651 // In order to skip spaces when moving right, we advance one
652 // word further and then move one word back. Given the
653 // semantics of previousWordPosition() this will put us at the
654 // beginning of the word following.
655 VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
656 if (positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
657 positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
659 bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
660 if (movingBackwardsMovedPositionToStartOfCurrentWord)
661 positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
663 return positionAfterCurrentWord;
666 #if ENABLE(USERSELECT_ALL)
667 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
669 if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
670 pos = isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary);
674 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
676 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
678 // The difference between modifyExtendingRight and modifyExtendingForward is:
679 // modifyExtendingForward always extends forward logically.
680 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
681 // it extends forward logically if the enclosing block is LTR direction,
682 // but it extends backward logically if the enclosing block is RTL direction.
683 switch (granularity) {
684 case CharacterGranularity:
685 if (directionOfEnclosingBlock() == LTR)
686 pos = pos.next(CannotCrossEditingBoundary);
688 pos = pos.previous(CannotCrossEditingBoundary);
690 case WordGranularity:
691 if (directionOfEnclosingBlock() == LTR)
692 pos = nextWordPositionForPlatform(pos);
694 pos = previousWordPosition(pos);
697 if (directionOfEnclosingBlock() == LTR)
698 pos = modifyExtendingForward(granularity);
700 pos = modifyExtendingBackward(granularity);
702 case SentenceGranularity:
703 case LineGranularity:
704 case ParagraphGranularity:
705 case SentenceBoundary:
706 case ParagraphBoundary:
707 case DocumentBoundary:
708 // FIXME: implement all of the above?
709 pos = modifyExtendingForward(granularity);
711 case DocumentGranularity:
712 ASSERT_NOT_REACHED();
715 #if ENABLE(USERSELECT_ALL)
716 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
721 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
723 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
724 switch (granularity) {
725 case CharacterGranularity:
726 pos = pos.next(CannotCrossEditingBoundary);
728 case WordGranularity:
729 pos = nextWordPositionForPlatform(pos);
731 case SentenceGranularity:
732 pos = nextSentencePosition(pos);
734 case LineGranularity:
735 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
737 case ParagraphGranularity:
738 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
740 case DocumentGranularity:
741 ASSERT_NOT_REACHED();
743 case SentenceBoundary:
744 pos = endOfSentence(endForPlatform());
747 pos = logicalEndOfLine(endForPlatform());
749 case ParagraphBoundary:
750 pos = endOfParagraph(endForPlatform());
752 case DocumentBoundary:
753 pos = endForPlatform();
754 if (isEditablePosition(pos.deepEquivalent()))
755 pos = endOfEditableContent(pos);
757 pos = endOfDocument(pos);
760 #if ENABLE(USERSELECT_ALL)
761 adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
766 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity, bool* reachedBoundary)
769 *reachedBoundary = false;
771 switch (granularity) {
772 case CharacterGranularity:
774 if (directionOfSelection() == LTR)
775 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
777 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
779 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true, reachedBoundary);
781 case WordGranularity: {
782 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
783 VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
784 pos = rightWordPosition(currentPosition, skipsSpaceWhenMovingRight);
786 *reachedBoundary = pos == currentPosition;
789 case SentenceGranularity:
790 case LineGranularity:
791 case ParagraphGranularity:
792 case SentenceBoundary:
793 case ParagraphBoundary:
794 case DocumentBoundary:
795 // FIXME: Implement all of the above.
796 pos = modifyMovingForward(granularity, reachedBoundary);
799 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
801 case DocumentGranularity:
802 ASSERT_NOT_REACHED();
808 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity, bool* reachedBoundary)
811 *reachedBoundary = false;
812 VisiblePosition currentPosition;
813 switch (granularity) {
814 case WordGranularity:
815 case SentenceGranularity:
816 currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
818 case LineGranularity:
819 case ParagraphGranularity:
820 case SentenceBoundary:
821 case ParagraphBoundary:
822 case DocumentBoundary:
823 currentPosition = endForPlatform();
829 // FIXME: Stay in editable content for the less common granularities.
830 switch (granularity) {
831 case CharacterGranularity:
833 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
835 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary, reachedBoundary);
837 case WordGranularity:
838 pos = nextWordPositionForPlatform(currentPosition);
840 case SentenceGranularity:
841 pos = nextSentencePosition(currentPosition);
843 case LineGranularity: {
844 // down-arrowing from a range selection that ends at the start of a line needs
845 // to leave the selection at that line start (no need to call nextLinePosition!)
846 pos = currentPosition;
847 if (!isRange() || !isStartOfLine(pos))
848 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
851 case ParagraphGranularity:
852 pos = nextParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
854 case DocumentGranularity:
855 ASSERT_NOT_REACHED();
857 case SentenceBoundary:
858 pos = endOfSentence(currentPosition);
861 pos = logicalEndOfLine(endForPlatform(), reachedBoundary);
863 case ParagraphBoundary:
864 pos = endOfParagraph(currentPosition);
866 case DocumentBoundary:
867 pos = currentPosition;
868 if (isEditablePosition(pos.deepEquivalent()))
869 pos = endOfEditableContent(pos);
871 pos = endOfDocument(pos);
874 switch (granularity) {
875 case WordGranularity:
876 case SentenceGranularity:
877 case LineGranularity:
878 case ParagraphGranularity:
879 case SentenceBoundary:
880 case ParagraphBoundary:
881 case DocumentBoundary:
883 *reachedBoundary = pos == currentPosition;
891 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
893 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
895 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
896 // modifyExtendingBackward always extends backward logically.
897 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
898 // it extends backward logically if the enclosing block is LTR direction,
899 // but it extends forward logically if the enclosing block is RTL direction.
900 switch (granularity) {
901 case CharacterGranularity:
902 if (directionOfEnclosingBlock() == LTR)
903 pos = pos.previous(CannotCrossEditingBoundary);
905 pos = pos.next(CannotCrossEditingBoundary);
907 case WordGranularity:
908 if (directionOfEnclosingBlock() == LTR)
909 pos = previousWordPosition(pos);
911 pos = nextWordPositionForPlatform(pos);
914 if (directionOfEnclosingBlock() == LTR)
915 pos = modifyExtendingBackward(granularity);
917 pos = modifyExtendingForward(granularity);
919 case SentenceGranularity:
920 case LineGranularity:
921 case ParagraphGranularity:
922 case SentenceBoundary:
923 case ParagraphBoundary:
924 case DocumentBoundary:
925 pos = modifyExtendingBackward(granularity);
927 case DocumentGranularity:
928 ASSERT_NOT_REACHED();
931 #if ENABLE(USERSELECT_ALL)
932 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
937 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
939 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
941 // Extending a selection backward by word or character from just after a table selects
942 // the table. This "makes sense" from the user perspective, esp. when deleting.
943 // It was done here instead of in VisiblePosition because we want VPs to iterate
945 switch (granularity) {
946 case CharacterGranularity:
947 pos = pos.previous(CannotCrossEditingBoundary);
949 case WordGranularity:
950 pos = previousWordPosition(pos);
952 case SentenceGranularity:
953 pos = previousSentencePosition(pos);
955 case LineGranularity:
956 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
958 case ParagraphGranularity:
959 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
961 case SentenceBoundary:
962 pos = startOfSentence(startForPlatform());
965 pos = logicalStartOfLine(startForPlatform());
967 case ParagraphBoundary:
968 pos = startOfParagraph(startForPlatform());
970 case DocumentBoundary:
971 pos = startForPlatform();
972 if (isEditablePosition(pos.deepEquivalent()))
973 pos = startOfEditableContent(pos);
975 pos = startOfDocument(pos);
977 case DocumentGranularity:
978 ASSERT_NOT_REACHED();
981 #if ENABLE(USERSELECT_ALL)
982 adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
987 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity, bool* reachedBoundary)
990 *reachedBoundary = false;
992 switch (granularity) {
993 case CharacterGranularity:
995 if (directionOfSelection() == LTR)
996 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
998 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
1000 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true, reachedBoundary);
1002 case WordGranularity: {
1003 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
1004 VisiblePosition currentPosition(m_selection.extent(), m_selection.affinity());
1005 pos = leftWordPosition(currentPosition, skipsSpaceWhenMovingRight);
1006 if (reachedBoundary)
1007 *reachedBoundary = pos == currentPosition;
1010 case SentenceGranularity:
1011 case LineGranularity:
1012 case ParagraphGranularity:
1013 case SentenceBoundary:
1014 case ParagraphBoundary:
1015 case DocumentBoundary:
1016 // FIXME: Implement all of the above.
1017 pos = modifyMovingBackward(granularity, reachedBoundary);
1020 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock(), reachedBoundary);
1022 case DocumentGranularity:
1023 ASSERT_NOT_REACHED();
1029 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity, bool* reachedBoundary)
1031 if (reachedBoundary)
1032 *reachedBoundary = false;
1033 VisiblePosition currentPosition;
1034 switch (granularity) {
1035 case WordGranularity:
1036 case SentenceGranularity:
1037 currentPosition = VisiblePosition(m_selection.extent(), m_selection.affinity());
1039 case LineGranularity:
1040 case ParagraphGranularity:
1041 case SentenceBoundary:
1042 case ParagraphBoundary:
1043 case DocumentBoundary:
1044 currentPosition = startForPlatform();
1049 VisiblePosition pos;
1050 switch (granularity) {
1051 case CharacterGranularity:
1053 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
1055 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary, reachedBoundary);
1057 case WordGranularity:
1058 pos = previousWordPosition(currentPosition);
1060 case SentenceGranularity:
1061 pos = previousSentencePosition(currentPosition);
1063 case LineGranularity:
1064 pos = previousLinePosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
1066 case ParagraphGranularity:
1067 pos = previousParagraphPosition(currentPosition, lineDirectionPointForBlockDirectionNavigation(START));
1069 case SentenceBoundary:
1070 pos = startOfSentence(currentPosition);
1073 pos = logicalStartOfLine(startForPlatform(), reachedBoundary);
1075 case ParagraphBoundary:
1076 pos = startOfParagraph(currentPosition);
1078 case DocumentBoundary:
1079 pos = currentPosition;
1080 if (isEditablePosition(pos.deepEquivalent()))
1081 pos = startOfEditableContent(pos);
1083 pos = startOfDocument(pos);
1085 case DocumentGranularity:
1086 ASSERT_NOT_REACHED();
1089 switch (granularity) {
1090 case WordGranularity:
1091 case SentenceGranularity:
1092 case LineGranularity:
1093 case ParagraphGranularity:
1094 case SentenceBoundary:
1095 case ParagraphBoundary:
1096 case DocumentBoundary:
1097 if (reachedBoundary)
1098 *reachedBoundary = pos == currentPosition;
1106 static bool isBoundary(TextGranularity granularity)
1108 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
1111 AXTextStateChangeIntent FrameSelection::textSelectionIntent(EAlteration alter, SelectionDirection direction, TextGranularity granularity)
1113 AXTextStateChangeIntent intent = AXTextStateChangeIntent();
1115 if (alter == FrameSelection::AlterationMove) {
1116 intent.type = AXTextStateChangeTypeSelectionMove;
1117 flip = isRange() && directionOfSelection() == RTL;
1119 intent.type = AXTextStateChangeTypeSelectionExtend;
1120 switch (granularity) {
1121 case CharacterGranularity:
1122 intent.selection.granularity = AXTextSelectionGranularityCharacter;
1124 case WordGranularity:
1125 intent.selection.granularity = AXTextSelectionGranularityWord;
1127 case SentenceGranularity:
1128 case SentenceBoundary:
1129 intent.selection.granularity = AXTextSelectionGranularitySentence;
1131 case LineGranularity:
1133 intent.selection.granularity = AXTextSelectionGranularityLine;
1135 case ParagraphGranularity:
1136 case ParagraphBoundary:
1137 intent.selection.granularity = AXTextSelectionGranularityParagraph;
1139 case DocumentGranularity:
1140 case DocumentBoundary:
1141 intent.selection.granularity = AXTextSelectionGranularityDocument;
1144 bool boundary = false;
1145 switch (granularity) {
1146 case CharacterGranularity:
1147 case WordGranularity:
1148 case SentenceGranularity:
1149 case LineGranularity:
1150 case ParagraphGranularity:
1151 case DocumentGranularity:
1153 case SentenceBoundary:
1155 case ParagraphBoundary:
1156 case DocumentBoundary:
1160 switch (direction) {
1161 case DirectionRight:
1162 case DirectionForward:
1164 intent.selection.direction = flip ? AXTextSelectionDirectionBeginning : AXTextSelectionDirectionEnd;
1166 intent.selection.direction = flip ? AXTextSelectionDirectionPrevious : AXTextSelectionDirectionNext;
1169 case DirectionBackward:
1171 intent.selection.direction = flip ? AXTextSelectionDirectionEnd : AXTextSelectionDirectionBeginning;
1173 intent.selection.direction = flip ? AXTextSelectionDirectionNext : AXTextSelectionDirectionPrevious;
1179 static AXTextSelection textSelectionWithDirectionAndGranularity(SelectionDirection direction, TextGranularity granularity)
1181 // FIXME: Account for BIDI in DirectionRight & DirectionLeft. (In a RTL block, Right would map to Previous/Beginning and Left to Next/End.)
1182 AXTextSelectionDirection intentDirection = AXTextSelectionDirectionUnknown;
1183 switch (direction) {
1184 case DirectionForward:
1185 intentDirection = AXTextSelectionDirectionNext;
1187 case DirectionRight:
1188 intentDirection = AXTextSelectionDirectionNext;
1190 case DirectionBackward:
1191 intentDirection = AXTextSelectionDirectionPrevious;
1194 intentDirection = AXTextSelectionDirectionPrevious;
1197 AXTextSelectionGranularity intentGranularity = AXTextSelectionGranularityUnknown;
1198 switch (granularity) {
1199 case CharacterGranularity:
1200 intentGranularity = AXTextSelectionGranularityCharacter;
1202 case WordGranularity:
1203 intentGranularity = AXTextSelectionGranularityWord;
1205 case SentenceGranularity:
1206 case SentenceBoundary: // FIXME: Boundary should affect direction.
1207 intentGranularity = AXTextSelectionGranularitySentence;
1209 case LineGranularity:
1210 intentGranularity = AXTextSelectionGranularityLine;
1212 case ParagraphGranularity:
1213 case ParagraphBoundary: // FIXME: Boundary should affect direction.
1214 intentGranularity = AXTextSelectionGranularityParagraph;
1216 case DocumentGranularity:
1217 case DocumentBoundary: // FIXME: Boundary should affect direction.
1218 intentGranularity = AXTextSelectionGranularityDocument;
1221 intentGranularity = AXTextSelectionGranularityLine;
1222 switch (direction) {
1223 case DirectionForward:
1224 intentDirection = AXTextSelectionDirectionEnd;
1226 case DirectionRight:
1227 intentDirection = AXTextSelectionDirectionEnd;
1229 case DirectionBackward:
1230 intentDirection = AXTextSelectionDirectionBeginning;
1233 intentDirection = AXTextSelectionDirectionBeginning;
1238 return { intentDirection, intentGranularity, false };
1241 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
1243 if (userTriggered == UserTriggered) {
1244 FrameSelection trialFrameSelection;
1245 trialFrameSelection.setSelection(m_selection);
1246 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
1248 bool change = shouldChangeSelection(trialFrameSelection.selection());
1252 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
1256 willBeModified(alter, direction);
1258 bool reachedBoundary = false;
1259 bool wasRange = m_selection.isRange();
1260 Position originalStartPosition = m_selection.start();
1261 VisiblePosition position;
1262 switch (direction) {
1263 case DirectionRight:
1264 if (alter == AlterationMove)
1265 position = modifyMovingRight(granularity, &reachedBoundary);
1267 position = modifyExtendingRight(granularity);
1269 case DirectionForward:
1270 if (alter == AlterationExtend)
1271 position = modifyExtendingForward(granularity);
1273 position = modifyMovingForward(granularity, &reachedBoundary);
1276 if (alter == AlterationMove)
1277 position = modifyMovingLeft(granularity, &reachedBoundary);
1279 position = modifyExtendingLeft(granularity);
1281 case DirectionBackward:
1282 if (alter == AlterationExtend)
1283 position = modifyExtendingBackward(granularity);
1285 position = modifyMovingBackward(granularity, &reachedBoundary);
1289 if (reachedBoundary && !isRange() && userTriggered == UserTriggered && m_frame && AXObjectCache::accessibilityEnabled()) {
1290 notifyAccessibilityForSelectionChange({ AXTextStateChangeTypeSelectionBoundary, textSelectionWithDirectionAndGranularity(direction, granularity) });
1294 if (position.isNull())
1297 if (isSpatialNavigationEnabled(m_frame))
1298 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
1301 if (m_frame && AXObjectCache::accessibilityEnabled()) {
1302 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1303 cache->setTextSelectionIntent(textSelectionIntent(alter, direction, granularity));
1306 // Some of the above operations set an xPosForVerticalArrowNavigation.
1307 // Setting a selection will clear it, so save it to possibly restore later.
1308 // Note: the START position type is arbitrary because it is unused, it would be
1309 // the requested position type if there were no xPosForVerticalArrowNavigation set.
1310 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
1311 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1314 case AlterationMove:
1315 moveTo(position, userTriggered);
1317 case AlterationExtend:
1319 if (!m_selection.isCaret()
1320 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
1321 && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1322 // Don't let the selection go across the base position directly. Needed to match mac
1323 // behavior when, for instance, word-selecting backwards starting with the caret in
1324 // the middle of a word and then word-selecting forward, leaving the caret in the
1325 // same place where it was, instead of directly selecting to the end of the word.
1326 VisibleSelection newSelection = m_selection;
1327 newSelection.setExtent(position);
1328 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1329 position = m_selection.base();
1332 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1333 // base in place and moving the extent. Matches NSTextView.
1334 if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1335 setExtent(position, userTriggered);
1337 TextDirection textDirection = directionOfEnclosingBlock();
1338 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1339 setEnd(position, userTriggered);
1341 setStart(position, userTriggered);
1346 if (granularity == LineGranularity || granularity == ParagraphGranularity)
1347 m_xPosForVerticalArrowNavigation = x;
1349 if (userTriggered == UserTriggered)
1350 m_granularity = CharacterGranularity;
1352 setCaretRectNeedsUpdate();
1357 // FIXME: Maybe baseline would be better?
1358 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1360 IntRect rect = c.absoluteCaretBounds();
1363 y = rect.y() + rect.height() / 2;
1367 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1369 if (!verticalDistance)
1372 if (userTriggered == UserTriggered) {
1373 FrameSelection trialFrameSelection;
1374 trialFrameSelection.setSelection(m_selection);
1375 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1377 bool change = shouldChangeSelection(trialFrameSelection.selection());
1382 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1384 VisiblePosition pos;
1385 LayoutUnit xPos = 0;
1387 case AlterationMove:
1388 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1389 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1390 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1392 case AlterationExtend:
1393 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1394 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1395 m_selection.setAffinity(DOWNSTREAM);
1400 if (!absoluteCaretY(pos, startY))
1402 if (direction == DirectionUp)
1406 VisiblePosition result;
1407 VisiblePosition next;
1408 for (VisiblePosition p = pos; ; p = next) {
1409 if (direction == DirectionUp)
1410 next = previousLinePosition(p, xPos);
1412 next = nextLinePosition(p, xPos);
1414 if (next.isNull() || next == p)
1417 if (!absoluteCaretY(next, nextY))
1419 if (direction == DirectionUp)
1421 if (nextY - startY > static_cast<int>(verticalDistance))
1423 if (nextY >= lastY) {
1429 if (result.isNull())
1433 case AlterationMove:
1434 moveTo(result, userTriggered, align);
1436 case AlterationExtend:
1437 setExtent(result, userTriggered);
1441 if (userTriggered == UserTriggered)
1442 m_granularity = CharacterGranularity;
1444 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1449 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1459 pos = m_selection.start();
1462 pos = m_selection.end();
1465 pos = m_selection.base();
1468 pos = m_selection.extent();
1472 Frame* frame = pos.anchorNode()->document().frame();
1476 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1477 VisiblePosition visiblePosition(pos, m_selection.affinity());
1478 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1479 // after the selection is created and before this function is called.
1480 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1481 m_xPosForVerticalArrowNavigation = x;
1483 x = m_xPosForVerticalArrowNavigation;
1488 void FrameSelection::clear()
1490 m_granularity = CharacterGranularity;
1491 setSelection(VisibleSelection());
1494 void FrameSelection::prepareForDestruction()
1496 m_granularity = CharacterGranularity;
1498 #if ENABLE(TEXT_CARET)
1499 m_caretBlinkTimer.stop();
1502 RenderView* view = m_frame->contentRenderer();
1504 view->clearSelection();
1506 setSelectionWithoutUpdatingAppearance(VisibleSelection(), defaultSetSelectionOptions(), AlignCursorOnScrollIfNeeded, CharacterGranularity);
1507 m_previousCaretNode = nullptr;
1510 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1512 if (m_selection.isBaseFirst())
1513 setBase(pos, trigger);
1515 setExtent(pos, trigger);
1518 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1520 if (m_selection.isBaseFirst())
1521 setExtent(pos, trigger);
1523 setBase(pos, trigger);
1526 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1528 const bool selectionHasDirection = true;
1529 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1532 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1534 const bool selectionHasDirection = true;
1535 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1538 void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1540 const bool selectionHasDirection = true;
1541 setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1544 void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1546 const bool selectionHasDirection = true;
1547 setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), defaultSetSelectionOptions(userTriggered));
1550 void CaretBase::clearCaretRect()
1552 m_caretLocalRect = LayoutRect();
1555 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
1557 document->updateLayoutIgnorePendingStylesheets();
1558 m_caretRectNeedsUpdate = false;
1559 RenderBlock* renderer;
1560 m_caretLocalRect = localCaretRectInRendererForCaretPainting(caretPosition, renderer);
1561 return !m_caretLocalRect.isEmpty();
1564 RenderBlock* FrameSelection::caretRendererWithoutUpdatingLayout() const
1566 return rendererForCaretPainting(m_selection.start().deprecatedNode());
1569 RenderBlock* DragCaretController::caretRenderer() const
1571 return rendererForCaretPainting(m_position.deepEquivalent().deprecatedNode());
1574 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1576 return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1579 IntRect FrameSelection::absoluteCaretBounds()
1583 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
1584 recomputeCaretRect();
1585 return m_absCaretBounds;
1588 static void repaintCaretForLocalRect(Node* node, const LayoutRect& rect)
1590 if (auto* caretPainter = rendererForCaretPainting(node))
1591 caretPainter->repaintRectangle(rect);
1594 bool FrameSelection::recomputeCaretRect()
1596 if (!shouldUpdateCaretRect())
1602 FrameView* v = m_frame->document()->view();
1606 LayoutRect oldRect = localCaretRectWithoutUpdate();
1608 RefPtr<Node> caretNode = m_previousCaretNode;
1609 if (shouldUpdateCaretRect()) {
1610 if (!isNonOrphanedCaret(m_selection))
1613 VisiblePosition visibleStart = m_selection.visibleStart();
1614 if (updateCaretRect(m_frame->document(), visibleStart)) {
1615 caretNode = visibleStart.deepEquivalent().deprecatedNode();
1616 m_absCaretBoundsDirty = true;
1620 LayoutRect newRect = localCaretRectWithoutUpdate();
1622 if (caretNode == m_previousCaretNode && oldRect == newRect && !m_absCaretBoundsDirty)
1625 IntRect oldAbsCaretBounds = m_absCaretBounds;
1626 m_absCaretBounds = absoluteBoundsForLocalCaretRect(rendererForCaretPainting(caretNode.get()), newRect);
1628 if (m_absCaretBoundsDirty && m_selection.isCaret()) // We should be able to always assert this condition.
1629 ASSERT(m_absCaretBounds == m_selection.visibleStart().absoluteCaretBounds());
1631 m_absCaretBoundsDirty = false;
1633 if (caretNode == m_previousCaretNode && oldAbsCaretBounds == m_absCaretBounds)
1636 #if ENABLE(TEXT_CARET)
1637 if (RenderView* view = m_frame->document()->renderView()) {
1638 bool previousOrNewCaretNodeIsContentEditable = m_selection.isContentEditable() || (m_previousCaretNode && m_previousCaretNode->isContentEditable());
1639 if (shouldRepaintCaret(view, previousOrNewCaretNodeIsContentEditable)) {
1640 if (m_previousCaretNode)
1641 repaintCaretForLocalRect(m_previousCaretNode.get(), oldRect);
1642 m_previousCaretNode = caretNode;
1643 repaintCaretForLocalRect(caretNode.get(), newRect);
1650 bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
1653 Frame* frame = &view->frameView().frame(); // The frame where the selection started.
1654 bool caretBrowsing = frame && frame->settings().caretBrowsingEnabled();
1655 return (caretBrowsing || isContentEditable);
1658 void FrameSelection::invalidateCaretRect()
1663 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1666 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
1668 // EDIT FIXME: This is an unfortunate hack.
1669 // Basically, we can't trust this layout position since we
1670 // can't guarantee that the check to see if we are in unrendered
1671 // content will work at this point. We may have to wait for
1672 // a layout and re-render of the document to happen. So, resetting this
1673 // flag will cause another caret layout to happen the first time
1674 // that we try to paint the caret after this call. That one will work since
1675 // it happens after the document has accounted for any editing
1676 // changes which may have been done.
1677 // And, we need to leave this layout here so the caret moves right
1678 // away after clicking.
1679 m_caretRectNeedsUpdate = true;
1681 if (caretRectChanged)
1684 if (RenderView* view = node->document().renderView()) {
1685 if (shouldRepaintCaret(view, isEditableNode(*node)))
1686 repaintCaretForLocalRect(node, localCaretRectWithoutUpdate());
1690 void FrameSelection::paintCaret(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1692 if (m_selection.isCaret() && m_caretPaint)
1693 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1696 #if ENABLE(TEXT_CARET)
1697 static inline bool disappearsIntoBackground(Color foreground, Color background)
1699 return background.blend(foreground) == background;
1703 void CaretBase::paintCaret(Node* node, GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1705 #if ENABLE(TEXT_CARET)
1706 if (m_caretVisibility == Hidden)
1709 LayoutRect drawingRect = localCaretRectWithoutUpdate();
1710 if (auto* renderer = rendererForCaretPainting(node))
1711 renderer->flipForWritingMode(drawingRect);
1712 drawingRect.moveBy(roundedIntPoint(paintOffset));
1713 LayoutRect caret = intersection(drawingRect, clipRect);
1714 if (caret.isEmpty())
1717 Color caretColor = Color::black;
1718 Element* element = is<Element>(*node) ? downcast<Element>(node) : node->parentElement();
1719 Element* rootEditableElement = node->rootEditableElement();
1721 if (element && element->renderer()) {
1722 bool setToRootEditableElement = false;
1723 if (rootEditableElement && rootEditableElement->renderer()) {
1724 const auto& rootEditableStyle = rootEditableElement->renderer()->style();
1725 const auto& elementStyle = element->renderer()->style();
1726 auto rootEditableBGColor = rootEditableStyle.visitedDependentColor(CSSPropertyBackgroundColor);
1727 auto elementBGColor = elementStyle.visitedDependentColor(CSSPropertyBackgroundColor);
1728 if (disappearsIntoBackground(elementBGColor, rootEditableBGColor)) {
1729 caretColor = rootEditableStyle.visitedDependentColor(CSSPropertyColor);
1730 setToRootEditableElement = true;
1733 if (!setToRootEditableElement)
1734 caretColor = element->renderer()->style().visitedDependentColor(CSSPropertyColor);
1737 context.fillRect(caret, caretColor);
1740 UNUSED_PARAM(context);
1741 UNUSED_PARAM(paintOffset);
1742 UNUSED_PARAM(clipRect);
1746 void FrameSelection::debugRenderer(RenderObject* renderer, bool selected) const
1748 if (is<Element>(*renderer->node())) {
1749 Element& element = downcast<Element>(*renderer->node());
1750 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element.localName().string().utf8().data());
1751 } else if (is<RenderText>(*renderer)) {
1752 RenderText& textRenderer = downcast<RenderText>(*renderer);
1753 if (!textRenderer.textLength() || !textRenderer.firstTextBox()) {
1754 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1758 static const int max = 36;
1759 String text = textRenderer.text();
1760 int textLength = text.length();
1763 if (renderer->node() == m_selection.start().containerNode())
1764 offset = m_selection.start().computeOffsetInContainerNode();
1765 else if (renderer->node() == m_selection.end().containerNode())
1766 offset = m_selection.end().computeOffsetInContainerNode();
1769 InlineTextBox* box = textRenderer.findNextInlineTextBox(offset, pos);
1770 text = text.substring(box->start(), box->len());
1776 // text is shorter than max
1777 if (textLength < max) {
1780 } else if (pos - mid < 0) {
1781 // too few characters to left
1782 show = text.left(max - 3) + "...";
1784 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1785 // enough characters on each side
1786 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1789 // too few characters on right
1790 show = "..." + text.right(max - 3);
1791 caret = pos - (textLength - show.length());
1794 show.replace('\n', ' ');
1795 show.replace('\r', ' ');
1796 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1797 fprintf(stderr, " ");
1798 for (int i = 0; i < caret; i++)
1799 fprintf(stderr, " ");
1800 fprintf(stderr, "^\n");
1802 if ((int)text.length() > max)
1803 text = text.left(max - 3) + "...";
1805 text = text.left(max);
1806 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1811 bool FrameSelection::contains(const LayoutPoint& point)
1813 // Treat a collapsed selection like no selection.
1817 RenderView* renderView = m_frame->contentRenderer();
1821 HitTestResult result(point);
1822 renderView->hitTest(HitTestRequest(), result);
1823 Node* innerNode = result.innerNode();
1824 if (!innerNode || !innerNode->renderer())
1827 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint(), nullptr));
1828 if (visiblePos.isNull())
1831 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1834 Position start(m_selection.visibleStart().deepEquivalent());
1835 Position end(m_selection.visibleEnd().deepEquivalent());
1836 Position p(visiblePos.deepEquivalent());
1838 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1841 // Workaround for the fact that it's hard to delete a frame.
1842 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1843 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1844 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1845 // mouse or the keyboard after setting the selection.
1846 void FrameSelection::selectFrameElementInParentIfFullySelected()
1848 // Find the parent frame; if there is none, then we have nothing to do.
1849 Frame* parent = m_frame->tree().parent();
1852 Page* page = m_frame->page();
1856 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1859 if (!isStartOfDocument(selection().visibleStart()))
1861 if (!isEndOfDocument(selection().visibleEnd()))
1864 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1865 Element* ownerElement = m_frame->ownerElement();
1868 ContainerNode* ownerElementParent = ownerElement->parentNode();
1869 if (!ownerElementParent)
1872 // 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.
1873 if (!ownerElementParent->hasEditableStyle())
1876 // Create compute positions before and after the element.
1877 unsigned ownerElementNodeIndex = ownerElement->computeNodeIndex();
1878 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1879 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1881 // Focus on the parent frame, and then select from before this element to after.
1882 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1883 if (parent->selection().shouldChangeSelection(newSelection)) {
1884 page->focusController().setFocusedFrame(parent);
1885 parent->selection().setSelection(newSelection);
1889 void FrameSelection::selectAll()
1891 Document* document = m_frame->document();
1893 Element* focusedElement = document->focusedElement();
1894 if (is<HTMLSelectElement>(focusedElement)) {
1895 HTMLSelectElement& selectElement = downcast<HTMLSelectElement>(*focusedElement);
1896 if (selectElement.canSelectAll()) {
1897 selectElement.selectAll();
1903 Node* selectStartTarget = nullptr;
1904 if (m_selection.isContentEditable()) {
1905 root = highestEditableRoot(m_selection.start());
1906 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1907 selectStartTarget = shadowRoot->shadowHost();
1909 selectStartTarget = root.get();
1911 if (m_selection.isNone() && focusedElement) {
1912 if (is<HTMLTextFormControlElement>(*focusedElement)) {
1913 downcast<HTMLTextFormControlElement>(*focusedElement).select();
1916 root = focusedElement->nonBoundaryShadowTreeRootNode();
1918 root = m_selection.nonBoundaryShadowTreeRootNode();
1921 selectStartTarget = root->shadowHost();
1923 root = document->documentElement();
1924 selectStartTarget = document->bodyOrFrameset();
1930 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
1933 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1935 if (shouldChangeSelection(newSelection)) {
1936 AXTextStateChangeIntent intent(AXTextStateChangeTypeSelectionExtend, AXTextSelection { AXTextSelectionDirectionDiscontiguous, AXTextSelectionGranularityAll, false });
1937 setSelection(newSelection, defaultSetSelectionOptions() | FireSelectEvent, intent);
1941 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1945 ASSERT(&range->startContainer().document() == &range->endContainer().document());
1947 VisibleSelection newSelection(*range, affinity);
1950 // FIXME: Why do we need this check only in iOS?
1951 if (newSelection.isNone())
1955 setSelection(newSelection, ClearTypingStyle | (closeTyping ? CloseTyping : 0));
1959 void FrameSelection::focusedOrActiveStateChanged()
1961 bool activeAndFocused = isFocusedAndActive();
1962 Ref<Document> document(*m_frame->document());
1964 document->updateStyleIfNeeded();
1966 #if USE(UIKIT_EDITING)
1967 // Caret blinking (blinks | does not blink)
1968 if (activeAndFocused)
1969 setSelectionFromNone();
1970 setCaretVisible(activeAndFocused);
1972 // Because RenderObject::selectionBackgroundColor() and
1973 // RenderObject::selectionForegroundColor() check if the frame is active,
1974 // we have to update places those colors were painted.
1975 if (RenderView* view = document->renderView())
1976 view->repaintSelection();
1978 // Caret appears in the active frame.
1979 if (activeAndFocused)
1980 setSelectionFromNone();
1981 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1983 // Because StyleResolver::checkOneSelector() and
1984 // RenderTheme::isFocused() check if the frame is active, we have to
1985 // update style and theme state that depended on those.
1986 if (Element* element = document->focusedElement()) {
1987 element->setNeedsStyleRecalc();
1988 if (RenderObject* renderer = element->renderer())
1989 if (renderer && renderer->style().hasAppearance())
1990 renderer->theme().stateChanged(*renderer, ControlStates::FocusState);
1995 void FrameSelection::pageActivationChanged()
1997 focusedOrActiveStateChanged();
2000 void FrameSelection::setFocused(bool flag)
2002 if (m_focused == flag)
2006 focusedOrActiveStateChanged();
2009 bool FrameSelection::isFocusedAndActive() const
2011 return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
2014 #if ENABLE(TEXT_CARET)
2015 inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame)
2017 return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
2021 void FrameSelection::updateAppearance()
2024 if (!m_updateAppearanceEnabled)
2028 // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
2029 // the FrameSelection will paint a blinking caret as usual).
2030 VisibleSelection oldSelection = selection();
2032 #if ENABLE(TEXT_CARET)
2033 bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
2034 bool caretRectChangedOrCleared = recomputeCaretRect();
2036 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2037 bool shouldBlink = !paintBlockCursor && caretIsVisible() && isCaret() && (oldSelection.isContentEditable() || caretBrowsing);
2039 // If the caret moved, stop the blink timer so we can restart with a
2040 // black caret in the new location.
2041 if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame))
2042 m_caretBlinkTimer.stop();
2044 // Start blinking with a black caret. Be sure not to restart if we're
2045 // already blinking in the right location.
2046 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
2047 if (double blinkInterval = m_frame->page()->theme().caretBlinkInterval())
2048 m_caretBlinkTimer.startRepeating(blinkInterval);
2050 if (!m_caretPaint) {
2051 m_caretPaint = true;
2052 invalidateCaretRect();
2057 RenderView* view = m_frame->contentRenderer();
2061 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
2062 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
2063 #if ENABLE(TEXT_CARET)
2064 VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : oldSelection.visibleEnd();
2065 VisibleSelection selection(oldSelection.visibleStart(), endVisiblePosition);
2067 VisibleSelection selection(oldSelection.visibleStart(), oldSelection.visibleEnd());
2070 if (!selection.isRange()) {
2071 view->clearSelection();
2075 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
2076 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
2077 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
2078 // and will fill the gap before 'bar'.
2079 Position startPos = selection.start();
2080 Position candidate = startPos.downstream();
2081 if (candidate.isCandidate())
2082 startPos = candidate;
2083 Position endPos = selection.end();
2084 candidate = endPos.upstream();
2085 if (candidate.isCandidate())
2088 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
2089 // because we don't yet notify the FrameSelection of text removal.
2090 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
2091 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
2092 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
2093 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
2097 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
2099 if (caretVisibility() == visibility)
2102 // FIXME: We shouldn't trigger a synchrnously layout here.
2104 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
2106 #if ENABLE(TEXT_CARET)
2108 m_caretPaint = false;
2109 invalidateCaretRect();
2111 CaretBase::setCaretVisibility(visibility);
2117 void FrameSelection::caretBlinkTimerFired()
2119 #if ENABLE(TEXT_CARET)
2120 ASSERT(caretIsVisible());
2122 bool caretPaint = m_caretPaint;
2123 if (isCaretBlinkingSuspended() && caretPaint)
2125 m_caretPaint = !caretPaint;
2126 invalidateCaretRect();
2130 // Helper function that tells whether a particular node is an element that has an entire
2131 // Frame and FrameView, a <frame>, <iframe>, or <object>.
2132 static bool isFrameElement(const Node* n)
2136 RenderObject* renderer = n->renderer();
2137 if (!is<RenderWidget>(renderer))
2139 Widget* widget = downcast<RenderWidget>(*renderer).widget();
2140 return widget && widget->isFrameView();
2143 void FrameSelection::setFocusedElementIfNeeded()
2145 if (isNone() || !isFocused())
2148 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2149 if (caretBrowsing) {
2150 if (Element* anchor = enclosingAnchorElement(m_selection.base())) {
2151 m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
2156 if (Element* target = m_selection.rootEditableElement()) {
2157 // Walk up the DOM tree to search for an element to focus.
2159 // We don't want to set focus on a subframe when selecting in a parent frame,
2160 // so add the !isFrameElement check here. There's probably a better way to make this
2161 // work in the long term, but this is the safest fix at this time.
2162 if (target->isMouseFocusable() && !isFrameElement(target)) {
2163 m_frame->page()->focusController().setFocusedElement(target, m_frame);
2166 target = target->parentOrShadowHostElement();
2168 m_frame->document()->setFocusedElement(0);
2172 m_frame->page()->focusController().setFocusedElement(0, m_frame);
2175 void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext& p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
2177 #if ENABLE(TEXT_CARET)
2178 if (m_position.deepEquivalent().deprecatedNode()->document().frame() == frame)
2179 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
2181 UNUSED_PARAM(frame);
2183 UNUSED_PARAM(paintOffset);
2184 UNUSED_PARAM(clipRect);
2188 PassRefPtr<MutableStyleProperties> FrameSelection::copyTypingStyle() const
2190 if (!m_typingStyle || !m_typingStyle->style())
2192 return m_typingStyle->style()->mutableCopy();
2195 bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
2198 if (m_frame->selectionChangeCallbacksDisabled())
2201 return m_frame->editor().client()->shouldDeleteRange(selection.toNormalizedRange().get());
2204 FloatRect FrameSelection::selectionBounds(bool clipToVisibleContent) const
2206 if (!m_frame->document())
2207 return LayoutRect();
2209 updateSelectionByUpdatingLayoutOrStyle(*m_frame);
2210 RenderView* root = m_frame->contentRenderer();
2211 FrameView* view = m_frame->view();
2213 return LayoutRect();
2215 LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent);
2216 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect)) : selectionRect;
2219 void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
2221 RenderView* root = m_frame->contentRenderer();
2225 Vector<FloatRect> textRects;
2226 getTextRectangles(textRects, textRectHeight);
2228 FloatRect visibleContentRect = m_frame->view()->visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
2230 for (const auto& rect : textRects) {
2231 FloatRect intersectionRect = intersection(rect, visibleContentRect);
2232 if (!intersectionRect.isEmpty())
2233 rectangles.append(intersectionRect);
2237 void FrameSelection::getTextRectangles(Vector<FloatRect>& rectangles, TextRectangleHeight textRectHeight) const
2239 RefPtr<Range> range = toNormalizedRange();
2243 Vector<FloatQuad> quads;
2244 range->absoluteTextQuads(quads, textRectHeight == TextRectangleHeight::SelectionHeight);
2246 for (const auto& quad : quads)
2247 rectangles.append(quad.boundingBox());
2250 // Scans logically forward from "start", including any child frames.
2251 static HTMLFormElement* scanForForm(Element* start)
2256 auto descendants = descendantsOfType<HTMLElement>(start->document());
2257 for (auto it = descendants.from(*start), end = descendants.end(); it != end; ++it) {
2258 HTMLElement& element = *it;
2259 if (is<HTMLFormElement>(element))
2260 return &downcast<HTMLFormElement>(element);
2261 if (is<HTMLFormControlElement>(element))
2262 return downcast<HTMLFormControlElement>(element).form();
2263 if (is<HTMLFrameElementBase>(element)) {
2264 Document* contentDocument = downcast<HTMLFrameElementBase>(element).contentDocument();
2265 if (!contentDocument)
2267 if (HTMLFormElement* frameResult = scanForForm(contentDocument->documentElement()))
2274 // We look for either the form containing the current focus, or for one immediately after it
2275 HTMLFormElement* FrameSelection::currentForm() const
2277 // Start looking either at the active (first responder) node, or where the selection is.
2278 Element* start = m_frame->document()->focusedElement();
2280 start = m_selection.start().element();
2284 if (auto form = lineageOfType<HTMLFormElement>(*start).first())
2286 if (auto formControl = lineageOfType<HTMLFormControlElement>(*start).first())
2287 return formControl->form();
2289 // Try walking forward in the node tree to find a form element.
2290 return scanForForm(start);
2293 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
2297 switch (m_selection.selectionType()) {
2298 case VisibleSelection::NoSelection:
2300 case VisibleSelection::CaretSelection:
2301 rect = absoluteCaretBounds();
2303 case VisibleSelection::RangeSelection:
2304 rect = revealExtentOption == RevealExtent ? VisiblePosition(m_selection.extent()).absoluteCaretBounds() : enclosingIntRect(selectionBounds(false));
2308 Position start = m_selection.start();
2309 ASSERT(start.deprecatedNode());
2310 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
2312 if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
2313 if (!m_scrollingSuppressCount) {
2314 layer->setAdjustForIOSCaretWhenScrolling(true);
2315 layer->scrollRectToVisible(rect, alignment, alignment);
2316 layer->setAdjustForIOSCaretWhenScrolling(false);
2318 if (m_frame->page())
2319 m_frame->page()->chrome().client().notifyRevealedSelectionByScrollingFrame(m_frame);
2323 // FIXME: This code only handles scrolling the startContainer's layer, but
2324 // the selection rect could intersect more than just that.
2325 // See <rdar://problem/4799899>.
2326 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
2332 void FrameSelection::setSelectionFromNone()
2334 // Put a caret inside the body if the entire frame is editable (either the
2335 // entire WebView is editable or designMode is on for this document).
2337 Document* document = m_frame->document();
2339 bool caretBrowsing = m_frame->settings().caretBrowsingEnabled();
2340 if (!isNone() || !(document->hasEditableStyle() || caretBrowsing))
2343 if (!document || !(isNone() || isStartOfDocument(VisiblePosition(m_selection.start(), m_selection.affinity()))) || !document->hasEditableStyle())
2347 if (auto* body = document->body())
2348 setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
2351 bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
2354 if (m_frame->selectionChangeCallbacksDisabled())
2357 return m_frame->editor().shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
2360 bool FrameSelection::dispatchSelectStart()
2362 Node* selectStartTarget = m_selection.extent().containerNode();
2363 if (!selectStartTarget)
2366 return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
2369 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
2371 m_shouldShowBlockCursor = shouldShowBlockCursor;
2373 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2378 void FrameSelection::updateAppearanceAfterLayout()
2380 setCaretRectNeedsUpdate();
2381 updateAndRevealSelection(AXTextStateChangeIntent());
2382 updateDataDetectorsForSelection();
2385 #if ENABLE(TREE_DEBUGGING)
2387 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
2389 m_selection.formatForDebugger(buffer, length);
2392 void FrameSelection::showTreeForThis() const
2394 m_selection.showTreeForThis();
2400 void FrameSelection::expandSelectionToElementContainingCaretSelection()
2402 RefPtr<Range> range = elementRangeContainingCaretSelection();
2405 VisibleSelection selection(*range, DOWNSTREAM);
2406 setSelection(selection);
2409 PassRefPtr<Range> FrameSelection::elementRangeContainingCaretSelection() const
2411 if (m_selection.isNone())
2414 VisibleSelection selection = m_selection;
2415 if (selection.isNone())
2418 VisiblePosition visiblePos(selection.start(), VP_DEFAULT_AFFINITY);
2419 if (visiblePos.isNull())
2422 Node* node = visiblePos.deepEquivalent().deprecatedNode();
2423 Element* element = deprecatedEnclosingBlockFlowElement(node);
2427 Position startPos = createLegacyEditingPosition(element, 0);
2428 Position endPos = createLegacyEditingPosition(element, element->countChildNodes());
2430 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY);
2431 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY);
2432 if (startVisiblePos.isNull() || endVisiblePos.isNull())
2435 selection.setBase(startVisiblePos);
2436 selection.setExtent(endVisiblePos);
2438 return selection.toNormalizedRange();
2441 void FrameSelection::expandSelectionToWordContainingCaretSelection()
2443 VisibleSelection selection(wordSelectionContainingCaretSelection(m_selection));
2444 if (selection.isCaretOrRange())
2445 setSelection(selection);
2448 PassRefPtr<Range> FrameSelection::wordRangeContainingCaretSelection()
2450 return wordSelectionContainingCaretSelection(m_selection).toNormalizedRange();
2453 void FrameSelection::expandSelectionToStartOfWordContainingCaretSelection()
2455 if (m_selection.isNone() || isStartOfDocument(m_selection.start()))
2458 VisiblePosition s1(m_selection.start());
2459 VisiblePosition e1(m_selection.end());
2461 VisibleSelection expanded(wordSelectionContainingCaretSelection(m_selection));
2462 VisiblePosition s2(expanded.start());
2464 // Don't allow the start to become greater after the expansion.
2465 if (s2.isNull() || s2 > s1)
2471 UChar FrameSelection::characterInRelationToCaretSelection(int amount) const
2473 if (m_selection.isNone())
2476 VisibleSelection selection = m_selection;
2477 ASSERT(selection.isCaretOrRange());
2479 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY);
2482 int count = abs(amount);
2483 for (int i = 0; i < count; i++)
2484 visiblePosition = visiblePosition.previous();
2485 return visiblePosition.characterBefore();
2487 for (int i = 0; i < amount; i++)
2488 visiblePosition = visiblePosition.next();
2489 return visiblePosition.characterAfter();
2492 UChar FrameSelection::characterBeforeCaretSelection() const
2494 if (m_selection.isNone())
2497 VisibleSelection selection = m_selection;
2498 ASSERT(selection.isCaretOrRange());
2500 VisiblePosition visiblePosition(selection.start(), VP_DEFAULT_AFFINITY);
2501 return visiblePosition.characterBefore();
2504 UChar FrameSelection::characterAfterCaretSelection() const
2506 if (m_selection.isNone())
2509 VisibleSelection selection = m_selection;
2510 ASSERT(selection.isCaretOrRange());
2512 VisiblePosition visiblePosition(selection.end(), VP_DEFAULT_AFFINITY);
2513 return visiblePosition.characterAfter();
2516 int FrameSelection::wordOffsetInRange(const Range *range) const
2521 VisibleSelection selection = m_selection;
2522 if (!selection.isCaret())
2525 // FIXME: This will only work in cases where the selection remains in
2526 // the same node after it is expanded. Improve to handle more complicated
2528 int result = selection.start().deprecatedEditingOffset() - range->startOffset();
2534 bool FrameSelection::spaceFollowsWordInRange(const Range *range) const
2538 Node& node = range->endContainer();
2539 int endOffset = range->endOffset();
2540 VisiblePosition pos(createLegacyEditingPosition(&node, endOffset), VP_DEFAULT_AFFINITY);
2541 return isSpaceOrNewline(pos.characterAfter());
2544 bool FrameSelection::selectionAtDocumentStart() const
2546 VisibleSelection selection = m_selection;
2547 if (selection.isNone())
2550 Position startPos(selection.start());
2551 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2555 return isStartOfDocument(pos);
2558 bool FrameSelection::selectionAtSentenceStart() const
2560 VisibleSelection selection = m_selection;
2561 if (selection.isNone())
2564 return actualSelectionAtSentenceStart(selection);
2567 bool FrameSelection::selectionAtWordStart() const
2569 VisibleSelection selection = m_selection;
2570 if (selection.isNone())
2573 Position startPos(selection.start());
2574 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2578 if (isStartOfParagraph(pos))
2582 unsigned previousCount = 0;
2583 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) {
2585 if (isStartOfParagraph(pos)) {
2586 if (previousCount == 1)
2590 UChar c(pos.characterAfter());
2592 result = isSpaceOrNewline(c) || c == 0xA0 || (u_ispunct(c) && c != ',' && c != '-' && c != '\'');
2600 PassRefPtr<Range> FrameSelection::rangeByMovingCurrentSelection(int amount) const
2602 return rangeByAlteringCurrentSelection(AlterationMove, amount);
2605 PassRefPtr<Range> FrameSelection::rangeByExtendingCurrentSelection(int amount) const
2607 return rangeByAlteringCurrentSelection(AlterationExtend, amount);
2610 void FrameSelection::selectRangeOnElement(unsigned location, unsigned length, Node& node)
2612 RefPtr<Range> resultRange = m_frame->document()->createRange();
2613 ExceptionCode ec = 0;
2614 resultRange->setStart(node, location, ec);
2616 resultRange->setEnd(node, location + length, ec);
2618 VisibleSelection selection = VisibleSelection(*resultRange, SEL_DEFAULT_AFFINITY);
2619 setSelection(selection, true);
2622 VisibleSelection FrameSelection::wordSelectionContainingCaretSelection(const VisibleSelection& selection)
2624 if (selection.isNone())
2625 return VisibleSelection();
2627 ASSERT(selection.isCaretOrRange());
2628 FrameSelection frameSelection;
2629 frameSelection.setSelection(selection);
2631 Position startPosBeforeExpansion(selection.start());
2632 Position endPosBeforeExpansion(selection.end());
2633 VisiblePosition startVisiblePosBeforeExpansion(startPosBeforeExpansion, VP_DEFAULT_AFFINITY);
2634 VisiblePosition endVisiblePosBeforeExpansion(endPosBeforeExpansion, VP_DEFAULT_AFFINITY);
2635 if (endVisiblePosBeforeExpansion.isNull())
2636 return VisibleSelection();
2638 if (isEndOfParagraph(endVisiblePosBeforeExpansion)) {
2639 UChar c(endVisiblePosBeforeExpansion.characterBefore());
2640 if (isSpaceOrNewline(c) || c == 0xA0) {
2641 // End of paragraph with space.
2642 return VisibleSelection();
2646 // If at end of paragraph, move backwards one character.
2647 // This has the effect of selecting the word on the line (which is
2648 // what we want, rather than selecting past the end of the line).
2649 if (isEndOfParagraph(endVisiblePosBeforeExpansion) && !isStartOfParagraph(endVisiblePosBeforeExpansion))
2650 frameSelection.modify(FrameSelection::AlterationMove, DirectionBackward, CharacterGranularity);
2652 VisibleSelection newSelection = frameSelection.selection();
2653 newSelection.expandUsingGranularity(WordGranularity);
2654 frameSelection.setSelection(newSelection, defaultSetSelectionOptions(), AXTextStateChangeIntent(), AlignCursorOnScrollIfNeeded, frameSelection.granularity());
2656 Position startPos(frameSelection.selection().start());
2657 Position endPos(frameSelection.selection().end());
2659 // Expansion cannot be allowed to change selection so that it is no longer
2660 // touches (or contains) the original, unexpanded selection.
2661 // Enforce this on the way into these additional calculations to give them
2662 // the best chance to yield a suitable answer.
2663 if (startPos > startPosBeforeExpansion)
2664 startPos = startPosBeforeExpansion;
2665 if (endPos < endPosBeforeExpansion)
2666 endPos = endPosBeforeExpansion;
2668 VisiblePosition startVisiblePos(startPos, VP_DEFAULT_AFFINITY);
2669 VisiblePosition endVisiblePos(endPos, VP_DEFAULT_AFFINITY);
2671 if (startVisiblePos.isNull() || endVisiblePos.isNull()) {
2672 // Start or end is nil
2673 return VisibleSelection();
2676 if (isEndOfLine(endVisiblePosBeforeExpansion)) {
2677 VisiblePosition previous(endVisiblePos.previous());
2678 if (previous == endVisiblePos) {
2680 return VisibleSelection();
2682 UChar c(previous.characterAfter());
2683 if (isSpaceOrNewline(c) || c == 0xA0) {
2684 // Space at end of line
2685 return VisibleSelection();
2689 // Expansion has selected past end of line.
2690 // Try repositioning backwards.
2691 if (isEndOfLine(startVisiblePos) && isStartOfLine(endVisiblePos)) {
2692 VisiblePosition previous(startVisiblePos.previous());
2693 if (isEndOfLine(previous)) {
2695 return VisibleSelection();
2697 UChar c(previous.characterAfter());
2698 if (isSpaceOrNewline(c) || c == 0xA0) {
2699 // Space at end of line
2700 return VisibleSelection();
2702 frameSelection.moveTo(startVisiblePos);
2703 frameSelection.modify(FrameSelection::AlterationExtend, DirectionBackward, WordGranularity);
2704 startPos = frameSelection.selection().start();
2705 endPos = frameSelection.selection().end();
2706 startVisiblePos = VisiblePosition(startPos, VP_DEFAULT_AFFINITY);
2707 endVisiblePos = VisiblePosition(endPos, VP_DEFAULT_AFFINITY);
2708 if (startVisiblePos.isNull() || endVisiblePos.isNull()) {
2709 // Start or end is nil
2710 return VisibleSelection();
2714 // Now loop backwards until we find a non-space.
2715 while (endVisiblePos != startVisiblePos) {
2716 VisiblePosition previous(endVisiblePos.previous());
2717 UChar c(previous.characterAfter());
2718 if (!isSpaceOrNewline(c) && c != 0xA0)
2720 endVisiblePos = previous;
2723 // Expansion cannot be allowed to change selection so that it is no longer
2724 // touches (or contains) the original, unexpanded selection.
2725 // Enforce this on the way out of the function to preserve the invariant.
2726 if (startVisiblePos > startVisiblePosBeforeExpansion)
2727 startVisiblePos = startVisiblePosBeforeExpansion;
2728 if (endVisiblePos < endVisiblePosBeforeExpansion)
2729 endVisiblePos = endVisiblePosBeforeExpansion;
2731 return VisibleSelection(startVisiblePos, endVisiblePos);
2734 bool FrameSelection::actualSelectionAtSentenceStart(const VisibleSelection& sel) const
2736 Position startPos(sel.start());
2737 VisiblePosition pos(createLegacyEditingPosition(startPos.deprecatedNode(), startPos.deprecatedEditingOffset()), VP_DEFAULT_AFFINITY);
2741 if (isStartOfParagraph(pos))
2745 bool sawSpace = false;
2746 unsigned previousCount = 0;
2747 for (pos = pos.previous(); !pos.isNull(); pos = pos.previous()) {
2749 if (isStartOfParagraph(pos)) {
2750 if (previousCount == 1 || (previousCount == 2 && sawSpace))
2754 UChar c(pos.characterAfter());
2756 if (isSpaceOrNewline(c) || c == 0xA0) {
2760 result = (c == '.' || c == '!' || c == '?');
2769 PassRefPtr<Range> FrameSelection::rangeByAlteringCurrentSelection(EAlteration alteration, int amount) const
2771 if (m_selection.isNone())
2775 return toNormalizedRange();
2777 FrameSelection frameSelection;
2778 frameSelection.setSelection(m_selection);
2779 SelectionDirection direction = amount > 0 ? DirectionForward : DirectionBackward;
2780 for (int i = 0; i < abs(amount); i++)
2781 frameSelection.modify(alteration, direction, CharacterGranularity);
2782 return frameSelection.toNormalizedRange();
2785 void FrameSelection::clearCurrentSelection()
2787 setSelection(VisibleSelection());
2790 void FrameSelection::setCaretBlinks(bool caretBlinks)
2792 if (m_caretBlinks == caretBlinks)
2794 #if ENABLE(TEXT_CARET)
2795 m_frame->document()->updateLayoutIgnorePendingStylesheets();
2797 m_caretPaint = false;
2798 invalidateCaretRect();
2802 setFocusedElementIfNeeded();
2803 m_caretBlinks = caretBlinks;
2807 void FrameSelection::setCaretColor(const Color& caretColor)
2809 if (m_caretColor != caretColor) {
2810 m_caretColor = caretColor;
2811 if (caretIsVisible() && m_caretBlinks && isCaret())
2812 invalidateCaretRect();
2815 #endif // PLATFORM(IOS)
2819 #if ENABLE(TREE_DEBUGGING)
2821 void showTree(const WebCore::FrameSelection& sel)
2823 sel.showTreeForThis();
2826 void showTree(const WebCore::FrameSelection* sel)
2829 sel->showTreeForThis();