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