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