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 "SelectionController.h"
28
29 #include "CharacterData.h"
30 #include "DeleteSelectionCommand.h"
31 #include "Document.h"
32 #include "Editor.h"
33 #include "EditorClient.h"
34 #include "Element.h"
35 #include "EventHandler.h"
36 #include "ExceptionCode.h"
37 #include "FloatQuad.h"
38 #include "FocusController.h"
39 #include "Frame.h"
40 #include "FrameTree.h"
41 #include "FrameView.h"
42 #include "GraphicsContext.h"
43 #include "HTMLFormElement.h"
44 #include "HTMLFrameElementBase.h"
45 #include "HTMLInputElement.h"
46 #include "HTMLNames.h"
47 #include "HitTestRequest.h"
48 #include "HitTestResult.h"
49 #include "Page.h"
50 #include "Range.h"
51 #include "RenderLayer.h"
52 #include "RenderTextControl.h"
53 #include "RenderTheme.h"
54 #include "RenderView.h"
55 #include "RenderWidget.h"
56 #include "SecureTextInput.h"
57 #include "Settings.h"
58 #include "TextIterator.h"
59 #include "TypingCommand.h"
60 #include "htmlediting.h"
61 #include "visible_units.h"
62 #ifdef ANDROID_ALLOW_TURNING_OFF_CARET
63 #include "WebViewCore.h"
64 #endif
65 #include <stdio.h>
66 #include <wtf/text/CString.h>
67
68 #define EDIT_DEBUG 0
69
70 namespace WebCore {
71
72 using namespace HTMLNames;
73
74 const int NoXPosForVerticalArrowNavigation = INT_MIN;
75
SelectionController(Frame * frame,bool isDragCaretController)76 SelectionController::SelectionController(Frame* frame, bool isDragCaretController)
77 : m_frame(frame)
78 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation)
79 , m_granularity(CharacterGranularity)
80 , m_caretBlinkTimer(this, &SelectionController::caretBlinkTimerFired)
81 , m_caretRectNeedsUpdate(true)
82 , m_absCaretBoundsDirty(true)
83 , m_isDragCaretController(isDragCaretController)
84 , m_isCaretBlinkingSuspended(false)
85 , m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame)
86 , m_caretVisible(isDragCaretController)
87 , m_caretPaint(true)
88 {
89 setIsDirectional(false);
90 }
91
moveTo(const VisiblePosition & pos,bool userTriggered,CursorAlignOnScroll align)92 void SelectionController::moveTo(const VisiblePosition &pos, bool userTriggered, CursorAlignOnScroll align)
93 {
94 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
95 if (userTriggered)
96 options |= UserTriggered;
97 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity()), options, align);
98 }
99
moveTo(const VisiblePosition & base,const VisiblePosition & extent,bool userTriggered)100 void SelectionController::moveTo(const VisiblePosition &base, const VisiblePosition &extent, bool userTriggered)
101 {
102 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
103 if (userTriggered)
104 options |= UserTriggered;
105 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity()), options);
106 }
107
moveTo(const Position & pos,EAffinity affinity,bool userTriggered)108 void SelectionController::moveTo(const Position &pos, EAffinity affinity, bool userTriggered)
109 {
110 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
111 if (userTriggered)
112 options |= UserTriggered;
113 setSelection(VisibleSelection(pos, affinity), options);
114 }
115
moveTo(const Range * r,EAffinity affinity,bool userTriggered)116 void SelectionController::moveTo(const Range *r, EAffinity affinity, bool userTriggered)
117 {
118 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
119 if (userTriggered)
120 options |= UserTriggered;
121 VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
122 setSelection(selection, options);
123 }
124
moveTo(const Position & base,const Position & extent,EAffinity affinity,bool userTriggered)125 void SelectionController::moveTo(const Position &base, const Position &extent, EAffinity affinity, bool userTriggered)
126 {
127 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
128 if (userTriggered)
129 options |= UserTriggered;
130 setSelection(VisibleSelection(base, extent, affinity), options);
131 }
132
setSelection(const VisibleSelection & s,SetSelectionOptions options,CursorAlignOnScroll align,TextGranularity granularity,DirectionalityPolicy directionalityPolicy)133 void SelectionController::setSelection(const VisibleSelection& s, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity, DirectionalityPolicy directionalityPolicy)
134 {
135 m_granularity = granularity;
136
137 bool closeTyping = options & CloseTyping;
138 bool shouldClearTypingStyle = options & ClearTypingStyle;
139 bool userTriggered = options & UserTriggered;
140
141 setIsDirectional(directionalityPolicy == MakeDirectionalSelection);
142
143 if (m_isDragCaretController) {
144 invalidateCaretRect();
145 m_selection = s;
146 m_caretRectNeedsUpdate = true;
147 invalidateCaretRect();
148 updateCaretRect();
149 return;
150 }
151 if (!m_frame) {
152 m_selection = s;
153 return;
154 }
155
156 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at SelectionController::setSelection
157 // if document->frame() == m_frame we can get into an infinite loop
158 if (s.base().anchorNode()) {
159 Document* document = s.base().anchorNode()->document();
160 if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
161 document->frame()->selection()->setSelection(s, options);
162 return;
163 }
164 }
165
166 if (closeTyping)
167 TypingCommand::closeTyping(m_frame->editor()->lastEditCommand());
168
169 if (shouldClearTypingStyle)
170 clearTypingStyle();
171
172 if (m_selection == s) {
173 // Even if selection was not changed, selection offsets may have been changed.
174 notifyRendererOfSelectionChange(userTriggered);
175 return;
176 }
177
178 VisibleSelection oldSelection = m_selection;
179
180 m_selection = s;
181
182 m_caretRectNeedsUpdate = true;
183
184 if (!s.isNone())
185 setFocusedNodeIfNeeded();
186
187 updateAppearance();
188
189 // Always clear the x position used for vertical arrow navigation.
190 // It will be restored by the vertical arrow navigation code if necessary.
191 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation;
192 selectFrameElementInParentIfFullySelected();
193 notifyRendererOfSelectionChange(userTriggered);
194 m_frame->editor()->respondToChangedSelection(oldSelection, options);
195 if (userTriggered) {
196 ScrollAlignment alignment;
197
198 if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
199 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
200 else
201 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
202
203 revealSelection(alignment, true);
204 }
205
206 notifyAccessibilityForSelectionChange();
207 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
208 }
209
removingNodeRemovesPosition(Node * node,const Position & position)210 static bool removingNodeRemovesPosition(Node* node, const Position& position)
211 {
212 if (!position.anchorNode())
213 return false;
214
215 if (position.anchorNode() == node)
216 return true;
217
218 if (!node->isElementNode())
219 return false;
220
221 Element* element = static_cast<Element*>(node);
222 return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
223 }
224
nodeWillBeRemoved(Node * node)225 void SelectionController::nodeWillBeRemoved(Node *node)
226 {
227 if (isNone())
228 return;
229
230 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
231 // the selection in the document that created the fragment needs no adjustment.
232 if (node && highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
233 return;
234
235 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
236 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
237 }
238
respondToNodeModification(Node * node,bool baseRemoved,bool extentRemoved,bool startRemoved,bool endRemoved)239 void SelectionController::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
240 {
241 bool clearRenderTreeSelection = false;
242 bool clearDOMTreeSelection = false;
243
244 if (startRemoved || endRemoved) {
245 // FIXME: When endpoints are removed, we should just alter the selection, instead of blowing it away.
246 clearRenderTreeSelection = true;
247 clearDOMTreeSelection = true;
248 } else if (baseRemoved || extentRemoved) {
249 // The base and/or extent are about to be removed, but the start and end aren't.
250 // Change the base and extent to the start and end, but don't re-validate the
251 // selection, since doing so could move the start and end into the node
252 // that is about to be removed.
253 if (m_selection.isBaseFirst())
254 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
255 else
256 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
257 } else if (RefPtr<Range> range = m_selection.firstRange()) {
258 ExceptionCode ec = 0;
259 Range::CompareResults compareResult = range->compareNode(node, ec);
260 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
261 // If we did nothing here, when this node's renderer was destroyed, the rect that it
262 // occupied would be invalidated, but, selection gaps that change as a result of
263 // the removal wouldn't be invalidated.
264 // FIXME: Don't do so much unnecessary invalidation.
265 clearRenderTreeSelection = true;
266 }
267 }
268
269 if (clearRenderTreeSelection) {
270 RefPtr<Document> document = m_selection.start().anchorNode()->document();
271 document->updateStyleIfNeeded();
272 if (RenderView* view = toRenderView(document->renderer()))
273 view->clearSelection();
274 }
275
276 if (clearDOMTreeSelection)
277 setSelection(VisibleSelection(), 0);
278 }
279
280 enum EndPointType { EndPointIsStart, EndPointIsEnd };
281
shouldRemovePositionAfterAdoptingTextReplacement(Position & position,EndPointType type,CharacterData * node,unsigned offset,unsigned oldLength,unsigned newLength)282 static bool shouldRemovePositionAfterAdoptingTextReplacement(Position& position, EndPointType type, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
283 {
284 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
285 return false;
286
287 ASSERT(position.offsetInContainerNode() >= 0);
288 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
289 if (positionOffset > offset && positionOffset < offset + oldLength)
290 return true;
291
292 // Adjust the offset if the position is after or at the end of the deleted contents (positionOffset >= offset + oldLength)
293 // to avoid having a stale offset except when the position is the end of selection and nothing is deleted, in which case,
294 // adjusting offset results in incorrectly extending the selection until the end of newly inserted contents.
295 if ((positionOffset > offset + oldLength) || (positionOffset == offset + oldLength && (type == EndPointIsStart || oldLength)))
296 position.moveToOffset(positionOffset - oldLength + newLength);
297
298 return false;
299 }
300
textWillBeReplaced(CharacterData * node,unsigned offset,unsigned oldLength,unsigned newLength)301 void SelectionController::textWillBeReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
302 {
303 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
304 if (isNone() || !node || highestAncestor(node)->nodeType() == Node::DOCUMENT_FRAGMENT_NODE)
305 return;
306
307 Position base = m_selection.base();
308 Position extent = m_selection.extent();
309 Position start = m_selection.start();
310 Position end = m_selection.end();
311 bool shouldRemoveBase = shouldRemovePositionAfterAdoptingTextReplacement(base, m_selection.isBaseFirst() ? EndPointIsStart : EndPointIsEnd, node, offset, oldLength, newLength);
312 bool shouldRemoveExtent = shouldRemovePositionAfterAdoptingTextReplacement(extent, m_selection.isBaseFirst() ? EndPointIsEnd : EndPointIsStart, node, offset, oldLength, newLength);
313 bool shouldRemoveStart = shouldRemovePositionAfterAdoptingTextReplacement(start, EndPointIsStart, node, offset, oldLength, newLength);
314 bool shouldRemoveEnd = shouldRemovePositionAfterAdoptingTextReplacement(end, EndPointIsEnd, node, offset, oldLength, newLength);
315
316 if ((base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end())
317 && !shouldRemoveStart && !shouldRemoveEnd) {
318 VisibleSelection newSelection;
319 if (!shouldRemoveBase && !shouldRemoveExtent)
320 newSelection.setWithoutValidation(base, extent);
321 else {
322 if (newSelection.isBaseFirst())
323 newSelection.setWithoutValidation(start, end);
324 else
325 newSelection.setWithoutValidation(end, start);
326 }
327 m_frame->document()->updateLayout();
328 setSelection(newSelection, 0);
329 return;
330 }
331
332 respondToNodeModification(node, shouldRemoveBase, shouldRemoveExtent, shouldRemoveStart, shouldRemoveEnd);
333 }
334
setIsDirectional(bool isDirectional)335 void SelectionController::setIsDirectional(bool isDirectional)
336 {
337 m_isDirectional = !m_frame || m_frame->editor()->behavior().shouldConsiderSelectionAsDirectional() || isDirectional;
338 }
339
directionOfEnclosingBlock()340 TextDirection SelectionController::directionOfEnclosingBlock()
341 {
342 return WebCore::directionOfEnclosingBlock(m_selection.extent());
343 }
344
willBeModified(EAlteration alter,SelectionDirection direction)345 void SelectionController::willBeModified(EAlteration alter, SelectionDirection direction)
346 {
347 if (alter != AlterationExtend)
348 return;
349
350 Position start = m_selection.start();
351 Position end = m_selection.end();
352
353 bool baseIsStart = true;
354
355 if (m_isDirectional) {
356 // Make base and extent match start and end so we extend the user-visible selection.
357 // This only matters for cases where base and extend point to different positions than
358 // start and end (e.g. after a double-click to select a word).
359 if (m_selection.isBaseFirst())
360 baseIsStart = true;
361 else
362 baseIsStart = false;
363 } else {
364 switch (direction) {
365 case DirectionRight:
366 if (directionOfEnclosingBlock() == LTR)
367 baseIsStart = true;
368 else
369 baseIsStart = false;
370 break;
371 case DirectionForward:
372 baseIsStart = true;
373 break;
374 case DirectionLeft:
375 if (directionOfEnclosingBlock() == LTR)
376 baseIsStart = false;
377 else
378 baseIsStart = true;
379 break;
380 case DirectionBackward:
381 baseIsStart = false;
382 break;
383 }
384 }
385 if (baseIsStart) {
386 m_selection.setBase(start);
387 m_selection.setExtent(end);
388 } else {
389 m_selection.setBase(end);
390 m_selection.setExtent(start);
391 }
392 }
393
positionForPlatform(bool isGetStart) const394 VisiblePosition SelectionController::positionForPlatform(bool isGetStart) const
395 {
396 Settings* settings = m_frame ? m_frame->settings() : 0;
397 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
398 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
399 // Linux and Windows always extend selections from the extent endpoint.
400 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
401 // base/extent always point to the same nodes as start/end, but which points
402 // to which depends on the value of isBaseFirst. Then this can be changed
403 // to just return m_sel.extent().
404 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
405 }
406
startForPlatform() const407 VisiblePosition SelectionController::startForPlatform() const
408 {
409 return positionForPlatform(true);
410 }
411
endForPlatform() const412 VisiblePosition SelectionController::endForPlatform() const
413 {
414 return positionForPlatform(false);
415 }
416
modifyExtendingRight(TextGranularity granularity)417 VisiblePosition SelectionController::modifyExtendingRight(TextGranularity granularity)
418 {
419 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
420
421 // The difference between modifyExtendingRight and modifyExtendingForward is:
422 // modifyExtendingForward always extends forward logically.
423 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
424 // it extends forward logically if the enclosing block is LTR direction,
425 // but it extends backward logically if the enclosing block is RTL direction.
426 switch (granularity) {
427 case CharacterGranularity:
428 if (directionOfEnclosingBlock() == LTR)
429 pos = pos.next(CannotCrossEditingBoundary);
430 else
431 pos = pos.previous(CannotCrossEditingBoundary);
432 break;
433 case WordGranularity:
434 if (directionOfEnclosingBlock() == LTR)
435 pos = nextWordPosition(pos);
436 else
437 pos = previousWordPosition(pos);
438 break;
439 case LineBoundary:
440 if (directionOfEnclosingBlock() == LTR)
441 pos = modifyExtendingForward(granularity);
442 else
443 pos = modifyExtendingBackward(granularity);
444 break;
445 case SentenceGranularity:
446 case LineGranularity:
447 case ParagraphGranularity:
448 case SentenceBoundary:
449 case ParagraphBoundary:
450 case DocumentBoundary:
451 // FIXME: implement all of the above?
452 pos = modifyExtendingForward(granularity);
453 break;
454 case WebKitVisualWordGranularity:
455 break;
456 }
457 return pos;
458 }
459
modifyExtendingForward(TextGranularity granularity)460 VisiblePosition SelectionController::modifyExtendingForward(TextGranularity granularity)
461 {
462 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
463 switch (granularity) {
464 case CharacterGranularity:
465 pos = pos.next(CannotCrossEditingBoundary);
466 break;
467 case WordGranularity:
468 pos = nextWordPosition(pos);
469 break;
470 case SentenceGranularity:
471 pos = nextSentencePosition(pos);
472 break;
473 case LineGranularity:
474 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
475 break;
476 case ParagraphGranularity:
477 pos = nextParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
478 break;
479 case SentenceBoundary:
480 pos = endOfSentence(endForPlatform());
481 break;
482 case LineBoundary:
483 pos = logicalEndOfLine(endForPlatform());
484 break;
485 case ParagraphBoundary:
486 pos = endOfParagraph(endForPlatform());
487 break;
488 case DocumentBoundary:
489 pos = endForPlatform();
490 if (isEditablePosition(pos.deepEquivalent()))
491 pos = endOfEditableContent(pos);
492 else
493 pos = endOfDocument(pos);
494 break;
495 case WebKitVisualWordGranularity:
496 break;
497 }
498
499 return pos;
500 }
501
modifyMovingRight(TextGranularity granularity)502 VisiblePosition SelectionController::modifyMovingRight(TextGranularity granularity)
503 {
504 VisiblePosition pos;
505 switch (granularity) {
506 case CharacterGranularity:
507 if (isRange()) {
508 if (directionOfEnclosingBlock() == LTR)
509 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
510 else
511 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
512 } else
513 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
514 break;
515 case WordGranularity:
516 case SentenceGranularity:
517 case LineGranularity:
518 case ParagraphGranularity:
519 case SentenceBoundary:
520 case ParagraphBoundary:
521 case DocumentBoundary:
522 // FIXME: Implement all of the above.
523 pos = modifyMovingForward(granularity);
524 break;
525 case LineBoundary:
526 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
527 break;
528 case WebKitVisualWordGranularity:
529 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
530 break;
531 }
532 return pos;
533 }
534
modifyMovingForward(TextGranularity granularity)535 VisiblePosition SelectionController::modifyMovingForward(TextGranularity granularity)
536 {
537 VisiblePosition pos;
538 // FIXME: Stay in editable content for the less common granularities.
539 switch (granularity) {
540 case CharacterGranularity:
541 if (isRange())
542 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
543 else
544 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
545 break;
546 case WordGranularity:
547 pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
548 break;
549 case SentenceGranularity:
550 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
551 break;
552 case LineGranularity: {
553 // down-arrowing from a range selection that ends at the start of a line needs
554 // to leave the selection at that line start (no need to call nextLinePosition!)
555 pos = endForPlatform();
556 if (!isRange() || !isStartOfLine(pos))
557 pos = nextLinePosition(pos, xPosForVerticalArrowNavigation(START));
558 break;
559 }
560 case ParagraphGranularity:
561 pos = nextParagraphPosition(endForPlatform(), xPosForVerticalArrowNavigation(START));
562 break;
563 case SentenceBoundary:
564 pos = endOfSentence(endForPlatform());
565 break;
566 case LineBoundary:
567 pos = logicalEndOfLine(endForPlatform());
568 break;
569 case ParagraphBoundary:
570 pos = endOfParagraph(endForPlatform());
571 break;
572 case DocumentBoundary:
573 pos = endForPlatform();
574 if (isEditablePosition(pos.deepEquivalent()))
575 pos = endOfEditableContent(pos);
576 else
577 pos = endOfDocument(pos);
578 break;
579 case WebKitVisualWordGranularity:
580 break;
581 }
582 return pos;
583 }
584
modifyExtendingLeft(TextGranularity granularity)585 VisiblePosition SelectionController::modifyExtendingLeft(TextGranularity granularity)
586 {
587 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
588
589 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
590 // modifyExtendingBackward always extends backward logically.
591 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
592 // it extends backward logically if the enclosing block is LTR direction,
593 // but it extends forward logically if the enclosing block is RTL direction.
594 switch (granularity) {
595 case CharacterGranularity:
596 if (directionOfEnclosingBlock() == LTR)
597 pos = pos.previous(CannotCrossEditingBoundary);
598 else
599 pos = pos.next(CannotCrossEditingBoundary);
600 break;
601 case WordGranularity:
602 if (directionOfEnclosingBlock() == LTR)
603 pos = previousWordPosition(pos);
604 else
605 pos = nextWordPosition(pos);
606 break;
607 case LineBoundary:
608 if (directionOfEnclosingBlock() == LTR)
609 pos = modifyExtendingBackward(granularity);
610 else
611 pos = modifyExtendingForward(granularity);
612 break;
613 case SentenceGranularity:
614 case LineGranularity:
615 case ParagraphGranularity:
616 case SentenceBoundary:
617 case ParagraphBoundary:
618 case DocumentBoundary:
619 pos = modifyExtendingBackward(granularity);
620 break;
621 case WebKitVisualWordGranularity:
622 break;
623 }
624 return pos;
625 }
626
modifyExtendingBackward(TextGranularity granularity)627 VisiblePosition SelectionController::modifyExtendingBackward(TextGranularity granularity)
628 {
629 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
630
631 // Extending a selection backward by word or character from just after a table selects
632 // the table. This "makes sense" from the user perspective, esp. when deleting.
633 // It was done here instead of in VisiblePosition because we want VPs to iterate
634 // over everything.
635 switch (granularity) {
636 case CharacterGranularity:
637 pos = pos.previous(CannotCrossEditingBoundary);
638 break;
639 case WordGranularity:
640 pos = previousWordPosition(pos);
641 break;
642 case SentenceGranularity:
643 pos = previousSentencePosition(pos);
644 break;
645 case LineGranularity:
646 pos = previousLinePosition(pos, xPosForVerticalArrowNavigation(EXTENT));
647 break;
648 case ParagraphGranularity:
649 pos = previousParagraphPosition(pos, xPosForVerticalArrowNavigation(EXTENT));
650 break;
651 case SentenceBoundary:
652 pos = startOfSentence(startForPlatform());
653 break;
654 case LineBoundary:
655 pos = logicalStartOfLine(startForPlatform());
656 break;
657 case ParagraphBoundary:
658 pos = startOfParagraph(startForPlatform());
659 break;
660 case DocumentBoundary:
661 pos = startForPlatform();
662 if (isEditablePosition(pos.deepEquivalent()))
663 pos = startOfEditableContent(pos);
664 else
665 pos = startOfDocument(pos);
666 break;
667 case WebKitVisualWordGranularity:
668 break;
669 }
670 return pos;
671 }
672
modifyMovingLeft(TextGranularity granularity)673 VisiblePosition SelectionController::modifyMovingLeft(TextGranularity granularity)
674 {
675 VisiblePosition pos;
676 switch (granularity) {
677 case CharacterGranularity:
678 if (isRange())
679 if (directionOfEnclosingBlock() == LTR)
680 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
681 else
682 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
683 else
684 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
685 break;
686 case WordGranularity:
687 case SentenceGranularity:
688 case LineGranularity:
689 case ParagraphGranularity:
690 case SentenceBoundary:
691 case ParagraphBoundary:
692 case DocumentBoundary:
693 // FIXME: Implement all of the above.
694 pos = modifyMovingBackward(granularity);
695 break;
696 case LineBoundary:
697 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
698 break;
699 case WebKitVisualWordGranularity:
700 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
701 break;
702 }
703 return pos;
704 }
705
modifyMovingBackward(TextGranularity granularity)706 VisiblePosition SelectionController::modifyMovingBackward(TextGranularity granularity)
707 {
708 VisiblePosition pos;
709 switch (granularity) {
710 case CharacterGranularity:
711 if (isRange())
712 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
713 else
714 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
715 break;
716 case WordGranularity:
717 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
718 break;
719 case SentenceGranularity:
720 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
721 break;
722 case LineGranularity:
723 pos = previousLinePosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
724 break;
725 case ParagraphGranularity:
726 pos = previousParagraphPosition(startForPlatform(), xPosForVerticalArrowNavigation(START));
727 break;
728 case SentenceBoundary:
729 pos = startOfSentence(startForPlatform());
730 break;
731 case LineBoundary:
732 pos = logicalStartOfLine(startForPlatform());
733 break;
734 case ParagraphBoundary:
735 pos = startOfParagraph(startForPlatform());
736 break;
737 case DocumentBoundary:
738 pos = startForPlatform();
739 if (isEditablePosition(pos.deepEquivalent()))
740 pos = startOfEditableContent(pos);
741 else
742 pos = startOfDocument(pos);
743 break;
744 case WebKitVisualWordGranularity:
745 break;
746 }
747 return pos;
748 }
749
isBoundary(TextGranularity granularity)750 static bool isBoundary(TextGranularity granularity)
751 {
752 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
753 }
754
modify(EAlteration alter,SelectionDirection direction,TextGranularity granularity,bool userTriggered)755 bool SelectionController::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, bool userTriggered)
756 {
757 if (userTriggered) {
758 SelectionController trialSelectionController;
759 trialSelectionController.setSelection(m_selection);
760 trialSelectionController.setIsDirectional(m_isDirectional);
761 trialSelectionController.modify(alter, direction, granularity, false);
762
763 bool change = shouldChangeSelection(trialSelectionController.selection());
764 if (!change)
765 return false;
766 }
767
768 willBeModified(alter, direction);
769
770 bool wasRange = m_selection.isRange();
771 Position originalStartPosition = m_selection.start();
772 VisiblePosition position;
773 switch (direction) {
774 case DirectionRight:
775 if (alter == AlterationMove)
776 position = modifyMovingRight(granularity);
777 else
778 position = modifyExtendingRight(granularity);
779 break;
780 case DirectionForward:
781 if (alter == AlterationExtend)
782 position = modifyExtendingForward(granularity);
783 else
784 position = modifyMovingForward(granularity);
785 break;
786 case DirectionLeft:
787 if (alter == AlterationMove)
788 position = modifyMovingLeft(granularity);
789 else
790 position = modifyExtendingLeft(granularity);
791 break;
792 case DirectionBackward:
793 if (alter == AlterationExtend)
794 position = modifyExtendingBackward(granularity);
795 else
796 position = modifyMovingBackward(granularity);
797 break;
798 }
799
800 if (position.isNull())
801 return false;
802
803 if (isSpatialNavigationEnabled(m_frame))
804 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
805 return false;
806
807 // Some of the above operations set an xPosForVerticalArrowNavigation.
808 // Setting a selection will clear it, so save it to possibly restore later.
809 // Note: the START position type is arbitrary because it is unused, it would be
810 // the requested position type if there were no xPosForVerticalArrowNavigation set.
811 int x = xPosForVerticalArrowNavigation(START);
812
813 switch (alter) {
814 case AlterationMove:
815 moveTo(position, userTriggered);
816 break;
817 case AlterationExtend:
818 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
819 // base in place and moving the extent. Matches NSTextView.
820 if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
821 setExtent(position, userTriggered);
822 else {
823 TextDirection textDirection = directionOfEnclosingBlock();
824 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
825 setEnd(position, userTriggered);
826 else
827 setStart(position, userTriggered);
828 }
829 break;
830 }
831
832 if (granularity == LineGranularity || granularity == ParagraphGranularity)
833 m_xPosForVerticalArrowNavigation = x;
834
835 if (userTriggered)
836 m_granularity = CharacterGranularity;
837
838
839 setCaretRectNeedsUpdate();
840
841 setIsDirectional(alter == AlterationExtend);
842
843 return true;
844 }
845
846 // FIXME: Maybe baseline would be better?
absoluteCaretY(const VisiblePosition & c,int & y)847 static bool absoluteCaretY(const VisiblePosition &c, int &y)
848 {
849 IntRect rect = c.absoluteCaretBounds();
850 if (rect.isEmpty())
851 return false;
852 y = rect.y() + rect.height() / 2;
853 return true;
854 }
855
modify(EAlteration alter,int verticalDistance,bool userTriggered,CursorAlignOnScroll align)856 bool SelectionController::modify(EAlteration alter, int verticalDistance, bool userTriggered, CursorAlignOnScroll align)
857 {
858 if (!verticalDistance)
859 return false;
860
861 if (userTriggered) {
862 SelectionController trialSelectionController;
863 trialSelectionController.setSelection(m_selection);
864 trialSelectionController.setIsDirectional(m_isDirectional);
865 trialSelectionController.modify(alter, verticalDistance, false);
866
867 bool change = shouldChangeSelection(trialSelectionController.selection());
868 if (!change)
869 return false;
870 }
871
872 bool up = verticalDistance < 0;
873 if (up)
874 verticalDistance = -verticalDistance;
875
876 willBeModified(alter, up ? DirectionBackward : DirectionForward);
877
878 VisiblePosition pos;
879 int xPos = 0;
880 switch (alter) {
881 case AlterationMove:
882 pos = VisiblePosition(up ? m_selection.start() : m_selection.end(), m_selection.affinity());
883 xPos = xPosForVerticalArrowNavigation(up ? START : END);
884 m_selection.setAffinity(up ? UPSTREAM : DOWNSTREAM);
885 break;
886 case AlterationExtend:
887 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
888 xPos = xPosForVerticalArrowNavigation(EXTENT);
889 m_selection.setAffinity(DOWNSTREAM);
890 break;
891 }
892
893 int startY;
894 if (!absoluteCaretY(pos, startY))
895 return false;
896 if (up)
897 startY = -startY;
898 int lastY = startY;
899
900 VisiblePosition result;
901 VisiblePosition next;
902 for (VisiblePosition p = pos; ; p = next) {
903 next = (up ? previousLinePosition : nextLinePosition)(p, xPos);
904 if (next.isNull() || next == p)
905 break;
906 int nextY;
907 if (!absoluteCaretY(next, nextY))
908 break;
909 if (up)
910 nextY = -nextY;
911 if (nextY - startY > verticalDistance)
912 break;
913 if (nextY >= lastY) {
914 lastY = nextY;
915 result = next;
916 }
917 }
918
919 if (result.isNull())
920 return false;
921
922 switch (alter) {
923 case AlterationMove:
924 moveTo(result, userTriggered, align);
925 break;
926 case AlterationExtend:
927 setExtent(result, userTriggered);
928 break;
929 }
930
931 if (userTriggered)
932 m_granularity = CharacterGranularity;
933
934 setIsDirectional(alter == AlterationExtend);
935
936 return true;
937 }
938
xPosForVerticalArrowNavigation(EPositionType type)939 int SelectionController::xPosForVerticalArrowNavigation(EPositionType type)
940 {
941 int x = 0;
942
943 if (isNone())
944 return x;
945
946 Position pos;
947 switch (type) {
948 case START:
949 pos = m_selection.start();
950 break;
951 case END:
952 pos = m_selection.end();
953 break;
954 case BASE:
955 pos = m_selection.base();
956 break;
957 case EXTENT:
958 pos = m_selection.extent();
959 break;
960 }
961
962 Frame* frame = pos.anchorNode()->document()->frame();
963 if (!frame)
964 return x;
965
966 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation) {
967 VisiblePosition visiblePosition(pos, m_selection.affinity());
968 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
969 // after the selection is created and before this function is called.
970 x = visiblePosition.isNotNull() ? visiblePosition.xOffsetForVerticalNavigation() : 0;
971 m_xPosForVerticalArrowNavigation = x;
972 } else
973 x = m_xPosForVerticalArrowNavigation;
974
975 return x;
976 }
977
clear()978 void SelectionController::clear()
979 {
980 m_granularity = CharacterGranularity;
981 setSelection(VisibleSelection());
982 }
983
setStart(const VisiblePosition & pos,bool userTriggered)984 void SelectionController::setStart(const VisiblePosition &pos, bool userTriggered)
985 {
986 if (m_selection.isBaseFirst())
987 setBase(pos, userTriggered);
988 else
989 setExtent(pos, userTriggered);
990 }
991
setEnd(const VisiblePosition & pos,bool userTriggered)992 void SelectionController::setEnd(const VisiblePosition &pos, bool userTriggered)
993 {
994 if (m_selection.isBaseFirst())
995 setExtent(pos, userTriggered);
996 else
997 setBase(pos, userTriggered);
998 }
999
setBase(const VisiblePosition & pos,bool userTriggered)1000 void SelectionController::setBase(const VisiblePosition &pos, bool userTriggered)
1001 {
1002 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
1003 if (userTriggered)
1004 options |= UserTriggered;
1005 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity()), options);
1006 }
1007
setExtent(const VisiblePosition & pos,bool userTriggered)1008 void SelectionController::setExtent(const VisiblePosition &pos, bool userTriggered)
1009 {
1010 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
1011 if (userTriggered)
1012 options |= UserTriggered;
1013 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity()), options);
1014 }
1015
setBase(const Position & pos,EAffinity affinity,bool userTriggered)1016 void SelectionController::setBase(const Position &pos, EAffinity affinity, bool userTriggered)
1017 {
1018 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
1019 if (userTriggered)
1020 options |= UserTriggered;
1021 setSelection(VisibleSelection(pos, m_selection.extent(), affinity), options);
1022 }
1023
setExtent(const Position & pos,EAffinity affinity,bool userTriggered)1024 void SelectionController::setExtent(const Position &pos, EAffinity affinity, bool userTriggered)
1025 {
1026 SetSelectionOptions options = CloseTyping | ClearTypingStyle;
1027 if (userTriggered)
1028 options |= UserTriggered;
1029 setSelection(VisibleSelection(m_selection.base(), pos, affinity), options);
1030 }
1031
setCaretRectNeedsUpdate(bool flag)1032 void SelectionController::setCaretRectNeedsUpdate(bool flag)
1033 {
1034 m_caretRectNeedsUpdate = flag;
1035 }
1036
updateCaretRect()1037 void SelectionController::updateCaretRect()
1038 {
1039 if (isNone() || !m_selection.start().anchorNode()->inDocument() || !m_selection.end().anchorNode()->inDocument()) {
1040 m_caretRect = IntRect();
1041 return;
1042 }
1043
1044 m_selection.start().anchorNode()->document()->updateStyleIfNeeded();
1045
1046 m_caretRect = IntRect();
1047
1048 if (isCaret()) {
1049 VisiblePosition pos(m_selection.start(), m_selection.affinity());
1050 if (pos.isNotNull()) {
1051 ASSERT(pos.deepEquivalent().deprecatedNode()->renderer());
1052
1053 // First compute a rect local to the renderer at the selection start
1054 RenderObject* renderer;
1055 IntRect localRect = pos.localCaretRect(renderer);
1056
1057 // Get the renderer that will be responsible for painting the caret (which
1058 // is either the renderer we just found, or one of its containers)
1059 RenderObject* caretPainter = caretRenderer();
1060
1061 // Compute an offset between the renderer and the caretPainter
1062 bool unrooted = false;
1063 while (renderer != caretPainter) {
1064 RenderObject* containerObject = renderer->container();
1065 if (!containerObject) {
1066 unrooted = true;
1067 break;
1068 }
1069 localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
1070 renderer = containerObject;
1071 }
1072
1073 if (!unrooted)
1074 m_caretRect = localRect;
1075
1076 m_absCaretBoundsDirty = true;
1077 }
1078 }
1079
1080 m_caretRectNeedsUpdate = false;
1081 }
1082
caretRenderer() const1083 RenderObject* SelectionController::caretRenderer() const
1084 {
1085 Node* node = m_selection.start().deprecatedNode();
1086 if (!node)
1087 return 0;
1088
1089 RenderObject* renderer = node->renderer();
1090 if (!renderer)
1091 return 0;
1092
1093 // if caretNode is a block and caret is inside it then caret should be painted by that block
1094 bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
1095 return paintedByBlock ? renderer : renderer->containingBlock();
1096 }
1097
localCaretRect()1098 IntRect SelectionController::localCaretRect()
1099 {
1100 if (m_caretRectNeedsUpdate)
1101 updateCaretRect();
1102
1103 return m_caretRect;
1104 }
1105
absoluteBoundsForLocalRect(const IntRect & rect) const1106 IntRect SelectionController::absoluteBoundsForLocalRect(const IntRect& rect) const
1107 {
1108 RenderObject* caretPainter = caretRenderer();
1109 if (!caretPainter)
1110 return IntRect();
1111
1112 IntRect localRect(rect);
1113 if (caretPainter->isBox())
1114 toRenderBox(caretPainter)->flipForWritingMode(localRect);
1115 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
1116 }
1117
absoluteCaretBounds()1118 IntRect SelectionController::absoluteCaretBounds()
1119 {
1120 recomputeCaretRect();
1121 return m_absCaretBounds;
1122 }
1123
repaintRectForCaret(IntRect caret)1124 static IntRect repaintRectForCaret(IntRect caret)
1125 {
1126 if (caret.isEmpty())
1127 return IntRect();
1128 // Ensure that the dirty rect intersects the block that paints the caret even in the case where
1129 // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
1130 caret.inflateX(1);
1131 return caret;
1132 }
1133
caretRepaintRect() const1134 IntRect SelectionController::caretRepaintRect() const
1135 {
1136 return absoluteBoundsForLocalRect(repaintRectForCaret(localCaretRectForPainting()));
1137 }
1138
recomputeCaretRect()1139 bool SelectionController::recomputeCaretRect()
1140 {
1141 if (!m_caretRectNeedsUpdate)
1142 return false;
1143
1144 if (!m_frame)
1145 return false;
1146
1147 FrameView* v = m_frame->document()->view();
1148 if (!v)
1149 return false;
1150
1151 IntRect oldRect = m_caretRect;
1152 IntRect newRect = localCaretRect();
1153 if (oldRect == newRect && !m_absCaretBoundsDirty)
1154 return false;
1155
1156 IntRect oldAbsCaretBounds = m_absCaretBounds;
1157 // FIXME: Rename m_caretRect to m_localCaretRect.
1158 m_absCaretBounds = absoluteBoundsForLocalRect(m_caretRect);
1159 m_absCaretBoundsDirty = false;
1160
1161 if (oldAbsCaretBounds == m_absCaretBounds)
1162 return false;
1163
1164 IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
1165 // We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
1166 m_absoluteCaretRepaintBounds = caretRepaintRect();
1167
1168 #if ENABLE(TEXT_CARET)
1169 if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
1170 // FIXME: make caret repainting container-aware.
1171 view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
1172 if (shouldRepaintCaret(view))
1173 view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
1174 }
1175 #endif
1176 return true;
1177 }
1178
shouldRepaintCaret(const RenderView * view) const1179 bool SelectionController::shouldRepaintCaret(const RenderView* view) const
1180 {
1181 ASSERT(view);
1182 Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
1183 bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
1184 return (caretBrowsing || isContentEditable());
1185 }
1186
invalidateCaretRect()1187 void SelectionController::invalidateCaretRect()
1188 {
1189 if (!isCaret())
1190 return;
1191
1192 Document* d = m_selection.start().anchorNode()->document();
1193
1194 // recomputeCaretRect will always return false for the drag caret,
1195 // because its m_frame is always 0.
1196 bool caretRectChanged = recomputeCaretRect();
1197
1198 // EDIT FIXME: This is an unfortunate hack.
1199 // Basically, we can't trust this layout position since we
1200 // can't guarantee that the check to see if we are in unrendered
1201 // content will work at this point. We may have to wait for
1202 // a layout and re-render of the document to happen. So, resetting this
1203 // flag will cause another caret layout to happen the first time
1204 // that we try to paint the caret after this call. That one will work since
1205 // it happens after the document has accounted for any editing
1206 // changes which may have been done.
1207 // And, we need to leave this layout here so the caret moves right
1208 // away after clicking.
1209 m_caretRectNeedsUpdate = true;
1210
1211 if (!caretRectChanged) {
1212 RenderView* view = toRenderView(d->renderer());
1213 if (view && shouldRepaintCaret(view))
1214 view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(), false);
1215 }
1216 }
1217
paintCaret(GraphicsContext * context,int tx,int ty,const IntRect & clipRect)1218 void SelectionController::paintCaret(GraphicsContext* context, int tx, int ty, const IntRect& clipRect)
1219 {
1220 #if ENABLE(TEXT_CARET)
1221 if (!m_caretVisible)
1222 return;
1223 if (!m_caretPaint)
1224 return;
1225 if (!m_selection.isCaret())
1226 return;
1227
1228 IntRect drawingRect = localCaretRectForPainting();
1229 if (caretRenderer() && caretRenderer()->isBox())
1230 toRenderBox(caretRenderer())->flipForWritingMode(drawingRect);
1231 drawingRect.move(tx, ty);
1232 IntRect caret = intersection(drawingRect, clipRect);
1233 if (caret.isEmpty())
1234 return;
1235
1236 Color caretColor = Color::black;
1237 ColorSpace colorSpace = ColorSpaceDeviceRGB;
1238 Element* element = rootEditableElement();
1239 if (element && element->renderer()) {
1240 caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
1241 colorSpace = element->renderer()->style()->colorSpace();
1242 }
1243
1244 context->fillRect(caret, caretColor, colorSpace);
1245 #else
1246 UNUSED_PARAM(context);
1247 UNUSED_PARAM(tx);
1248 UNUSED_PARAM(ty);
1249 UNUSED_PARAM(clipRect);
1250 #endif
1251 }
1252
debugRenderer(RenderObject * r,bool selected) const1253 void SelectionController::debugRenderer(RenderObject *r, bool selected) const
1254 {
1255 if (r->node()->isElementNode()) {
1256 Element* element = static_cast<Element *>(r->node());
1257 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
1258 } else if (r->isText()) {
1259 RenderText* textRenderer = toRenderText(r);
1260 if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
1261 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1262 return;
1263 }
1264
1265 static const int max = 36;
1266 String text = textRenderer->text();
1267 int textLength = text.length();
1268 if (selected) {
1269 int offset = 0;
1270 if (r->node() == m_selection.start().containerNode())
1271 offset = m_selection.start().computeOffsetInContainerNode();
1272 else if (r->node() == m_selection.end().containerNode())
1273 offset = m_selection.end().computeOffsetInContainerNode();
1274
1275 int pos;
1276 InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
1277 text = text.substring(box->start(), box->len());
1278
1279 String show;
1280 int mid = max / 2;
1281 int caret = 0;
1282
1283 // text is shorter than max
1284 if (textLength < max) {
1285 show = text;
1286 caret = pos;
1287 } else if (pos - mid < 0) {
1288 // too few characters to left
1289 show = text.left(max - 3) + "...";
1290 caret = pos;
1291 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1292 // enough characters on each side
1293 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1294 caret = mid;
1295 } else {
1296 // too few characters on right
1297 show = "..." + text.right(max - 3);
1298 caret = pos - (textLength - show.length());
1299 }
1300
1301 show.replace('\n', ' ');
1302 show.replace('\r', ' ');
1303 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1304 fprintf(stderr, " ");
1305 for (int i = 0; i < caret; i++)
1306 fprintf(stderr, " ");
1307 fprintf(stderr, "^\n");
1308 } else {
1309 if ((int)text.length() > max)
1310 text = text.left(max - 3) + "...";
1311 else
1312 text = text.left(max);
1313 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1314 }
1315 }
1316 }
1317
contains(const IntPoint & point)1318 bool SelectionController::contains(const IntPoint& point)
1319 {
1320 Document* document = m_frame->document();
1321
1322 // Treat a collapsed selection like no selection.
1323 if (!isRange())
1324 return false;
1325 if (!document->renderer())
1326 return false;
1327
1328 HitTestRequest request(HitTestRequest::ReadOnly |
1329 HitTestRequest::Active);
1330 HitTestResult result(point);
1331 document->renderView()->layer()->hitTest(request, result);
1332 Node* innerNode = result.innerNode();
1333 if (!innerNode || !innerNode->renderer())
1334 return false;
1335
1336 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1337 if (visiblePos.isNull())
1338 return false;
1339
1340 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1341 return false;
1342
1343 Position start(m_selection.visibleStart().deepEquivalent());
1344 Position end(m_selection.visibleEnd().deepEquivalent());
1345 Position p(visiblePos.deepEquivalent());
1346
1347 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1348 }
1349
1350 // Workaround for the fact that it's hard to delete a frame.
1351 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1352 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1353 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1354 // mouse or the keyboard after setting the selection.
selectFrameElementInParentIfFullySelected()1355 void SelectionController::selectFrameElementInParentIfFullySelected()
1356 {
1357 // Find the parent frame; if there is none, then we have nothing to do.
1358 Frame* parent = m_frame->tree()->parent();
1359 if (!parent)
1360 return;
1361 Page* page = m_frame->page();
1362 if (!page)
1363 return;
1364
1365 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1366 if (!isRange())
1367 return;
1368 if (!isStartOfDocument(selection().visibleStart()))
1369 return;
1370 if (!isEndOfDocument(selection().visibleEnd()))
1371 return;
1372
1373 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1374 Element* ownerElement = m_frame->ownerElement();
1375 if (!ownerElement)
1376 return;
1377 ContainerNode* ownerElementParent = ownerElement->parentNode();
1378 if (!ownerElementParent)
1379 return;
1380
1381 // 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.
1382 if (!ownerElementParent->rendererIsEditable())
1383 return;
1384
1385 // Create compute positions before and after the element.
1386 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1387 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1388 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1389
1390 // Focus on the parent frame, and then select from before this element to after.
1391 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1392 if (parent->selection()->shouldChangeSelection(newSelection)) {
1393 page->focusController()->setFocusedFrame(parent);
1394 parent->selection()->setSelection(newSelection);
1395 }
1396 }
1397
selectAll()1398 void SelectionController::selectAll()
1399 {
1400 Document* document = m_frame->document();
1401
1402 if (document->focusedNode() && document->focusedNode()->canSelectAll()) {
1403 document->focusedNode()->selectAll();
1404 return;
1405 }
1406
1407 Node* root = 0;
1408 if (isContentEditable())
1409 root = highestEditableRoot(m_selection.start());
1410 else {
1411 root = shadowTreeRootNode();
1412 if (!root)
1413 root = document->documentElement();
1414 }
1415 if (!root)
1416 return;
1417 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root));
1418 if (shouldChangeSelection(newSelection))
1419 setSelection(newSelection);
1420 selectFrameElementInParentIfFullySelected();
1421 notifyRendererOfSelectionChange(true);
1422 }
1423
setSelectedRange(Range * range,EAffinity affinity,bool closeTyping)1424 bool SelectionController::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1425 {
1426 if (!range)
1427 return false;
1428
1429 ExceptionCode ec = 0;
1430 Node* startContainer = range->startContainer(ec);
1431 if (ec)
1432 return false;
1433
1434 Node* endContainer = range->endContainer(ec);
1435 if (ec)
1436 return false;
1437
1438 ASSERT(startContainer);
1439 ASSERT(endContainer);
1440 ASSERT(startContainer->document() == endContainer->document());
1441
1442 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1443
1444 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1445 // they start at the beginning of the next line instead
1446 bool collapsed = range->collapsed(ec);
1447 if (ec)
1448 return false;
1449
1450 int startOffset = range->startOffset(ec);
1451 if (ec)
1452 return false;
1453
1454 int endOffset = range->endOffset(ec);
1455 if (ec)
1456 return false;
1457
1458 // FIXME: Can we provide extentAffinity?
1459 VisiblePosition visibleStart(Position(startContainer, startOffset, Position::PositionIsOffsetInAnchor), collapsed ? affinity : DOWNSTREAM);
1460 VisiblePosition visibleEnd(Position(endContainer, endOffset, Position::PositionIsOffsetInAnchor), SEL_DEFAULT_AFFINITY);
1461 SetSelectionOptions options = ClearTypingStyle;
1462 if (closeTyping)
1463 options |= CloseTyping;
1464 setSelection(VisibleSelection(visibleStart, visibleEnd), options);
1465 return true;
1466 }
1467
isInPasswordField() const1468 bool SelectionController::isInPasswordField() const
1469 {
1470 ASSERT(start().isNull() || start().anchorType() == Position::PositionIsOffsetInAnchor
1471 || start().containerNode() || !start().anchorNode()->shadowAncestorNode());
1472 Node* startNode = start().containerNode();
1473 if (!startNode)
1474 return false;
1475
1476 startNode = startNode->shadowAncestorNode();
1477 if (!startNode)
1478 return false;
1479
1480 if (!startNode->hasTagName(inputTag))
1481 return false;
1482
1483 return static_cast<HTMLInputElement*>(startNode)->isPasswordField();
1484 }
1485
caretRendersInsideNode(Node * node) const1486 bool SelectionController::caretRendersInsideNode(Node* node) const
1487 {
1488 if (!node)
1489 return false;
1490 return !isTableElement(node) && !editingIgnoresContent(node);
1491 }
1492
focusedOrActiveStateChanged()1493 void SelectionController::focusedOrActiveStateChanged()
1494 {
1495 bool activeAndFocused = isFocusedAndActive();
1496
1497 // Because RenderObject::selectionBackgroundColor() and
1498 // RenderObject::selectionForegroundColor() check if the frame is active,
1499 // we have to update places those colors were painted.
1500 if (RenderView* view = toRenderView(m_frame->document()->renderer()))
1501 view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
1502
1503 // Caret appears in the active frame.
1504 if (activeAndFocused)
1505 setSelectionFromNone();
1506 setCaretVisible(activeAndFocused);
1507
1508 // Update for caps lock state
1509 m_frame->eventHandler()->capsLockStateMayHaveChanged();
1510
1511 // Because CSSStyleSelector::checkOneSelector() and
1512 // RenderTheme::isFocused() check if the frame is active, we have to
1513 // update style and theme state that depended on those.
1514 if (Node* node = m_frame->document()->focusedNode()) {
1515 node->setNeedsStyleRecalc();
1516 if (RenderObject* renderer = node->renderer())
1517 if (renderer && renderer->style()->hasAppearance())
1518 renderer->theme()->stateChanged(renderer, FocusState);
1519 }
1520
1521 // Secure keyboard entry is set by the active frame.
1522 if (m_frame->document()->useSecureKeyboardEntryWhenActive())
1523 setUseSecureKeyboardEntry(activeAndFocused);
1524 }
1525
pageActivationChanged()1526 void SelectionController::pageActivationChanged()
1527 {
1528 focusedOrActiveStateChanged();
1529 }
1530
updateSecureKeyboardEntryIfActive()1531 void SelectionController::updateSecureKeyboardEntryIfActive()
1532 {
1533 if (m_frame->document() && isFocusedAndActive())
1534 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1535 }
1536
setUseSecureKeyboardEntry(bool enable)1537 void SelectionController::setUseSecureKeyboardEntry(bool enable)
1538 {
1539 if (enable)
1540 enableSecureTextInput();
1541 else
1542 disableSecureTextInput();
1543 }
1544
setFocused(bool flag)1545 void SelectionController::setFocused(bool flag)
1546 {
1547 if (m_focused == flag)
1548 return;
1549 m_focused = flag;
1550
1551 focusedOrActiveStateChanged();
1552 }
1553
isFocusedAndActive() const1554 bool SelectionController::isFocusedAndActive() const
1555 {
1556 return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
1557 }
1558
updateAppearance()1559 void SelectionController::updateAppearance()
1560 {
1561 ASSERT(!m_isDragCaretController);
1562
1563 #if ENABLE(TEXT_CARET)
1564 bool caretRectChanged = recomputeCaretRect();
1565
1566 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1567 bool shouldBlink = m_caretVisible
1568 && isCaret() && (isContentEditable() || caretBrowsing);
1569
1570 // If the caret moved, stop the blink timer so we can restart with a
1571 // black caret in the new location.
1572 if (caretRectChanged || !shouldBlink)
1573 m_caretBlinkTimer.stop();
1574
1575 // Start blinking with a black caret. Be sure not to restart if we're
1576 // already blinking in the right location.
1577 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1578 if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
1579 m_caretBlinkTimer.startRepeating(blinkInterval);
1580
1581 if (!m_caretPaint) {
1582 m_caretPaint = true;
1583 invalidateCaretRect();
1584 }
1585 }
1586 #endif
1587
1588 // We need to update style in case the node containing the selection is made display:none.
1589 m_frame->document()->updateStyleIfNeeded();
1590
1591 #if PLATFORM(ANDROID)
1592 return;
1593 #endif
1594
1595 RenderView* view = m_frame->contentRenderer();
1596 if (!view)
1597 return;
1598
1599 VisibleSelection selection = this->selection();
1600
1601 if (!selection.isRange()) {
1602 view->clearSelection();
1603 return;
1604 }
1605
1606 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1607 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1608 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1609 // and will fill the gap before 'bar'.
1610 Position startPos = selection.start();
1611 Position candidate = startPos.downstream();
1612 if (candidate.isCandidate())
1613 startPos = candidate;
1614 Position endPos = selection.end();
1615 candidate = endPos.upstream();
1616 if (candidate.isCandidate())
1617 endPos = candidate;
1618
1619 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1620 // because we don't yet notify the SelectionController of text removal.
1621 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1622 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1623 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1624 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1625 }
1626 }
1627
setCaretVisible(bool flag)1628 void SelectionController::setCaretVisible(bool flag)
1629 {
1630 if (m_caretVisible == flag)
1631 return;
1632 clearCaretRectIfNeeded();
1633 m_caretVisible = flag;
1634 updateAppearance();
1635 }
1636
clearCaretRectIfNeeded()1637 void SelectionController::clearCaretRectIfNeeded()
1638 {
1639 #if ENABLE(TEXT_CARET)
1640 if (!m_caretPaint)
1641 return;
1642 m_caretPaint = false;
1643 invalidateCaretRect();
1644 #endif
1645 }
1646
caretBlinkTimerFired(Timer<SelectionController> *)1647 void SelectionController::caretBlinkTimerFired(Timer<SelectionController>*)
1648 {
1649 #if ENABLE(TEXT_CARET)
1650 ASSERT(m_caretVisible);
1651 ASSERT(isCaret());
1652 bool caretPaint = m_caretPaint;
1653 if (isCaretBlinkingSuspended() && caretPaint)
1654 return;
1655 m_caretPaint = !caretPaint;
1656 invalidateCaretRect();
1657 #endif
1658 }
1659
notifyRendererOfSelectionChange(bool userTriggered)1660 void SelectionController::notifyRendererOfSelectionChange(bool userTriggered)
1661 {
1662 m_frame->document()->updateStyleIfNeeded();
1663
1664 if (!rootEditableElement())
1665 return;
1666
1667 RenderObject* renderer = rootEditableElement()->shadowAncestorNode()->renderer();
1668 if (!renderer || !renderer->isTextControl())
1669 return;
1670
1671 toRenderTextControl(renderer)->selectionChanged(userTriggered);
1672 }
1673
1674 // Helper function that tells whether a particular node is an element that has an entire
1675 // Frame and FrameView, a <frame>, <iframe>, or <object>.
isFrameElement(const Node * n)1676 static bool isFrameElement(const Node* n)
1677 {
1678 if (!n)
1679 return false;
1680 RenderObject* renderer = n->renderer();
1681 if (!renderer || !renderer->isWidget())
1682 return false;
1683 Widget* widget = toRenderWidget(renderer)->widget();
1684 return widget && widget->isFrameView();
1685 }
1686
setFocusedNodeIfNeeded()1687 void SelectionController::setFocusedNodeIfNeeded()
1688 {
1689 if (isNone() || !isFocused())
1690 return;
1691
1692 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1693 if (caretBrowsing) {
1694 if (Node* anchor = enclosingAnchorElement(base())) {
1695 m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
1696 return;
1697 }
1698 }
1699
1700 if (Node* target = rootEditableElement()) {
1701 // Walk up the DOM tree to search for a node to focus.
1702 while (target) {
1703 // We don't want to set focus on a subframe when selecting in a parent frame,
1704 // so add the !isFrameElement check here. There's probably a better way to make this
1705 // work in the long term, but this is the safest fix at this time.
1706 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
1707 m_frame->page()->focusController()->setFocusedNode(target, m_frame);
1708 return;
1709 }
1710 target = target->parentOrHostNode();
1711 }
1712 m_frame->document()->setFocusedNode(0);
1713 }
1714
1715 if (caretBrowsing)
1716 m_frame->page()->focusController()->setFocusedNode(0, m_frame);
1717 }
1718
paintDragCaret(GraphicsContext * p,int tx,int ty,const IntRect & clipRect) const1719 void SelectionController::paintDragCaret(GraphicsContext* p, int tx, int ty, const IntRect& clipRect) const
1720 {
1721 #if ENABLE(TEXT_CARET)
1722 SelectionController* dragCaretController = m_frame->page()->dragCaretController();
1723 ASSERT(dragCaretController->selection().isCaret());
1724 if (dragCaretController->selection().start().anchorNode()->document()->frame() == m_frame)
1725 dragCaretController->paintCaret(p, tx, ty, clipRect);
1726 #else
1727 UNUSED_PARAM(p);
1728 UNUSED_PARAM(tx);
1729 UNUSED_PARAM(ty);
1730 UNUSED_PARAM(clipRect);
1731 #endif
1732 }
1733
copyTypingStyle() const1734 PassRefPtr<CSSMutableStyleDeclaration> SelectionController::copyTypingStyle() const
1735 {
1736 if (!m_typingStyle || !m_typingStyle->style())
1737 return 0;
1738 return m_typingStyle->style()->copy();
1739 }
1740
shouldDeleteSelection(const VisibleSelection & selection) const1741 bool SelectionController::shouldDeleteSelection(const VisibleSelection& selection) const
1742 {
1743 return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
1744 }
1745
bounds(bool clipToVisibleContent) const1746 FloatRect SelectionController::bounds(bool clipToVisibleContent) const
1747 {
1748 RenderView* root = m_frame->contentRenderer();
1749 FrameView* view = m_frame->view();
1750 if (!root || !view)
1751 return IntRect();
1752
1753 IntRect selectionRect = root->selectionBounds(clipToVisibleContent);
1754 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1755 }
1756
getClippedVisibleTextRectangles(Vector<FloatRect> & rectangles) const1757 void SelectionController::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
1758 {
1759 RenderView* root = m_frame->contentRenderer();
1760 if (!root)
1761 return;
1762
1763 FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
1764
1765 Vector<FloatQuad> quads;
1766 toNormalizedRange()->textQuads(quads, true);
1767
1768 // FIXME: We are appending empty rectangles to the list for those that fall outside visibleContentRect.
1769 // It might be better to omit those rectangles entirely.
1770 size_t size = quads.size();
1771 for (size_t i = 0; i < size; ++i)
1772 rectangles.append(intersection(quads[i].enclosingBoundingBox(), visibleContentRect));
1773 }
1774
1775 // Scans logically forward from "start", including any child frames.
scanForForm(Node * start)1776 static HTMLFormElement* scanForForm(Node* start)
1777 {
1778 for (Node* node = start; node; node = node->traverseNextNode()) {
1779 if (node->hasTagName(formTag))
1780 return static_cast<HTMLFormElement*>(node);
1781 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1782 return static_cast<HTMLFormControlElement*>(node)->form();
1783 if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
1784 Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
1785 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1786 return frameResult;
1787 }
1788 }
1789 return 0;
1790 }
1791
1792 // We look for either the form containing the current focus, or for one immediately after it
currentForm() const1793 HTMLFormElement* SelectionController::currentForm() const
1794 {
1795 // Start looking either at the active (first responder) node, or where the selection is.
1796 Node* start = m_frame->document()->focusedNode();
1797 if (!start)
1798 start = this->start().deprecatedNode();
1799
1800 // Try walking up the node tree to find a form element.
1801 Node* node;
1802 for (node = start; node; node = node->parentNode()) {
1803 if (node->hasTagName(formTag))
1804 return static_cast<HTMLFormElement*>(node);
1805 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1806 return static_cast<HTMLFormControlElement*>(node)->form();
1807 }
1808
1809 // Try walking forward in the node tree to find a form element.
1810 return scanForForm(start);
1811 }
1812
revealSelection(const ScrollAlignment & alignment,bool revealExtent)1813 void SelectionController::revealSelection(const ScrollAlignment& alignment, bool revealExtent)
1814 {
1815 IntRect rect;
1816
1817 switch (selectionType()) {
1818 case VisibleSelection::NoSelection:
1819 return;
1820 case VisibleSelection::CaretSelection:
1821 rect = absoluteCaretBounds();
1822 break;
1823 case VisibleSelection::RangeSelection:
1824 rect = revealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1825 break;
1826 }
1827
1828 Position start = this->start();
1829 ASSERT(start.deprecatedNode());
1830 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1831 // FIXME: This code only handles scrolling the startContainer's layer, but
1832 // the selection rect could intersect more than just that.
1833 // See <rdar://problem/4799899>.
1834 if (RenderLayer* layer = start.deprecatedNode()->renderer()->enclosingLayer()) {
1835 layer->scrollRectToVisible(rect, false, alignment, alignment);
1836 updateAppearance();
1837 }
1838 }
1839 }
1840
setSelectionFromNone()1841 void SelectionController::setSelectionFromNone()
1842 {
1843 // Put a caret inside the body if the entire frame is editable (either the
1844 // entire WebView is editable or designMode is on for this document).
1845
1846 Document* document = m_frame->document();
1847 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1848 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1849 return;
1850
1851 Node* node = document->documentElement();
1852 while (node && !node->hasTagName(bodyTag))
1853 node = node->traverseNextNode();
1854 if (node)
1855 setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
1856 }
1857
shouldChangeSelection(const VisibleSelection & newSelection) const1858 bool SelectionController::shouldChangeSelection(const VisibleSelection& newSelection) const
1859 {
1860 return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
1861 }
1862
1863 #ifndef NDEBUG
1864
formatForDebugger(char * buffer,unsigned length) const1865 void SelectionController::formatForDebugger(char* buffer, unsigned length) const
1866 {
1867 m_selection.formatForDebugger(buffer, length);
1868 }
1869
showTreeForThis() const1870 void SelectionController::showTreeForThis() const
1871 {
1872 m_selection.showTreeForThis();
1873 }
1874
1875 #endif
1876
1877 }
1878
1879 #ifndef NDEBUG
1880
showTree(const WebCore::SelectionController & sel)1881 void showTree(const WebCore::SelectionController& sel)
1882 {
1883 sel.showTreeForThis();
1884 }
1885
showTree(const WebCore::SelectionController * sel)1886 void showTree(const WebCore::SelectionController* sel)
1887 {
1888 if (sel)
1889 sel->showTreeForThis();
1890 }
1891
1892 #endif
1893