• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 #include "core/editing/FrameSelection.h"
28 
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "core/HTMLNames.h"
31 #include "core/InputTypeNames.h"
32 #include "core/accessibility/AXObjectCache.h"
33 #include "core/css/StylePropertySet.h"
34 #include "core/dom/CharacterData.h"
35 #include "core/dom/Document.h"
36 #include "core/dom/Element.h"
37 #include "core/dom/ElementTraversal.h"
38 #include "core/dom/NodeTraversal.h"
39 #include "core/dom/Text.h"
40 #include "core/editing/Editor.h"
41 #include "core/editing/InputMethodController.h"
42 #include "core/editing/RenderedPosition.h"
43 #include "core/editing/SpellChecker.h"
44 #include "core/editing/TextIterator.h"
45 #include "core/editing/TypingCommand.h"
46 #include "core/editing/VisibleUnits.h"
47 #include "core/editing/htmlediting.h"
48 #include "core/events/Event.h"
49 #include "core/frame/LocalDOMWindow.h"
50 #include "core/frame/LocalFrame.h"
51 #include "core/html/HTMLBodyElement.h"
52 #include "core/html/HTMLFormElement.h"
53 #include "core/html/HTMLFrameElementBase.h"
54 #include "core/html/HTMLInputElement.h"
55 #include "core/html/HTMLSelectElement.h"
56 #include "core/page/EditorClient.h"
57 #include "core/page/EventHandler.h"
58 #include "core/page/FocusController.h"
59 #include "core/page/FrameTree.h"
60 #include "core/frame/FrameView.h"
61 #include "core/page/Page.h"
62 #include "core/frame/Settings.h"
63 #include "core/page/SpatialNavigation.h"
64 #include "core/rendering/HitTestRequest.h"
65 #include "core/rendering/HitTestResult.h"
66 #include "core/rendering/InlineTextBox.h"
67 #include "core/rendering/RenderLayer.h"
68 #include "core/rendering/RenderText.h"
69 #include "core/rendering/RenderTheme.h"
70 #include "core/rendering/RenderView.h"
71 #include "core/rendering/RenderWidget.h"
72 #include "platform/SecureTextInput.h"
73 #include "platform/geometry/FloatQuad.h"
74 #include "platform/graphics/GraphicsContext.h"
75 #include "wtf/text/CString.h"
76 #include <stdio.h>
77 
78 #define EDIT_DEBUG 0
79 
80 namespace blink {
81 
82 using namespace HTMLNames;
83 
NoXPosForVerticalArrowNavigation()84 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
85 {
86     return LayoutUnit::min();
87 }
88 
shouldAlwaysUseDirectionalSelection(LocalFrame * frame)89 static inline bool shouldAlwaysUseDirectionalSelection(LocalFrame* frame)
90 {
91     return !frame || frame->editor().behavior().shouldConsiderSelectionAsDirectional();
92 }
93 
FrameSelection(LocalFrame * frame)94 FrameSelection::FrameSelection(LocalFrame* frame)
95     : m_frame(frame)
96     , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
97     , m_observingVisibleSelection(false)
98     , m_granularity(CharacterGranularity)
99     , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
100     , m_caretRectDirty(true)
101     , m_shouldPaintCaret(true)
102     , m_isCaretBlinkingSuspended(false)
103     , m_focused(frame && frame->page() && frame->page()->focusController().focusedFrame() == frame)
104     , m_shouldShowBlockCursor(false)
105 {
106     if (shouldAlwaysUseDirectionalSelection(m_frame))
107         m_selection.setIsDirectional(true);
108 }
109 
~FrameSelection()110 FrameSelection::~FrameSelection()
111 {
112 #if !ENABLE(OILPAN)
113     // Oilpan: No need to clear out VisibleSelection observer;
114     // it is finalized as a part object of FrameSelection.
115     stopObservingVisibleSelectionChangeIfNecessary();
116 #endif
117 }
118 
rootEditableElementOrDocumentElement() const119 Element* FrameSelection::rootEditableElementOrDocumentElement() const
120 {
121     Element* selectionRoot = m_selection.rootEditableElement();
122     return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
123 }
124 
rootEditableElementOrTreeScopeRootNode() const125 ContainerNode* FrameSelection::rootEditableElementOrTreeScopeRootNode() const
126 {
127     Element* selectionRoot = m_selection.rootEditableElement();
128     if (selectionRoot)
129         return selectionRoot;
130 
131     Node* node = m_selection.base().containerNode();
132     return node ? &node->treeScope().rootNode() : 0;
133 }
134 
moveTo(const VisiblePosition & pos,EUserTriggered userTriggered,CursorAlignOnScroll align)135 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
136 {
137     SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
138     setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
139 }
140 
moveTo(const VisiblePosition & base,const VisiblePosition & extent,EUserTriggered userTriggered)141 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
142 {
143     const bool selectionHasDirection = true;
144     SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
145     setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
146 }
147 
moveTo(const Position & pos,EAffinity affinity,EUserTriggered userTriggered)148 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
149 {
150     SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
151     setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
152 }
153 
adjustEndpointsAtBidiBoundary(VisiblePosition & visibleBase,VisiblePosition & visibleExtent)154 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
155 {
156     RenderedPosition base(visibleBase);
157     RenderedPosition extent(visibleExtent);
158 
159     if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
160         return;
161 
162     if (base.atLeftBoundaryOfBidiRun()) {
163         if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
164             && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
165             visibleBase = VisiblePosition(base.positionAtLeftBoundaryOfBiDiRun());
166             return;
167         }
168         return;
169     }
170 
171     if (base.atRightBoundaryOfBidiRun()) {
172         if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
173             && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
174             visibleBase = VisiblePosition(base.positionAtRightBoundaryOfBiDiRun());
175             return;
176         }
177         return;
178     }
179 
180     if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
181         visibleExtent = VisiblePosition(extent.positionAtLeftBoundaryOfBiDiRun());
182         return;
183     }
184 
185     if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
186         visibleExtent = VisiblePosition(extent.positionAtRightBoundaryOfBiDiRun());
187         return;
188     }
189 }
190 
setNonDirectionalSelectionIfNeeded(const VisibleSelection & passedNewSelection,TextGranularity granularity,EndPointsAdjustmentMode endpointsAdjustmentMode)191 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
192     EndPointsAdjustmentMode endpointsAdjustmentMode)
193 {
194     VisibleSelection newSelection = passedNewSelection;
195     bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
196 
197     VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
198     VisiblePosition newBase = base;
199     VisiblePosition extent = newSelection.visibleExtent();
200     VisiblePosition newExtent = extent;
201     if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
202         adjustEndpointsAtBidiBoundary(newBase, newExtent);
203 
204     if (newBase != base || newExtent != extent) {
205         m_originalBase = base;
206         newSelection.setBase(newBase);
207         newSelection.setExtent(newExtent);
208     } else if (m_originalBase.isNotNull()) {
209         if (m_selection.base() == newSelection.base())
210             newSelection.setBase(m_originalBase);
211         m_originalBase.clear();
212     }
213 
214     newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
215     if (m_selection == newSelection)
216         return;
217 
218     setSelection(newSelection, granularity);
219 }
220 
setSelection(const VisibleSelection & newSelection,SetSelectionOptions options,CursorAlignOnScroll align,TextGranularity granularity)221 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
222 {
223     bool closeTyping = options & CloseTyping;
224     bool shouldClearTypingStyle = options & ClearTypingStyle;
225     EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
226 
227     VisibleSelection s = validateSelection(newSelection);
228     if (shouldAlwaysUseDirectionalSelection(m_frame))
229         s.setIsDirectional(true);
230 
231     if (!m_frame) {
232         m_selection = s;
233         return;
234     }
235 
236     // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
237     // if document->frame() == m_frame we can get into an infinite loop
238     if (s.base().anchorNode()) {
239         Document& document = *s.base().document();
240         if (document.frame() && document.frame() != m_frame && document != m_frame->document()) {
241             RefPtrWillBeRawPtr<LocalFrame> guard(document.frame());
242             document.frame()->selection().setSelection(s, options, align, granularity);
243             // It's possible that during the above set selection, this FrameSelection has been modified by
244             // selectFrameElementInParentIfFullySelected, but that the selection is no longer valid since
245             // the frame is about to be destroyed. If this is the case, clear our selection.
246             if (!guard->host() && !m_selection.isNonOrphanedCaretOrRange())
247                 clear();
248             return;
249         }
250     }
251 
252     m_granularity = granularity;
253 
254     if (closeTyping)
255         TypingCommand::closeTyping(m_frame);
256 
257     if (shouldClearTypingStyle)
258         clearTypingStyle();
259 
260     if (m_selection == s) {
261         // Even if selection was not changed, selection offsets may have been changed.
262         m_frame->inputMethodController().cancelCompositionIfSelectionIsInvalid();
263         notifyRendererOfSelectionChange(userTriggered);
264         return;
265     }
266 
267     VisibleSelection oldSelection = m_selection;
268 
269     m_selection = s;
270     setCaretRectNeedsUpdate();
271 
272     if (!s.isNone() && !(options & DoNotSetFocus))
273         setFocusedNodeIfNeeded();
274 
275     if (!(options & DoNotUpdateAppearance)) {
276         // Hits in compositing/overflow/do-not-paint-outline-into-composited-scrolling-contents.html
277         DisableCompositingQueryAsserts disabler;
278         updateAppearance(ResetCaretBlink);
279     }
280 
281     // Always clear the x position used for vertical arrow navigation.
282     // It will be restored by the vertical arrow navigation code if necessary.
283     m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
284     selectFrameElementInParentIfFullySelected();
285     notifyRendererOfSelectionChange(userTriggered);
286     m_frame->editor().respondToChangedSelection(oldSelection, options);
287     if (userTriggered == UserTriggered) {
288         ScrollAlignment alignment;
289 
290         if (m_frame->editor().behavior().shouldCenterAlignWhenSelectionIsRevealed())
291             alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
292         else
293             alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
294 
295         revealSelection(alignment, RevealExtent);
296     }
297 
298     notifyAccessibilityForSelectionChange();
299     notifyCompositorForSelectionChange();
300     notifyEventHandlerForSelectionChange();
301     m_frame->domWindow()->enqueueDocumentEvent(Event::create(EventTypeNames::selectionchange));
302 }
303 
removingNodeRemovesPosition(Node & node,const Position & position)304 static bool removingNodeRemovesPosition(Node& node, const Position& position)
305 {
306     if (!position.anchorNode())
307         return false;
308 
309     if (position.anchorNode() == node)
310         return true;
311 
312     if (!node.isElementNode())
313         return false;
314 
315     Element& element = toElement(node);
316     return element.containsIncludingShadowDOM(position.anchorNode());
317 }
318 
nodeWillBeRemoved(Node & node)319 void FrameSelection::nodeWillBeRemoved(Node& node)
320 {
321     // There can't be a selection inside a fragment, so if a fragment's node is being removed,
322     // the selection in the document that created the fragment needs no adjustment.
323     if (isNone() || !node.inActiveDocument())
324         return;
325 
326     respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
327         removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
328 }
329 
respondToNodeModification(Node & node,bool baseRemoved,bool extentRemoved,bool startRemoved,bool endRemoved)330 void FrameSelection::respondToNodeModification(Node& node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
331 {
332     ASSERT(node.document().isActive());
333 
334     bool clearRenderTreeSelection = false;
335     bool clearDOMTreeSelection = false;
336 
337     if (startRemoved || endRemoved) {
338         Position start = m_selection.start();
339         Position end = m_selection.end();
340         if (startRemoved)
341             updatePositionForNodeRemoval(start, node);
342         if (endRemoved)
343             updatePositionForNodeRemoval(end, node);
344 
345         if (start.isNotNull() && end.isNotNull()) {
346             if (m_selection.isBaseFirst())
347                 m_selection.setWithoutValidation(start, end);
348             else
349                 m_selection.setWithoutValidation(end, start);
350         } else
351             clearDOMTreeSelection = true;
352 
353         clearRenderTreeSelection = true;
354     } else if (baseRemoved || extentRemoved) {
355         // The base and/or extent are about to be removed, but the start and end aren't.
356         // Change the base and extent to the start and end, but don't re-validate the
357         // selection, since doing so could move the start and end into the node
358         // that is about to be removed.
359         if (m_selection.isBaseFirst())
360             m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
361         else
362             m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
363     } else if (m_selection.intersectsNode(&node)) {
364         // If we did nothing here, when this node's renderer was destroyed, the rect that it
365         // occupied would be invalidated, but, selection gaps that change as a result of
366         // the removal wouldn't be invalidated.
367         // FIXME: Don't do so much unnecessary invalidation.
368         clearRenderTreeSelection = true;
369     }
370 
371     if (clearRenderTreeSelection)
372         m_selection.start().document()->renderView()->clearSelection();
373 
374     if (clearDOMTreeSelection)
375         setSelection(VisibleSelection(), DoNotSetFocus);
376 }
377 
updatePositionAfterAdoptingTextReplacement(const Position & position,CharacterData * node,unsigned offset,unsigned oldLength,unsigned newLength)378 static Position updatePositionAfterAdoptingTextReplacement(const Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
379 {
380     if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
381         return position;
382 
383     // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
384     ASSERT(position.offsetInContainerNode() >= 0);
385     unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
386     // Replacing text can be viewed as a deletion followed by insertion.
387     if (positionOffset >= offset && positionOffset <= offset + oldLength)
388         positionOffset = offset;
389 
390     // Adjust the offset if the position is after the end of the deleted contents
391     // (positionOffset > offset + oldLength) to avoid having a stale offset.
392     if (positionOffset > offset + oldLength)
393         positionOffset = positionOffset - oldLength + newLength;
394 
395     ASSERT_WITH_SECURITY_IMPLICATION(positionOffset <= node->length());
396     // CharacterNode in VisibleSelection must be Text node, because Comment
397     // and ProcessingInstruction node aren't visible.
398     return Position(toText(node), positionOffset);
399 }
400 
didUpdateCharacterData(CharacterData * node,unsigned offset,unsigned oldLength,unsigned newLength)401 void FrameSelection::didUpdateCharacterData(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
402 {
403     // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
404     if (isNone() || !node || !node->inDocument())
405         return;
406 
407     Position base = updatePositionAfterAdoptingTextReplacement(m_selection.base(), node, offset, oldLength, newLength);
408     Position extent = updatePositionAfterAdoptingTextReplacement(m_selection.extent(), node, offset, oldLength, newLength);
409     Position start = updatePositionAfterAdoptingTextReplacement(m_selection.start(), node, offset, oldLength, newLength);
410     Position end = updatePositionAfterAdoptingTextReplacement(m_selection.end(), node, offset, oldLength, newLength);
411     updateSelectionIfNeeded(base, extent, start, end);
412 }
413 
updatePostionAfterAdoptingTextNodesMerged(const Position & position,const Text & oldNode,unsigned offset)414 static Position updatePostionAfterAdoptingTextNodesMerged(const Position& position, const Text& oldNode, unsigned offset)
415 {
416     if (!position.anchorNode() || position.anchorType() != Position::PositionIsOffsetInAnchor)
417         return position;
418 
419     ASSERT(position.offsetInContainerNode() >= 0);
420     unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
421 
422     if (position.anchorNode() == &oldNode)
423         return Position(toText(oldNode.previousSibling()), positionOffset + offset);
424 
425     if (position.anchorNode() == oldNode.parentNode() && positionOffset == offset)
426         return Position(toText(oldNode.previousSibling()), offset);
427 
428     return position;
429 }
430 
didMergeTextNodes(const Text & oldNode,unsigned offset)431 void FrameSelection::didMergeTextNodes(const Text& oldNode, unsigned offset)
432 {
433     if (isNone() || !oldNode.inDocument())
434         return;
435     Position base = updatePostionAfterAdoptingTextNodesMerged(m_selection.base(), oldNode, offset);
436     Position extent = updatePostionAfterAdoptingTextNodesMerged(m_selection.extent(), oldNode, offset);
437     Position start = updatePostionAfterAdoptingTextNodesMerged(m_selection.start(), oldNode, offset);
438     Position end = updatePostionAfterAdoptingTextNodesMerged(m_selection.end(), oldNode, offset);
439     updateSelectionIfNeeded(base, extent, start, end);
440 }
441 
updatePostionAfterAdoptingTextNodeSplit(const Position & position,const Text & oldNode)442 static Position updatePostionAfterAdoptingTextNodeSplit(const Position& position, const Text& oldNode)
443 {
444     if (!position.anchorNode() || position.anchorNode() != &oldNode || position.anchorType() != Position::PositionIsOffsetInAnchor)
445         return position;
446     // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
447     ASSERT(position.offsetInContainerNode() >= 0);
448     unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
449     unsigned oldLength = oldNode.length();
450     if (positionOffset <= oldLength)
451         return position;
452     return Position(toText(oldNode.nextSibling()), positionOffset - oldLength);
453 }
454 
didSplitTextNode(const Text & oldNode)455 void FrameSelection::didSplitTextNode(const Text& oldNode)
456 {
457     if (isNone() || !oldNode.inDocument())
458         return;
459     Position base = updatePostionAfterAdoptingTextNodeSplit(m_selection.base(), oldNode);
460     Position extent = updatePostionAfterAdoptingTextNodeSplit(m_selection.extent(), oldNode);
461     Position start = updatePostionAfterAdoptingTextNodeSplit(m_selection.start(), oldNode);
462     Position end = updatePostionAfterAdoptingTextNodeSplit(m_selection.end(), oldNode);
463     updateSelectionIfNeeded(base, extent, start, end);
464 }
465 
updateSelectionIfNeeded(const Position & base,const Position & extent,const Position & start,const Position & end)466 void FrameSelection::updateSelectionIfNeeded(const Position& base, const Position& extent, const Position& start, const Position& end)
467 {
468     if (base == m_selection.base() && extent == m_selection.extent() && start == m_selection.start() && end == m_selection.end())
469         return;
470     VisibleSelection newSelection;
471     if (m_selection.isBaseFirst())
472         newSelection.setWithoutValidation(start, end);
473     else
474         newSelection.setWithoutValidation(end, start);
475     setSelection(newSelection, DoNotSetFocus);
476 }
477 
directionOfEnclosingBlock()478 TextDirection FrameSelection::directionOfEnclosingBlock()
479 {
480     return blink::directionOfEnclosingBlock(m_selection.extent());
481 }
482 
directionOfSelection()483 TextDirection FrameSelection::directionOfSelection()
484 {
485     InlineBox* startBox = 0;
486     InlineBox* endBox = 0;
487     int unusedOffset;
488     // Cache the VisiblePositions because visibleStart() and visibleEnd()
489     // can cause layout, which has the potential to invalidate lineboxes.
490     VisiblePosition startPosition = m_selection.visibleStart();
491     VisiblePosition endPosition = m_selection.visibleEnd();
492     if (startPosition.isNotNull())
493         startPosition.getInlineBoxAndOffset(startBox, unusedOffset);
494     if (endPosition.isNotNull())
495         endPosition.getInlineBoxAndOffset(endBox, unusedOffset);
496     if (startBox && endBox && startBox->direction() == endBox->direction())
497         return startBox->direction();
498 
499     return directionOfEnclosingBlock();
500 }
501 
didChangeFocus()502 void FrameSelection::didChangeFocus()
503 {
504     // Hits in virtual/gpu/compositedscrolling/scrollbars/scrollbar-miss-mousemove-disabled.html
505     DisableCompositingQueryAsserts disabler;
506     updateAppearance();
507 }
508 
willBeModified(EAlteration alter,SelectionDirection direction)509 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
510 {
511     if (alter != AlterationExtend)
512         return;
513 
514     Position start = m_selection.start();
515     Position end = m_selection.end();
516 
517     bool baseIsStart = true;
518 
519     if (m_selection.isDirectional()) {
520         // Make base and extent match start and end so we extend the user-visible selection.
521         // This only matters for cases where base and extend point to different positions than
522         // start and end (e.g. after a double-click to select a word).
523         if (m_selection.isBaseFirst())
524             baseIsStart = true;
525         else
526             baseIsStart = false;
527     } else {
528         switch (direction) {
529         case DirectionRight:
530             if (directionOfSelection() == LTR)
531                 baseIsStart = true;
532             else
533                 baseIsStart = false;
534             break;
535         case DirectionForward:
536             baseIsStart = true;
537             break;
538         case DirectionLeft:
539             if (directionOfSelection() == LTR)
540                 baseIsStart = false;
541             else
542                 baseIsStart = true;
543             break;
544         case DirectionBackward:
545             baseIsStart = false;
546             break;
547         }
548     }
549     if (baseIsStart) {
550         m_selection.setBase(start);
551         m_selection.setExtent(end);
552     } else {
553         m_selection.setBase(end);
554         m_selection.setExtent(start);
555     }
556 }
557 
positionForPlatform(bool isGetStart) const558 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
559 {
560     Settings* settings = m_frame ? m_frame->settings() : 0;
561     if (settings && settings->editingBehaviorType() == EditingMacBehavior)
562         return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
563     // Linux and Windows always extend selections from the extent endpoint.
564     // FIXME: VisibleSelection should be fixed to ensure as an invariant that
565     // base/extent always point to the same nodes as start/end, but which points
566     // to which depends on the value of isBaseFirst. Then this can be changed
567     // to just return m_sel.extent().
568     return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
569 }
570 
startForPlatform() const571 VisiblePosition FrameSelection::startForPlatform() const
572 {
573     return positionForPlatform(true);
574 }
575 
endForPlatform() const576 VisiblePosition FrameSelection::endForPlatform() const
577 {
578     return positionForPlatform(false);
579 }
580 
nextWordPositionForPlatform(const VisiblePosition & originalPosition)581 VisiblePosition FrameSelection::nextWordPositionForPlatform(const VisiblePosition &originalPosition)
582 {
583     VisiblePosition positionAfterCurrentWord = nextWordPosition(originalPosition);
584 
585     if (m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight()) {
586         // In order to skip spaces when moving right, we advance one
587         // word further and then move one word back. Given the
588         // semantics of previousWordPosition() this will put us at the
589         // beginning of the word following.
590         VisiblePosition positionAfterSpacingAndFollowingWord = nextWordPosition(positionAfterCurrentWord);
591         if (positionAfterSpacingAndFollowingWord.isNotNull() && positionAfterSpacingAndFollowingWord != positionAfterCurrentWord)
592             positionAfterCurrentWord = previousWordPosition(positionAfterSpacingAndFollowingWord);
593 
594         bool movingBackwardsMovedPositionToStartOfCurrentWord = positionAfterCurrentWord == previousWordPosition(nextWordPosition(originalPosition));
595         if (movingBackwardsMovedPositionToStartOfCurrentWord)
596             positionAfterCurrentWord = positionAfterSpacingAndFollowingWord;
597     }
598     return positionAfterCurrentWord;
599 }
600 
adjustPositionForUserSelectAll(VisiblePosition & pos,bool isForward)601 static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
602 {
603     if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
604         pos = VisiblePosition(isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary));
605 }
606 
modifyExtendingRight(TextGranularity granularity)607 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
608 {
609     VisiblePosition pos(m_selection.extent(), m_selection.affinity());
610 
611     // The difference between modifyExtendingRight and modifyExtendingForward is:
612     // modifyExtendingForward always extends forward logically.
613     // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
614     // it extends forward logically if the enclosing block is LTR direction,
615     // but it extends backward logically if the enclosing block is RTL direction.
616     switch (granularity) {
617     case CharacterGranularity:
618         if (directionOfEnclosingBlock() == LTR)
619             pos = pos.next(CanSkipOverEditingBoundary);
620         else
621             pos = pos.previous(CanSkipOverEditingBoundary);
622         break;
623     case WordGranularity:
624         if (directionOfEnclosingBlock() == LTR)
625             pos = nextWordPositionForPlatform(pos);
626         else
627             pos = previousWordPosition(pos);
628         break;
629     case LineBoundary:
630         if (directionOfEnclosingBlock() == LTR)
631             pos = modifyExtendingForward(granularity);
632         else
633             pos = modifyExtendingBackward(granularity);
634         break;
635     case SentenceGranularity:
636     case LineGranularity:
637     case ParagraphGranularity:
638     case SentenceBoundary:
639     case ParagraphBoundary:
640     case DocumentBoundary:
641         // FIXME: implement all of the above?
642         pos = modifyExtendingForward(granularity);
643         break;
644     }
645     adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
646     return pos;
647 }
648 
modifyExtendingForward(TextGranularity granularity)649 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
650 {
651     VisiblePosition pos(m_selection.extent(), m_selection.affinity());
652     switch (granularity) {
653     case CharacterGranularity:
654         pos = pos.next(CanSkipOverEditingBoundary);
655         break;
656     case WordGranularity:
657         pos = nextWordPositionForPlatform(pos);
658         break;
659     case SentenceGranularity:
660         pos = nextSentencePosition(pos);
661         break;
662     case LineGranularity:
663         pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
664         break;
665     case ParagraphGranularity:
666         pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
667         break;
668     case SentenceBoundary:
669         pos = endOfSentence(endForPlatform());
670         break;
671     case LineBoundary:
672         pos = logicalEndOfLine(endForPlatform());
673         break;
674     case ParagraphBoundary:
675         pos = endOfParagraph(endForPlatform());
676         break;
677     case DocumentBoundary:
678         pos = endForPlatform();
679         if (isEditablePosition(pos.deepEquivalent()))
680             pos = endOfEditableContent(pos);
681         else
682             pos = endOfDocument(pos);
683         break;
684     }
685     adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
686     return pos;
687 }
688 
modifyMovingRight(TextGranularity granularity)689 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
690 {
691     VisiblePosition pos;
692     switch (granularity) {
693     case CharacterGranularity:
694         if (isRange()) {
695             if (directionOfSelection() == LTR)
696                 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
697             else
698                 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
699         } else
700             pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
701         break;
702     case WordGranularity: {
703         bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
704         pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
705         break;
706     }
707     case SentenceGranularity:
708     case LineGranularity:
709     case ParagraphGranularity:
710     case SentenceBoundary:
711     case ParagraphBoundary:
712     case DocumentBoundary:
713         // FIXME: Implement all of the above.
714         pos = modifyMovingForward(granularity);
715         break;
716     case LineBoundary:
717         pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
718         break;
719     }
720     return pos;
721 }
722 
modifyMovingForward(TextGranularity granularity)723 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
724 {
725     VisiblePosition pos;
726     // FIXME: Stay in editable content for the less common granularities.
727     switch (granularity) {
728     case CharacterGranularity:
729         if (isRange())
730             pos = VisiblePosition(m_selection.end(), m_selection.affinity());
731         else
732             pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CanSkipOverEditingBoundary);
733         break;
734     case WordGranularity:
735         pos = nextWordPositionForPlatform(VisiblePosition(m_selection.extent(), m_selection.affinity()));
736         break;
737     case SentenceGranularity:
738         pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
739         break;
740     case LineGranularity: {
741         // down-arrowing from a range selection that ends at the start of a line needs
742         // to leave the selection at that line start (no need to call nextLinePosition!)
743         pos = endForPlatform();
744         if (!isRange() || !isStartOfLine(pos))
745             pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
746         break;
747     }
748     case ParagraphGranularity:
749         pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
750         break;
751     case SentenceBoundary:
752         pos = endOfSentence(endForPlatform());
753         break;
754     case LineBoundary:
755         pos = logicalEndOfLine(endForPlatform());
756         break;
757     case ParagraphBoundary:
758         pos = endOfParagraph(endForPlatform());
759         break;
760     case DocumentBoundary:
761         pos = endForPlatform();
762         if (isEditablePosition(pos.deepEquivalent()))
763             pos = endOfEditableContent(pos);
764         else
765             pos = endOfDocument(pos);
766         break;
767     }
768     return pos;
769 }
770 
modifyExtendingLeft(TextGranularity granularity)771 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
772 {
773     VisiblePosition pos(m_selection.extent(), m_selection.affinity());
774 
775     // The difference between modifyExtendingLeft and modifyExtendingBackward is:
776     // modifyExtendingBackward always extends backward logically.
777     // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
778     // it extends backward logically if the enclosing block is LTR direction,
779     // but it extends forward logically if the enclosing block is RTL direction.
780     switch (granularity) {
781     case CharacterGranularity:
782         if (directionOfEnclosingBlock() == LTR)
783             pos = pos.previous(CanSkipOverEditingBoundary);
784         else
785             pos = pos.next(CanSkipOverEditingBoundary);
786         break;
787     case WordGranularity:
788         if (directionOfEnclosingBlock() == LTR)
789             pos = previousWordPosition(pos);
790         else
791             pos = nextWordPositionForPlatform(pos);
792         break;
793     case LineBoundary:
794         if (directionOfEnclosingBlock() == LTR)
795             pos = modifyExtendingBackward(granularity);
796         else
797             pos = modifyExtendingForward(granularity);
798         break;
799     case SentenceGranularity:
800     case LineGranularity:
801     case ParagraphGranularity:
802     case SentenceBoundary:
803     case ParagraphBoundary:
804     case DocumentBoundary:
805         pos = modifyExtendingBackward(granularity);
806         break;
807     }
808     adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
809     return pos;
810 }
811 
modifyExtendingBackward(TextGranularity granularity)812 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
813 {
814     VisiblePosition pos(m_selection.extent(), m_selection.affinity());
815 
816     // Extending a selection backward by word or character from just after a table selects
817     // the table.  This "makes sense" from the user perspective, esp. when deleting.
818     // It was done here instead of in VisiblePosition because we want VPs to iterate
819     // over everything.
820     switch (granularity) {
821     case CharacterGranularity:
822         pos = pos.previous(CanSkipOverEditingBoundary);
823         break;
824     case WordGranularity:
825         pos = previousWordPosition(pos);
826         break;
827     case SentenceGranularity:
828         pos = previousSentencePosition(pos);
829         break;
830     case LineGranularity:
831         pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
832         break;
833     case ParagraphGranularity:
834         pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
835         break;
836     case SentenceBoundary:
837         pos = startOfSentence(startForPlatform());
838         break;
839     case LineBoundary:
840         pos = logicalStartOfLine(startForPlatform());
841         break;
842     case ParagraphBoundary:
843         pos = startOfParagraph(startForPlatform());
844         break;
845     case DocumentBoundary:
846         pos = startForPlatform();
847         if (isEditablePosition(pos.deepEquivalent()))
848             pos = startOfEditableContent(pos);
849         else
850             pos = startOfDocument(pos);
851         break;
852     }
853     adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
854     return pos;
855 }
856 
modifyMovingLeft(TextGranularity granularity)857 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
858 {
859     VisiblePosition pos;
860     switch (granularity) {
861     case CharacterGranularity:
862         if (isRange())
863             if (directionOfSelection() == LTR)
864                 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
865             else
866                 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
867         else
868             pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
869         break;
870     case WordGranularity: {
871         bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor().behavior().shouldSkipSpaceWhenMovingRight();
872         pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
873         break;
874     }
875     case SentenceGranularity:
876     case LineGranularity:
877     case ParagraphGranularity:
878     case SentenceBoundary:
879     case ParagraphBoundary:
880     case DocumentBoundary:
881         // FIXME: Implement all of the above.
882         pos = modifyMovingBackward(granularity);
883         break;
884     case LineBoundary:
885         pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
886         break;
887     }
888     return pos;
889 }
890 
modifyMovingBackward(TextGranularity granularity)891 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
892 {
893     VisiblePosition pos;
894     switch (granularity) {
895     case CharacterGranularity:
896         if (isRange())
897             pos = VisiblePosition(m_selection.start(), m_selection.affinity());
898         else
899             pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CanSkipOverEditingBoundary);
900         break;
901     case WordGranularity:
902         pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
903         break;
904     case SentenceGranularity:
905         pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
906         break;
907     case LineGranularity:
908         pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
909         break;
910     case ParagraphGranularity:
911         pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
912         break;
913     case SentenceBoundary:
914         pos = startOfSentence(startForPlatform());
915         break;
916     case LineBoundary:
917         pos = logicalStartOfLine(startForPlatform());
918         break;
919     case ParagraphBoundary:
920         pos = startOfParagraph(startForPlatform());
921         break;
922     case DocumentBoundary:
923         pos = startForPlatform();
924         if (isEditablePosition(pos.deepEquivalent()))
925             pos = startOfEditableContent(pos);
926         else
927             pos = startOfDocument(pos);
928         break;
929     }
930     return pos;
931 }
932 
isBoundary(TextGranularity granularity)933 static bool isBoundary(TextGranularity granularity)
934 {
935     return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
936 }
937 
modify(EAlteration alter,SelectionDirection direction,TextGranularity granularity,EUserTriggered userTriggered)938 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
939 {
940     if (userTriggered == UserTriggered) {
941         OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
942         trialFrameSelection->setSelection(m_selection);
943         trialFrameSelection->modify(alter, direction, granularity, NotUserTriggered);
944 
945         if (trialFrameSelection->selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
946             return false;
947     }
948 
949     willBeModified(alter, direction);
950 
951     bool wasRange = m_selection.isRange();
952     VisiblePosition originalStartPosition = m_selection.visibleStart();
953     VisiblePosition position;
954     switch (direction) {
955     case DirectionRight:
956         if (alter == AlterationMove)
957             position = modifyMovingRight(granularity);
958         else
959             position = modifyExtendingRight(granularity);
960         break;
961     case DirectionForward:
962         if (alter == AlterationExtend)
963             position = modifyExtendingForward(granularity);
964         else
965             position = modifyMovingForward(granularity);
966         break;
967     case DirectionLeft:
968         if (alter == AlterationMove)
969             position = modifyMovingLeft(granularity);
970         else
971             position = modifyExtendingLeft(granularity);
972         break;
973     case DirectionBackward:
974         if (alter == AlterationExtend)
975             position = modifyExtendingBackward(granularity);
976         else
977             position = modifyMovingBackward(granularity);
978         break;
979     }
980 
981     if (position.isNull())
982         return false;
983 
984     if (isSpatialNavigationEnabled(m_frame))
985         if (!wasRange && alter == AlterationMove && position == originalStartPosition)
986             return false;
987 
988     // Some of the above operations set an xPosForVerticalArrowNavigation.
989     // Setting a selection will clear it, so save it to possibly restore later.
990     // Note: the START position type is arbitrary because it is unused, it would be
991     // the requested position type if there were no xPosForVerticalArrowNavigation set.
992     LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
993     m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
994 
995     switch (alter) {
996     case AlterationMove:
997         moveTo(position, userTriggered);
998         break;
999     case AlterationExtend:
1000 
1001         if (!m_selection.isCaret()
1002             && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
1003             && m_frame && !m_frame->editor().behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
1004             // Don't let the selection go across the base position directly. Needed to match mac
1005             // behavior when, for instance, word-selecting backwards starting with the caret in
1006             // the middle of a word and then word-selecting forward, leaving the caret in the
1007             // same place where it was, instead of directly selecting to the end of the word.
1008             VisibleSelection newSelection = m_selection;
1009             newSelection.setExtent(position);
1010             if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
1011                 position = m_selection.visibleBase();
1012         }
1013 
1014         // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1015         // base in place and moving the extent. Matches NSTextView.
1016         if (!m_frame || !m_frame->editor().behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1017             setExtent(position, userTriggered);
1018         else {
1019             TextDirection textDirection = directionOfEnclosingBlock();
1020             if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1021                 setEnd(position, userTriggered);
1022             else
1023                 setStart(position, userTriggered);
1024         }
1025         break;
1026     }
1027 
1028     if (granularity == LineGranularity || granularity == ParagraphGranularity)
1029         m_xPosForVerticalArrowNavigation = x;
1030 
1031     if (userTriggered == UserTriggered)
1032         m_granularity = CharacterGranularity;
1033 
1034     setCaretRectNeedsUpdate();
1035 
1036     return true;
1037 }
1038 
1039 // FIXME: Maybe baseline would be better?
absoluteCaretY(const VisiblePosition & c,int & y)1040 static bool absoluteCaretY(const VisiblePosition &c, int &y)
1041 {
1042     IntRect rect = c.absoluteCaretBounds();
1043     if (rect.isEmpty())
1044         return false;
1045     y = rect.y() + rect.height() / 2;
1046     return true;
1047 }
1048 
modify(EAlteration alter,unsigned verticalDistance,VerticalDirection direction,EUserTriggered userTriggered,CursorAlignOnScroll align)1049 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1050 {
1051     if (!verticalDistance)
1052         return false;
1053 
1054     if (userTriggered == UserTriggered) {
1055         OwnPtrWillBeRawPtr<FrameSelection> trialFrameSelection = FrameSelection::create();
1056         trialFrameSelection->setSelection(m_selection);
1057         trialFrameSelection->modify(alter, verticalDistance, direction, NotUserTriggered);
1058     }
1059 
1060     willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1061 
1062     VisiblePosition pos;
1063     LayoutUnit xPos = 0;
1064     switch (alter) {
1065     case AlterationMove:
1066         pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1067         xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1068         m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1069         break;
1070     case AlterationExtend:
1071         pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1072         xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1073         m_selection.setAffinity(DOWNSTREAM);
1074         break;
1075     }
1076 
1077     int startY;
1078     if (!absoluteCaretY(pos, startY))
1079         return false;
1080     if (direction == DirectionUp)
1081         startY = -startY;
1082     int lastY = startY;
1083 
1084     VisiblePosition result;
1085     VisiblePosition next;
1086     for (VisiblePosition p = pos; ; p = next) {
1087         if (direction == DirectionUp)
1088             next = previousLinePosition(p, xPos);
1089         else
1090             next = nextLinePosition(p, xPos);
1091 
1092         if (next.isNull() || next == p)
1093             break;
1094         int nextY;
1095         if (!absoluteCaretY(next, nextY))
1096             break;
1097         if (direction == DirectionUp)
1098             nextY = -nextY;
1099         if (nextY - startY > static_cast<int>(verticalDistance))
1100             break;
1101         if (nextY >= lastY) {
1102             lastY = nextY;
1103             result = next;
1104         }
1105     }
1106 
1107     if (result.isNull())
1108         return false;
1109 
1110     switch (alter) {
1111     case AlterationMove:
1112         moveTo(result, userTriggered, align);
1113         break;
1114     case AlterationExtend:
1115         setExtent(result, userTriggered);
1116         break;
1117     }
1118 
1119     if (userTriggered == UserTriggered)
1120         m_granularity = CharacterGranularity;
1121 
1122     m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1123 
1124     return true;
1125 }
1126 
lineDirectionPointForBlockDirectionNavigation(EPositionType type)1127 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1128 {
1129     LayoutUnit x = 0;
1130 
1131     if (isNone())
1132         return x;
1133 
1134     Position pos;
1135     switch (type) {
1136     case START:
1137         pos = m_selection.start();
1138         break;
1139     case END:
1140         pos = m_selection.end();
1141         break;
1142     case BASE:
1143         pos = m_selection.base();
1144         break;
1145     case EXTENT:
1146         pos = m_selection.extent();
1147         break;
1148     }
1149 
1150     LocalFrame* frame = pos.document()->frame();
1151     if (!frame)
1152         return x;
1153 
1154     if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1155         VisiblePosition visiblePosition(pos, m_selection.affinity());
1156         // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1157         // after the selection is created and before this function is called.
1158         x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1159         m_xPosForVerticalArrowNavigation = x;
1160     } else
1161         x = m_xPosForVerticalArrowNavigation;
1162 
1163     return x;
1164 }
1165 
clear()1166 void FrameSelection::clear()
1167 {
1168     m_granularity = CharacterGranularity;
1169     setSelection(VisibleSelection());
1170 }
1171 
prepareForDestruction()1172 void FrameSelection::prepareForDestruction()
1173 {
1174     m_granularity = CharacterGranularity;
1175 
1176     m_caretBlinkTimer.stop();
1177 
1178     RenderView* view = m_frame->contentRenderer();
1179     if (view)
1180         view->clearSelection();
1181 
1182     setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
1183     m_previousCaretNode.clear();
1184 }
1185 
setStart(const VisiblePosition & pos,EUserTriggered trigger)1186 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1187 {
1188     if (m_selection.isBaseFirst())
1189         setBase(pos, trigger);
1190     else
1191         setExtent(pos, trigger);
1192 }
1193 
setEnd(const VisiblePosition & pos,EUserTriggered trigger)1194 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1195 {
1196     if (m_selection.isBaseFirst())
1197         setExtent(pos, trigger);
1198     else
1199         setBase(pos, trigger);
1200 }
1201 
setBase(const VisiblePosition & pos,EUserTriggered userTriggered)1202 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1203 {
1204     const bool selectionHasDirection = true;
1205     setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1206 }
1207 
setExtent(const VisiblePosition & pos,EUserTriggered userTriggered)1208 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1209 {
1210     const bool selectionHasDirection = true;
1211     setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1212 }
1213 
caretRenderer() const1214 RenderBlock* FrameSelection::caretRenderer() const
1215 {
1216     return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1217 }
1218 
isNonOrphanedCaret(const VisibleSelection & selection)1219 static bool isNonOrphanedCaret(const VisibleSelection& selection)
1220 {
1221     return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1222 }
1223 
isTextFormControl(const VisibleSelection & selection)1224 static bool isTextFormControl(const VisibleSelection& selection)
1225 {
1226     return enclosingTextFormControl(selection.start());
1227 }
1228 
absoluteCaretBounds()1229 IntRect FrameSelection::absoluteCaretBounds()
1230 {
1231     ASSERT(m_frame->document()->lifecycle().state() != DocumentLifecycle::InPaintInvalidation);
1232     m_frame->document()->updateLayoutIgnorePendingStylesheets();
1233     if (!isNonOrphanedCaret(m_selection)) {
1234         clearCaretRect();
1235     } else {
1236         if (isTextFormControl(m_selection))
1237             updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start().isCandidate() ? m_selection.start() : Position(), m_selection.affinity()));
1238         else
1239             updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity()));
1240     }
1241     return absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1242 }
1243 
localCaretRect(const VisibleSelection & m_selection,const PositionWithAffinity & caretPosition,RenderObject * & renderer)1244 static LayoutRect localCaretRect(const VisibleSelection& m_selection, const PositionWithAffinity& caretPosition, RenderObject*& renderer)
1245 {
1246     renderer = nullptr;
1247     if (!isNonOrphanedCaret(m_selection))
1248         return LayoutRect();
1249 
1250     return localCaretRectOfPosition(caretPosition, renderer);
1251 }
1252 
invalidateCaretRect()1253 void FrameSelection::invalidateCaretRect()
1254 {
1255     if (!m_caretRectDirty)
1256         return;
1257     m_caretRectDirty = false;
1258 
1259     RenderObject* renderer = nullptr;
1260     LayoutRect newRect = localCaretRect(m_selection, PositionWithAffinity(m_selection.start(), m_selection.affinity()), renderer);
1261     Node* newNode = renderer ? renderer->node() : nullptr;
1262 
1263     if (!m_caretBlinkTimer.isActive() && newNode == m_previousCaretNode && newRect == m_previousCaretRect)
1264         return;
1265 
1266     RenderView* view = m_frame->document()->renderView();
1267     if (m_previousCaretNode && shouldRepaintCaret(view, m_previousCaretNode->isContentEditable()))
1268         invalidateLocalCaretRect(m_previousCaretNode.get(), m_previousCaretRect);
1269     if (newNode && shouldRepaintCaret(view, newNode->isContentEditable()))
1270         invalidateLocalCaretRect(newNode, newRect);
1271 
1272     m_previousCaretNode = newNode;
1273     m_previousCaretRect = newRect;
1274 }
1275 
paintCaret(GraphicsContext * context,const LayoutPoint & paintOffset,const LayoutRect & clipRect)1276 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1277 {
1278     if (m_selection.isCaret() && m_shouldPaintCaret) {
1279         updateCaretRect(m_frame->document(), PositionWithAffinity(m_selection.start(), m_selection.affinity()));
1280         CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1281     }
1282 }
1283 
contains(const LayoutPoint & point)1284 bool FrameSelection::contains(const LayoutPoint& point)
1285 {
1286     Document* document = m_frame->document();
1287 
1288     // Treat a collapsed selection like no selection.
1289     if (!isRange())
1290         return false;
1291     if (!document->renderView())
1292         return false;
1293 
1294     HitTestRequest request(HitTestRequest::ReadOnly | HitTestRequest::Active);
1295     HitTestResult result(point);
1296     document->renderView()->hitTest(request, result);
1297     Node* innerNode = result.innerNode();
1298     if (!innerNode || !innerNode->renderer())
1299         return false;
1300 
1301     VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1302     if (visiblePos.isNull())
1303         return false;
1304 
1305     if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1306         return false;
1307 
1308     Position start(m_selection.visibleStart().deepEquivalent());
1309     Position end(m_selection.visibleEnd().deepEquivalent());
1310     Position p(visiblePos.deepEquivalent());
1311 
1312     return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1313 }
1314 
1315 // Workaround for the fact that it's hard to delete a frame.
1316 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1317 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1318 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1319 // mouse or the keyboard after setting the selection.
selectFrameElementInParentIfFullySelected()1320 void FrameSelection::selectFrameElementInParentIfFullySelected()
1321 {
1322     // Find the parent frame; if there is none, then we have nothing to do.
1323     Frame* parent = m_frame->tree().parent();
1324     if (!parent)
1325         return;
1326     Page* page = m_frame->page();
1327     if (!page)
1328         return;
1329 
1330     // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1331     if (!isRange())
1332         return;
1333     if (!isStartOfDocument(selection().visibleStart()))
1334         return;
1335     if (!isEndOfDocument(selection().visibleEnd()))
1336         return;
1337 
1338     // FIXME: This is not yet implemented for cross-process frame relationships.
1339     if (!parent->isLocalFrame())
1340         return;
1341 
1342     // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1343     // FIXME: Doesn't work for OOPI.
1344     HTMLFrameOwnerElement* ownerElement = m_frame->deprecatedLocalOwner();
1345     if (!ownerElement)
1346         return;
1347     ContainerNode* ownerElementParent = ownerElement->parentNode();
1348     if (!ownerElementParent)
1349         return;
1350 
1351     // This method's purpose is it to make it easier to select iframes (in order to delete them).  Don't do anything if the iframe isn't deletable.
1352     if (!ownerElementParent->hasEditableStyle())
1353         return;
1354 
1355     // Create compute positions before and after the element.
1356     unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1357     VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1358     VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1359 
1360     // Focus on the parent frame, and then select from before this element to after.
1361     VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1362     page->focusController().setFocusedFrame(parent);
1363     toLocalFrame(parent)->selection().setSelection(newSelection);
1364 }
1365 
selectAll()1366 void FrameSelection::selectAll()
1367 {
1368     Document* document = m_frame->document();
1369 
1370     if (isHTMLSelectElement(document->focusedElement())) {
1371         HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedElement());
1372         if (selectElement->canSelectAll()) {
1373             selectElement->selectAll();
1374             return;
1375         }
1376     }
1377 
1378     RefPtrWillBeRawPtr<Node> root = nullptr;
1379     Node* selectStartTarget = 0;
1380     if (isContentEditable()) {
1381         root = highestEditableRoot(m_selection.start());
1382         if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1383             selectStartTarget = shadowRoot->shadowHost();
1384         else
1385             selectStartTarget = root.get();
1386     } else {
1387         root = m_selection.nonBoundaryShadowTreeRootNode();
1388         if (root)
1389             selectStartTarget = root->shadowHost();
1390         else {
1391             root = document->documentElement();
1392             selectStartTarget = document->body();
1393         }
1394     }
1395     if (!root)
1396         return;
1397 
1398     if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart)))
1399         return;
1400 
1401     VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1402     setSelection(newSelection);
1403     selectFrameElementInParentIfFullySelected();
1404     notifyRendererOfSelectionChange(UserTriggered);
1405 }
1406 
setSelectedRange(Range * range,EAffinity affinity,DirectoinalOption directional,SetSelectionOptions options)1407 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, DirectoinalOption directional, SetSelectionOptions options)
1408 {
1409     if (!range || !range->startContainer() || !range->endContainer())
1410         return false;
1411     ASSERT(range->startContainer()->document() == range->endContainer()->document());
1412 
1413     // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1414     // they start at the beginning of the next line instead
1415     m_logicalRange = nullptr;
1416     stopObservingVisibleSelectionChangeIfNecessary();
1417 
1418     VisibleSelection newSelection(range, affinity, directional == Directional);
1419     setSelection(newSelection, options);
1420 
1421     m_logicalRange = range->cloneRange();
1422     startObservingVisibleSelectionChange();
1423 
1424     return true;
1425 }
1426 
firstRange() const1427 PassRefPtrWillBeRawPtr<Range> FrameSelection::firstRange() const
1428 {
1429     if (m_logicalRange)
1430         return m_logicalRange->cloneRange();
1431     return m_selection.firstRange();
1432 }
1433 
isInPasswordField() const1434 bool FrameSelection::isInPasswordField() const
1435 {
1436     HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1437     return isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->type() == InputTypeNames::password;
1438 }
1439 
notifyAccessibilityForSelectionChange()1440 void FrameSelection::notifyAccessibilityForSelectionChange()
1441 {
1442     if (m_selection.start().isNotNull() && m_selection.end().isNotNull()) {
1443         if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
1444             cache->selectionChanged(m_selection.start().containerNode());
1445     }
1446 }
1447 
notifyCompositorForSelectionChange()1448 void FrameSelection::notifyCompositorForSelectionChange()
1449 {
1450     if (!RuntimeEnabledFeatures::compositedSelectionUpdateEnabled())
1451         return;
1452 
1453     scheduleVisualUpdate();
1454 }
1455 
notifyEventHandlerForSelectionChange()1456 void FrameSelection::notifyEventHandlerForSelectionChange()
1457 {
1458     m_frame->eventHandler().notifySelectionChanged();
1459 }
1460 
focusedOrActiveStateChanged()1461 void FrameSelection::focusedOrActiveStateChanged()
1462 {
1463     bool activeAndFocused = isFocusedAndActive();
1464 
1465     RefPtrWillBeRawPtr<Document> document = m_frame->document();
1466     document->updateRenderTreeIfNeeded();
1467 
1468     // Because RenderObject::selectionBackgroundColor() and
1469     // RenderObject::selectionForegroundColor() check if the frame is active,
1470     // we have to update places those colors were painted.
1471     if (RenderView* view = document->renderView())
1472         view->invalidatePaintForSelection();
1473 
1474     // Caret appears in the active frame.
1475     if (activeAndFocused)
1476         setSelectionFromNone();
1477     else
1478         m_frame->spellChecker().spellCheckAfterBlur();
1479     setCaretVisibility(activeAndFocused ? Visible : Hidden);
1480 
1481     // Update for caps lock state
1482     m_frame->eventHandler().capsLockStateMayHaveChanged();
1483 
1484     // We may have lost active status even though the focusElement hasn't changed
1485     // give the element a chance to recalc style if its affected by focus.
1486     if (Element* element = document->focusedElement())
1487         element->focusStateChanged();
1488 
1489     // Secure keyboard entry is set by the active frame.
1490     if (document->useSecureKeyboardEntryWhenActive())
1491         setUseSecureKeyboardEntry(activeAndFocused);
1492 }
1493 
pageActivationChanged()1494 void FrameSelection::pageActivationChanged()
1495 {
1496     focusedOrActiveStateChanged();
1497 }
1498 
updateSecureKeyboardEntryIfActive()1499 void FrameSelection::updateSecureKeyboardEntryIfActive()
1500 {
1501     if (m_frame->document() && isFocusedAndActive())
1502         setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1503 }
1504 
setUseSecureKeyboardEntry(bool enable)1505 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1506 {
1507     if (enable)
1508         enableSecureTextInput();
1509     else
1510         disableSecureTextInput();
1511 }
1512 
setFocused(bool flag)1513 void FrameSelection::setFocused(bool flag)
1514 {
1515     if (m_focused == flag)
1516         return;
1517     m_focused = flag;
1518 
1519     focusedOrActiveStateChanged();
1520 }
1521 
isFocusedAndActive() const1522 bool FrameSelection::isFocusedAndActive() const
1523 {
1524     return m_focused && m_frame->page() && m_frame->page()->focusController().isActive();
1525 }
1526 
shouldStopBlinkingDueToTypingCommand(LocalFrame * frame)1527 inline static bool shouldStopBlinkingDueToTypingCommand(LocalFrame* frame)
1528 {
1529     return frame->editor().lastEditCommand() && frame->editor().lastEditCommand()->shouldStopCaretBlinking();
1530 }
1531 
updateAppearance(ResetCaretBlinkOption option)1532 void FrameSelection::updateAppearance(ResetCaretBlinkOption option)
1533 {
1534     // Paint a block cursor instead of a caret in overtype mode unless the caret is at the end of a line (in this case
1535     // the FrameSelection will paint a blinking caret as usual).
1536     bool paintBlockCursor = m_shouldShowBlockCursor && m_selection.isCaret() && !isLogicalEndOfLine(m_selection.visibleEnd());
1537 
1538     bool shouldBlink = !paintBlockCursor && shouldBlinkCaret();
1539 
1540     bool willNeedCaretRectUpdate = false;
1541 
1542     // If the caret moved, stop the blink timer so we can restart with a
1543     // black caret in the new location.
1544     if (option == ResetCaretBlink || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame)) {
1545         m_caretBlinkTimer.stop();
1546 
1547         m_shouldPaintCaret = false;
1548         willNeedCaretRectUpdate = true;
1549     }
1550 
1551     // Start blinking with a black caret. Be sure not to restart if we're
1552     // already blinking in the right location.
1553     if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1554         if (double blinkInterval = RenderTheme::theme().caretBlinkInterval())
1555             m_caretBlinkTimer.startRepeating(blinkInterval, FROM_HERE);
1556 
1557         m_shouldPaintCaret = true;
1558         willNeedCaretRectUpdate = true;
1559     }
1560 
1561     if (willNeedCaretRectUpdate)
1562         setCaretRectNeedsUpdate();
1563 
1564     RenderView* view = m_frame->contentRenderer();
1565     if (!view)
1566         return;
1567 
1568     // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1569     // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1570 
1571     VisibleSelection selection;
1572     if (isTextFormControl(m_selection)) {
1573         Position endPosition = paintBlockCursor ? m_selection.extent().next() : m_selection.end();
1574         selection.setWithoutValidation(m_selection.start(), endPosition);
1575     } else {
1576         VisiblePosition endVisiblePosition = paintBlockCursor ? modifyExtendingForward(CharacterGranularity) : m_selection.visibleEnd();
1577         selection = VisibleSelection(m_selection.visibleStart(), endVisiblePosition);
1578     }
1579 
1580     if (!selection.isRange()) {
1581         view->clearSelection();
1582         return;
1583     }
1584 
1585     m_frame->document()->updateLayoutIgnorePendingStylesheets();
1586 
1587     // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1588     // Example: foo <a>bar</a>.  Imagine that a line wrap occurs after 'foo', and that 'bar' is selected.   If we pass [foo, 3]
1589     // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1590     // and will fill the gap before 'bar'.
1591     Position startPos = selection.start();
1592     Position candidate = startPos.downstream();
1593     if (candidate.isCandidate())
1594         startPos = candidate;
1595     Position endPos = selection.end();
1596     candidate = endPos.upstream();
1597     if (candidate.isCandidate())
1598         endPos = candidate;
1599 
1600     // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1601     // because we don't yet notify the FrameSelection of text removal.
1602     if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1603         RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1604         RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1605         if (startRenderer && endRenderer && startRenderer->view() == view && endRenderer->view() == view)
1606             view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1607     }
1608 }
1609 
setCaretVisibility(CaretVisibility visibility)1610 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1611 {
1612     if (caretVisibility() == visibility)
1613         return;
1614 
1615     CaretBase::setCaretVisibility(visibility);
1616 
1617     updateAppearance();
1618 }
1619 
shouldBlinkCaret() const1620 bool FrameSelection::shouldBlinkCaret() const
1621 {
1622     if (!caretIsVisible() || !isCaret())
1623         return false;
1624 
1625     if (m_frame->settings() && m_frame->settings()->caretBrowsingEnabled())
1626         return false;
1627 
1628     Element* root = rootEditableElement();
1629     if (!root)
1630         return false;
1631 
1632     Element* focusedElement = root->document().focusedElement();
1633     if (!focusedElement)
1634         return false;
1635 
1636     return focusedElement->containsIncludingShadowDOM(m_selection.start().anchorNode());
1637 }
1638 
caretBlinkTimerFired(Timer<FrameSelection> *)1639 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1640 {
1641     ASSERT(caretIsVisible());
1642     ASSERT(isCaret());
1643     if (isCaretBlinkingSuspended() && m_shouldPaintCaret)
1644         return;
1645     m_shouldPaintCaret = !m_shouldPaintCaret;
1646     setCaretRectNeedsUpdate();
1647 }
1648 
notifyRendererOfSelectionChange(EUserTriggered userTriggered)1649 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1650 {
1651     if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1652         textControl->selectionChanged(userTriggered == UserTriggered);
1653 }
1654 
1655 // Helper function that tells whether a particular node is an element that has an entire
1656 // LocalFrame and FrameView, a <frame>, <iframe>, or <object>.
isFrameElement(const Node * n)1657 static bool isFrameElement(const Node* n)
1658 {
1659     if (!n)
1660         return false;
1661     RenderObject* renderer = n->renderer();
1662     if (!renderer || !renderer->isWidget())
1663         return false;
1664     Widget* widget = toRenderWidget(renderer)->widget();
1665     return widget && widget->isFrameView();
1666 }
1667 
setFocusedNodeIfNeeded()1668 void FrameSelection::setFocusedNodeIfNeeded()
1669 {
1670     if (isNone() || !isFocused())
1671         return;
1672 
1673     bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1674     if (caretBrowsing) {
1675         if (Element* anchor = enclosingAnchorElement(base())) {
1676             m_frame->page()->focusController().setFocusedElement(anchor, m_frame);
1677             return;
1678         }
1679     }
1680 
1681     if (Element* target = rootEditableElement()) {
1682         // Walk up the DOM tree to search for a node to focus.
1683         while (target) {
1684             // We don't want to set focus on a subframe when selecting in a parent frame,
1685             // so add the !isFrameElement check here. There's probably a better way to make this
1686             // work in the long term, but this is the safest fix at this time.
1687             if (target->isMouseFocusable() && !isFrameElement(target)) {
1688                 m_frame->page()->focusController().setFocusedElement(target, m_frame);
1689                 return;
1690             }
1691             target = target->parentOrShadowHostElement();
1692         }
1693         m_frame->document()->setFocusedElement(nullptr);
1694     }
1695 
1696     if (caretBrowsing)
1697         m_frame->page()->focusController().setFocusedElement(0, m_frame);
1698 }
1699 
extractSelectedText(const FrameSelection & selection,TextIteratorBehavior behavior)1700 static String extractSelectedText(const FrameSelection& selection, TextIteratorBehavior behavior)
1701 {
1702     // We remove '\0' characters because they are not visibly rendered to the user.
1703     return plainText(selection.toNormalizedRange().get(), behavior).replace(0, "");
1704 }
1705 
selectedText() const1706 String FrameSelection::selectedText() const
1707 {
1708     return extractSelectedText(*this, TextIteratorDefaultBehavior);
1709 }
1710 
selectedTextForClipboard() const1711 String FrameSelection::selectedTextForClipboard() const
1712 {
1713     if (m_frame->settings() && m_frame->settings()->selectionIncludesAltImageText())
1714         return extractSelectedText(*this, TextIteratorEmitsImageAltText);
1715     return selectedText();
1716 }
1717 
bounds() const1718 FloatRect FrameSelection::bounds() const
1719 {
1720     m_frame->document()->updateRenderTreeIfNeeded();
1721 
1722     FrameView* view = m_frame->view();
1723     RenderView* renderView = m_frame->contentRenderer();
1724 
1725     if (!view || !renderView)
1726         return FloatRect();
1727 
1728     LayoutRect selectionRect = renderView->selectionBounds();
1729     return selectionRect;
1730 }
1731 
associatedFormElement(HTMLElement & element)1732 static inline HTMLFormElement* associatedFormElement(HTMLElement& element)
1733 {
1734     if (isHTMLFormElement(element))
1735         return &toHTMLFormElement(element);
1736     return element.formOwner();
1737 }
1738 
1739 // Scans logically forward from "start", including any child frames.
scanForForm(Node * start)1740 static HTMLFormElement* scanForForm(Node* start)
1741 {
1742     if (!start)
1743         return 0;
1744 
1745     HTMLElement* element = start->isHTMLElement() ? toHTMLElement(start) : Traversal<HTMLElement>::next(*start);
1746     for (; element; element = Traversal<HTMLElement>::next(*element)) {
1747         if (HTMLFormElement* form = associatedFormElement(*element))
1748             return form;
1749 
1750         if (isHTMLFrameElementBase(*element)) {
1751             Node* childDocument = toHTMLFrameElementBase(*element).contentDocument();
1752             if (HTMLFormElement* frameResult = scanForForm(childDocument))
1753                 return frameResult;
1754         }
1755     }
1756     return 0;
1757 }
1758 
1759 // We look for either the form containing the current focus, or for one immediately after it
currentForm() const1760 HTMLFormElement* FrameSelection::currentForm() const
1761 {
1762     // Start looking either at the active (first responder) node, or where the selection is.
1763     Node* start = m_frame->document()->focusedElement();
1764     if (!start)
1765         start = this->start().deprecatedNode();
1766     if (!start)
1767         return 0;
1768 
1769     // Try walking up the node tree to find a form element.
1770     for (HTMLElement* element = Traversal<HTMLElement>::firstAncestorOrSelf(*start); element; element = Traversal<HTMLElement>::firstAncestor(*element)) {
1771         if (HTMLFormElement* form = associatedFormElement(*element))
1772             return form;
1773     }
1774 
1775     // Try walking forward in the node tree to find a form element.
1776     return scanForForm(start);
1777 }
1778 
revealSelection(const ScrollAlignment & alignment,RevealExtentOption revealExtentOption)1779 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1780 {
1781     LayoutRect rect;
1782 
1783     switch (selectionType()) {
1784     case NoSelection:
1785         return;
1786     case CaretSelection:
1787         rect = absoluteCaretBounds();
1788         break;
1789     case RangeSelection:
1790         rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds());
1791         break;
1792     }
1793 
1794     Position start = this->start();
1795     ASSERT(start.deprecatedNode());
1796     if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1797         // FIXME: This code only handles scrolling the startContainer's layer, but
1798         // the selection rect could intersect more than just that.
1799         // See <rdar://problem/4799899>.
1800         if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1801             updateAppearance();
1802     }
1803 }
1804 
setSelectionFromNone()1805 void FrameSelection::setSelectionFromNone()
1806 {
1807     // Put a caret inside the body if the entire frame is editable (either the
1808     // entire WebView is editable or designMode is on for this document).
1809 
1810     Document* document = m_frame->document();
1811     bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1812     if (!isNone() || !(document->hasEditableStyle() || caretBrowsing))
1813         return;
1814 
1815     Element* documentElement = document->documentElement();
1816     if (!documentElement)
1817         return;
1818     if (HTMLBodyElement* body = Traversal<HTMLBodyElement>::firstChild(*documentElement))
1819         setSelection(VisibleSelection(firstPositionInOrBeforeNode(body), DOWNSTREAM));
1820 }
1821 
dispatchSelectStart()1822 bool FrameSelection::dispatchSelectStart()
1823 {
1824     Node* selectStartTarget = m_selection.extent().containerNode();
1825     if (!selectStartTarget)
1826         return true;
1827 
1828     return selectStartTarget->dispatchEvent(Event::createCancelableBubble(EventTypeNames::selectstart));
1829 }
1830 
setShouldShowBlockCursor(bool shouldShowBlockCursor)1831 void FrameSelection::setShouldShowBlockCursor(bool shouldShowBlockCursor)
1832 {
1833     m_shouldShowBlockCursor = shouldShowBlockCursor;
1834 
1835     m_frame->document()->updateLayoutIgnorePendingStylesheets();
1836 
1837     updateAppearance();
1838 }
1839 
didChangeVisibleSelection()1840 void FrameSelection::didChangeVisibleSelection()
1841 {
1842     ASSERT(m_observingVisibleSelection);
1843     // Invalidate the logical range when the underlying VisibleSelection has changed.
1844     m_logicalRange = nullptr;
1845     m_selection.clearChangeObserver();
1846     m_observingVisibleSelection = false;
1847 }
1848 
validateSelection(const VisibleSelection & selection)1849 VisibleSelection FrameSelection::validateSelection(const VisibleSelection& selection)
1850 {
1851     if (!m_frame || selection.isNone())
1852         return selection;
1853 
1854     Position base = selection.base();
1855     Position extent = selection.extent();
1856     bool isBaseValid = base.document() == m_frame->document();
1857     bool isExtentValid = extent.document() == m_frame->document();
1858 
1859     if (isBaseValid && isExtentValid)
1860         return selection;
1861 
1862     VisibleSelection newSelection;
1863     if (isBaseValid) {
1864         newSelection.setWithoutValidation(base, base);
1865     } else if (isExtentValid) {
1866         newSelection.setWithoutValidation(extent, extent);
1867     }
1868     return newSelection;
1869 }
1870 
startObservingVisibleSelectionChange()1871 void FrameSelection::startObservingVisibleSelectionChange()
1872 {
1873     ASSERT(!m_observingVisibleSelection);
1874     m_selection.setChangeObserver(*this);
1875     m_observingVisibleSelection = true;
1876 }
1877 
stopObservingVisibleSelectionChangeIfNecessary()1878 void FrameSelection::stopObservingVisibleSelectionChangeIfNecessary()
1879 {
1880     if (m_observingVisibleSelection) {
1881         m_selection.clearChangeObserver();
1882         m_observingVisibleSelection = false;
1883     }
1884 }
1885 
1886 #ifndef NDEBUG
1887 
formatForDebugger(char * buffer,unsigned length) const1888 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
1889 {
1890     m_selection.formatForDebugger(buffer, length);
1891 }
1892 
showTreeForThis() const1893 void FrameSelection::showTreeForThis() const
1894 {
1895     m_selection.showTreeForThis();
1896 }
1897 
1898 #endif
1899 
trace(Visitor * visitor)1900 void FrameSelection::trace(Visitor* visitor)
1901 {
1902     visitor->trace(m_frame);
1903     visitor->trace(m_selection);
1904     visitor->trace(m_originalBase);
1905     visitor->trace(m_logicalRange);
1906     visitor->trace(m_previousCaretNode);
1907     visitor->trace(m_typingStyle);
1908     VisibleSelection::ChangeObserver::trace(visitor);
1909 }
1910 
setCaretRectNeedsUpdate()1911 void FrameSelection::setCaretRectNeedsUpdate()
1912 {
1913     m_caretRectDirty = true;
1914 
1915     scheduleVisualUpdate();
1916 }
1917 
scheduleVisualUpdate() const1918 void FrameSelection::scheduleVisualUpdate() const
1919 {
1920     if (!m_frame)
1921         return;
1922     if (Page* page = m_frame->page())
1923         page->animator().scheduleVisualUpdate();
1924 }
1925 
1926 }
1927 
1928 #ifndef NDEBUG
1929 
showTree(const blink::FrameSelection & sel)1930 void showTree(const blink::FrameSelection& sel)
1931 {
1932     sel.showTreeForThis();
1933 }
1934 
showTree(const blink::FrameSelection * sel)1935 void showTree(const blink::FrameSelection* sel)
1936 {
1937     if (sel)
1938         sel->showTreeForThis();
1939 }
1940 
1941 #endif
1942