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