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