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