2 * Copyright (C) 2004, 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "FrameSelection.h"
29 #include "CharacterData.h"
30 #include "DeleteSelectionCommand.h"
33 #include "EditorClient.h"
35 #include "EventHandler.h"
36 #include "ExceptionCode.h"
37 #include "FloatQuad.h"
38 #include "FocusController.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 "HTMLSelectElement.h"
47 #include "HTMLNames.h"
48 #include "HitTestRequest.h"
49 #include "HitTestResult.h"
50 #include "InlineTextBox.h"
53 #include "RenderText.h"
54 #include "RenderTextControl.h"
55 #include "RenderTheme.h"
56 #include "RenderView.h"
57 #include "RenderWidget.h"
58 #include "RenderedPosition.h"
59 #include "SecureTextInput.h"
61 #include "SpatialNavigation.h"
62 #include "TextIterator.h"
63 #include "TypingCommand.h"
64 #include "htmlediting.h"
65 #include "visible_units.h"
68 #include <wtf/text/CString.h>
74 using namespace HTMLNames;
76 static inline LayoutUnit NoXPosForVerticalArrowNavigation()
78 return MIN_LAYOUT_UNIT;
81 CaretBase::CaretBase(CaretVisibility visibility)
82 : m_caretRectNeedsUpdate(true)
83 , m_caretVisibility(visibility)
87 DragCaretController::DragCaretController()
92 PassOwnPtr<DragCaretController> DragCaretController::create()
94 return adoptPtr(new DragCaretController);
97 bool DragCaretController::isContentRichlyEditable() const
99 return isRichlyEditablePosition(m_position.deepEquivalent());
102 static inline bool shouldAlwaysUseDirectionalSelection(Frame* frame)
104 return !frame || frame->editor()->behavior().shouldConsiderSelectionAsDirectional();
107 FrameSelection::FrameSelection(Frame* frame)
109 , m_xPosForVerticalArrowNavigation(NoXPosForVerticalArrowNavigation())
110 , m_granularity(CharacterGranularity)
111 , m_caretBlinkTimer(this, &FrameSelection::caretBlinkTimerFired)
112 , m_absCaretBoundsDirty(true)
114 , m_isCaretBlinkingSuspended(false)
115 , m_focused(frame && frame->page() && frame->page()->focusController()->focusedFrame() == frame)
117 if (shouldAlwaysUseDirectionalSelection(m_frame))
118 m_selection.setIsDirectional(true);
121 Element* FrameSelection::rootEditableElementOrDocumentElement() const
123 Element* selectionRoot = m_selection.rootEditableElement();
124 return selectionRoot ? selectionRoot : m_frame->document()->documentElement();
127 void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
129 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
130 setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
133 void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
135 const bool selectionHasDirection = true;
136 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
137 setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
140 void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
142 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
143 setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
146 void FrameSelection::moveTo(const Range *r, EAffinity affinity, EUserTriggered userTriggered)
148 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
149 VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
150 setSelection(selection, options);
153 void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
155 const bool selectionHasDirection = true;
156 SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
157 setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), options);
160 void DragCaretController::setCaretPosition(const VisiblePosition& position)
162 if (Node* node = m_position.deepEquivalent().deprecatedNode())
163 invalidateCaretRect(node);
164 m_position = position;
165 setCaretRectNeedsUpdate();
166 Document* document = 0;
167 if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
168 invalidateCaretRect(node);
169 document = node->document();
171 if (m_position.isNull() || m_position.isOrphan())
174 updateCaretRect(document, m_position);
177 static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
179 RenderedPosition base(visibleBase);
180 RenderedPosition extent(visibleExtent);
182 if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
185 if (base.atLeftBoundaryOfBidiRun()) {
186 if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
187 && base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
188 visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
194 if (base.atRightBoundaryOfBidiRun()) {
195 if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
196 && base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
197 visibleBase = base.positionAtRightBoundaryOfBiDiRun();
203 if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
204 visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
208 if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
209 visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
214 void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
215 EndPointsAdjustmentMode endpointsAdjustmentMode)
217 VisibleSelection newSelection = passedNewSelection;
218 bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
220 VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
221 VisiblePosition newBase = base;
222 VisiblePosition newExtent = newSelection.visibleExtent();
223 if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
224 adjustEndpointsAtBidiBoundary(newBase, newExtent);
226 if (newBase != base || newExtent != newSelection.visibleExtent()) {
227 m_originalBase = base;
228 newSelection.setBase(newBase);
229 newSelection.setExtent(newExtent);
230 } else if (m_originalBase.isNotNull()) {
231 if (m_selection.base() == newSelection.base())
232 newSelection.setBase(m_originalBase);
233 m_originalBase.clear();
236 newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
237 if (m_selection == newSelection || !shouldChangeSelection(newSelection))
240 setSelection(newSelection, granularity);
243 void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
245 bool closeTyping = options & CloseTyping;
246 bool shouldClearTypingStyle = options & ClearTypingStyle;
247 EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
249 VisibleSelection s = newSelection;
250 if (shouldAlwaysUseDirectionalSelection(m_frame))
251 s.setIsDirectional(true);
258 // <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
259 // if document->frame() == m_frame we can get into an infinite loop
260 if (s.base().anchorNode()) {
261 Document* document = s.base().anchorNode()->document();
262 if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
263 document->frame()->selection()->setSelection(s, options, align, granularity);
268 m_granularity = granularity;
271 TypingCommand::closeTyping(m_frame);
273 if (shouldClearTypingStyle)
276 if (m_selection == s) {
277 // Even if selection was not changed, selection offsets may have been changed.
278 notifyRendererOfSelectionChange(userTriggered);
282 VisibleSelection oldSelection = m_selection;
285 setCaretRectNeedsUpdate();
287 if (!s.isNone() && !(options & DoNotSetFocus))
288 setFocusedNodeIfNeeded();
292 // Always clear the x position used for vertical arrow navigation.
293 // It will be restored by the vertical arrow navigation code if necessary.
294 m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
295 selectFrameElementInParentIfFullySelected();
296 notifyRendererOfSelectionChange(userTriggered);
297 m_frame->editor()->respondToChangedSelection(oldSelection, options);
298 if (userTriggered == UserTriggered) {
299 ScrollAlignment alignment;
301 if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
302 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
304 alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
306 revealSelection(alignment, RevealExtent);
309 notifyAccessibilityForSelectionChange();
310 m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
313 static bool removingNodeRemovesPosition(Node* node, const Position& position)
315 if (!position.anchorNode())
318 if (position.anchorNode() == node)
321 if (!node->isElementNode())
324 Element* element = static_cast<Element*>(node);
325 return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
328 static void clearRenderViewSelection(const Position& position)
330 RefPtr<Document> document = position.anchorNode()->document();
331 document->updateStyleIfNeeded();
332 if (RenderView* view = toRenderView(document->renderer()))
333 view->clearSelection();
336 void DragCaretController::nodeWillBeRemoved(Node* node)
338 if (!hasCaret() || (node && !node->inDocument()))
341 if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
344 clearRenderViewSelection(m_position.deepEquivalent());
348 void FrameSelection::nodeWillBeRemoved(Node* node)
350 // There can't be a selection inside a fragment, so if a fragment's node is being removed,
351 // the selection in the document that created the fragment needs no adjustment.
352 if (isNone() || (node && !node->inDocument()))
355 respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
356 removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
359 void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
361 bool clearRenderTreeSelection = false;
362 bool clearDOMTreeSelection = false;
364 if (startRemoved || endRemoved) {
365 Position start = m_selection.start();
366 Position end = m_selection.end();
368 updatePositionForNodeRemoval(start, node);
370 updatePositionForNodeRemoval(end, node);
372 if (start.isNotNull() && end.isNotNull()) {
373 if (m_selection.isBaseFirst())
374 m_selection.setWithoutValidation(start, end);
376 m_selection.setWithoutValidation(end, start);
378 clearDOMTreeSelection = true;
380 clearRenderTreeSelection = true;
381 } else if (baseRemoved || extentRemoved) {
382 // The base and/or extent are about to be removed, but the start and end aren't.
383 // Change the base and extent to the start and end, but don't re-validate the
384 // selection, since doing so could move the start and end into the node
385 // that is about to be removed.
386 if (m_selection.isBaseFirst())
387 m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
389 m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
390 } else if (RefPtr<Range> range = m_selection.firstRange()) {
391 ExceptionCode ec = 0;
392 Range::CompareResults compareResult = range->compareNode(node, ec);
393 if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
394 // If we did nothing here, when this node's renderer was destroyed, the rect that it
395 // occupied would be invalidated, but, selection gaps that change as a result of
396 // the removal wouldn't be invalidated.
397 // FIXME: Don't do so much unnecessary invalidation.
398 clearRenderTreeSelection = true;
402 if (clearRenderTreeSelection)
403 clearRenderViewSelection(m_selection.start());
405 if (clearDOMTreeSelection)
406 setSelection(VisibleSelection(), DoNotSetFocus);
408 #if ENABLE(TIZEN_WEBKIT2_TEXT_SELECTION)
409 if(clearRenderTreeSelection && !clearDOMTreeSelection && (startRemoved || endRemoved))
410 m_frame->editor()->client()->respondToChangedSelection(m_frame);
414 static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
416 if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
419 // See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
420 ASSERT(position.offsetInContainerNode() >= 0);
421 unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
422 // Replacing text can be viewed as a deletion followed by insertion.
423 if (positionOffset >= offset && positionOffset <= offset + oldLength)
424 position.moveToOffset(offset);
426 // Adjust the offset if the position is after the end of the deleted contents
427 // (positionOffset > offset + oldLength) to avoid having a stale offset.
428 if (positionOffset > offset + oldLength)
429 position.moveToOffset(positionOffset - oldLength + newLength);
431 ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length());
434 static inline bool nodeIsDetachedFromDocument(Node* node)
437 Node* highest = highestAncestor(node);
438 return highest->nodeType() == Node::DOCUMENT_FRAGMENT_NODE && !highest->isShadowRoot();
441 void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
443 // The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
444 if (isNone() || !node || nodeIsDetachedFromDocument(node))
447 Position base = m_selection.base();
448 Position extent = m_selection.extent();
449 Position start = m_selection.start();
450 Position end = m_selection.end();
451 updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength);
452 updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength);
453 updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength);
454 updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength);
456 if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) {
457 VisibleSelection newSelection;
458 newSelection.setWithoutValidation(base, extent);
459 m_frame->document()->updateLayout();
460 setSelection(newSelection, DoNotSetFocus);
464 TextDirection FrameSelection::directionOfEnclosingBlock()
466 return WebCore::directionOfEnclosingBlock(m_selection.extent());
469 TextDirection FrameSelection::directionOfSelection()
471 InlineBox* startBox = 0;
472 InlineBox* endBox = 0;
474 if (m_selection.start().isNotNull())
475 m_selection.visibleStart().getInlineBoxAndOffset(startBox, unusedOffset);
476 if (m_selection.end().isNotNull())
477 m_selection.visibleEnd().getInlineBoxAndOffset(endBox, unusedOffset);
478 if (startBox && endBox && startBox->direction() == endBox->direction())
479 return startBox->direction();
481 return directionOfEnclosingBlock();
484 void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
486 if (alter != AlterationExtend)
489 Position start = m_selection.start();
490 Position end = m_selection.end();
492 bool baseIsStart = true;
494 if (m_selection.isDirectional()) {
495 // Make base and extent match start and end so we extend the user-visible selection.
496 // This only matters for cases where base and extend point to different positions than
497 // start and end (e.g. after a double-click to select a word).
498 if (m_selection.isBaseFirst())
505 if (directionOfSelection() == LTR)
510 case DirectionForward:
514 if (directionOfSelection() == LTR)
519 case DirectionBackward:
525 m_selection.setBase(start);
526 m_selection.setExtent(end);
528 m_selection.setBase(end);
529 m_selection.setExtent(start);
533 VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
535 Settings* settings = m_frame ? m_frame->settings() : 0;
536 if (settings && settings->editingBehaviorType() == EditingMacBehavior)
537 return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
538 // Linux and Windows always extend selections from the extent endpoint.
539 // FIXME: VisibleSelection should be fixed to ensure as an invariant that
540 // base/extent always point to the same nodes as start/end, but which points
541 // to which depends on the value of isBaseFirst. Then this can be changed
542 // to just return m_sel.extent().
543 return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
546 VisiblePosition FrameSelection::startForPlatform() const
548 return positionForPlatform(true);
551 VisiblePosition FrameSelection::endForPlatform() const
553 return positionForPlatform(false);
556 VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
558 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
560 // The difference between modifyExtendingRight and modifyExtendingForward is:
561 // modifyExtendingForward always extends forward logically.
562 // modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
563 // it extends forward logically if the enclosing block is LTR direction,
564 // but it extends backward logically if the enclosing block is RTL direction.
565 switch (granularity) {
566 case CharacterGranularity:
567 if (directionOfEnclosingBlock() == LTR)
568 pos = pos.next(CannotCrossEditingBoundary);
570 pos = pos.previous(CannotCrossEditingBoundary);
572 case WordGranularity:
573 if (directionOfEnclosingBlock() == LTR)
574 pos = nextWordPosition(pos);
576 pos = previousWordPosition(pos);
579 if (directionOfEnclosingBlock() == LTR)
580 pos = modifyExtendingForward(granularity);
582 pos = modifyExtendingBackward(granularity);
584 case SentenceGranularity:
585 case LineGranularity:
586 case ParagraphGranularity:
587 case SentenceBoundary:
588 case ParagraphBoundary:
589 case DocumentBoundary:
590 // FIXME: implement all of the above?
591 pos = modifyExtendingForward(granularity);
597 VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
599 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
600 switch (granularity) {
601 case CharacterGranularity:
602 pos = pos.next(CannotCrossEditingBoundary);
604 case WordGranularity:
605 pos = nextWordPosition(pos);
607 case SentenceGranularity:
608 pos = nextSentencePosition(pos);
610 case LineGranularity:
611 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
613 case ParagraphGranularity:
614 pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
616 case SentenceBoundary:
617 pos = endOfSentence(endForPlatform());
620 pos = logicalEndOfLine(endForPlatform());
622 case ParagraphBoundary:
623 pos = endOfParagraph(endForPlatform());
625 case DocumentBoundary:
626 pos = endForPlatform();
627 if (isEditablePosition(pos.deepEquivalent()))
628 pos = endOfEditableContent(pos);
630 pos = endOfDocument(pos);
637 VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
640 switch (granularity) {
641 case CharacterGranularity:
643 if (directionOfSelection() == LTR)
644 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
646 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
648 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
650 case WordGranularity: {
652 // Visual word movement relies on isWordTextBreak which is not implemented in WinCE and QT.
653 // https://bugs.webkit.org/show_bug.cgi?id=81136.
654 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
655 pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
659 case SentenceGranularity:
660 case LineGranularity:
661 case ParagraphGranularity:
662 case SentenceBoundary:
663 case ParagraphBoundary:
664 case DocumentBoundary:
665 // FIXME: Implement all of the above.
666 pos = modifyMovingForward(granularity);
669 pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
675 VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
678 // FIXME: Stay in editable content for the less common granularities.
679 switch (granularity) {
680 case CharacterGranularity:
682 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
684 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
686 case WordGranularity:
687 pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
689 case SentenceGranularity:
690 pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
692 case LineGranularity: {
693 // down-arrowing from a range selection that ends at the start of a line needs
694 // to leave the selection at that line start (no need to call nextLinePosition!)
695 pos = endForPlatform();
696 if (!isRange() || !isStartOfLine(pos))
697 pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
700 case ParagraphGranularity:
701 pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
703 case SentenceBoundary:
704 pos = endOfSentence(endForPlatform());
707 pos = logicalEndOfLine(endForPlatform());
709 case ParagraphBoundary:
710 pos = endOfParagraph(endForPlatform());
712 case DocumentBoundary:
713 pos = endForPlatform();
714 if (isEditablePosition(pos.deepEquivalent()))
715 pos = endOfEditableContent(pos);
717 pos = endOfDocument(pos);
723 VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
725 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
727 // The difference between modifyExtendingLeft and modifyExtendingBackward is:
728 // modifyExtendingBackward always extends backward logically.
729 // modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
730 // it extends backward logically if the enclosing block is LTR direction,
731 // but it extends forward logically if the enclosing block is RTL direction.
732 switch (granularity) {
733 case CharacterGranularity:
734 if (directionOfEnclosingBlock() == LTR)
735 pos = pos.previous(CannotCrossEditingBoundary);
737 pos = pos.next(CannotCrossEditingBoundary);
739 case WordGranularity:
740 if (directionOfEnclosingBlock() == LTR)
741 pos = previousWordPosition(pos);
743 pos = nextWordPosition(pos);
746 if (directionOfEnclosingBlock() == LTR)
747 pos = modifyExtendingBackward(granularity);
749 pos = modifyExtendingForward(granularity);
751 case SentenceGranularity:
752 case LineGranularity:
753 case ParagraphGranularity:
754 case SentenceBoundary:
755 case ParagraphBoundary:
756 case DocumentBoundary:
757 pos = modifyExtendingBackward(granularity);
763 VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
765 VisiblePosition pos(m_selection.extent(), m_selection.affinity());
767 // Extending a selection backward by word or character from just after a table selects
768 // the table. This "makes sense" from the user perspective, esp. when deleting.
769 // It was done here instead of in VisiblePosition because we want VPs to iterate
771 switch (granularity) {
772 case CharacterGranularity:
773 pos = pos.previous(CannotCrossEditingBoundary);
775 case WordGranularity:
776 pos = previousWordPosition(pos);
778 case SentenceGranularity:
779 pos = previousSentencePosition(pos);
781 case LineGranularity:
782 pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
784 case ParagraphGranularity:
785 pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
787 case SentenceBoundary:
788 pos = startOfSentence(startForPlatform());
791 pos = logicalStartOfLine(startForPlatform());
793 case ParagraphBoundary:
794 pos = startOfParagraph(startForPlatform());
796 case DocumentBoundary:
797 pos = startForPlatform();
798 if (isEditablePosition(pos.deepEquivalent()))
799 pos = startOfEditableContent(pos);
801 pos = startOfDocument(pos);
807 VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
810 switch (granularity) {
811 case CharacterGranularity:
813 if (directionOfSelection() == LTR)
814 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
816 pos = VisiblePosition(m_selection.end(), m_selection.affinity());
818 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
820 case WordGranularity: {
822 bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
823 pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
827 case SentenceGranularity:
828 case LineGranularity:
829 case ParagraphGranularity:
830 case SentenceBoundary:
831 case ParagraphBoundary:
832 case DocumentBoundary:
833 // FIXME: Implement all of the above.
834 pos = modifyMovingBackward(granularity);
837 pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
843 VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
846 switch (granularity) {
847 case CharacterGranularity:
849 pos = VisiblePosition(m_selection.start(), m_selection.affinity());
851 pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
853 case WordGranularity:
854 pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
856 case SentenceGranularity:
857 pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
859 case LineGranularity:
860 pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
862 case ParagraphGranularity:
863 pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
865 case SentenceBoundary:
866 pos = startOfSentence(startForPlatform());
869 pos = logicalStartOfLine(startForPlatform());
871 case ParagraphBoundary:
872 pos = startOfParagraph(startForPlatform());
874 case DocumentBoundary:
875 pos = startForPlatform();
876 if (isEditablePosition(pos.deepEquivalent()))
877 pos = startOfEditableContent(pos);
879 pos = startOfDocument(pos);
885 static bool isBoundary(TextGranularity granularity)
887 return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
890 bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
892 if (userTriggered == UserTriggered) {
893 FrameSelection trialFrameSelection;
894 trialFrameSelection.setSelection(m_selection);
895 trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
897 bool change = shouldChangeSelection(trialFrameSelection.selection());
901 if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
905 willBeModified(alter, direction);
907 bool wasRange = m_selection.isRange();
908 Position originalStartPosition = m_selection.start();
909 VisiblePosition position;
912 if (alter == AlterationMove)
913 position = modifyMovingRight(granularity);
915 position = modifyExtendingRight(granularity);
917 case DirectionForward:
918 if (alter == AlterationExtend)
919 position = modifyExtendingForward(granularity);
921 position = modifyMovingForward(granularity);
924 if (alter == AlterationMove)
925 position = modifyMovingLeft(granularity);
927 position = modifyExtendingLeft(granularity);
929 case DirectionBackward:
930 if (alter == AlterationExtend)
931 position = modifyExtendingBackward(granularity);
933 position = modifyMovingBackward(granularity);
937 if (position.isNull())
940 if (isSpatialNavigationEnabled(m_frame))
941 if (!wasRange && alter == AlterationMove && position == originalStartPosition)
944 // Some of the above operations set an xPosForVerticalArrowNavigation.
945 // Setting a selection will clear it, so save it to possibly restore later.
946 // Note: the START position type is arbitrary because it is unused, it would be
947 // the requested position type if there were no xPosForVerticalArrowNavigation set.
948 LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
949 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
953 moveTo(position, userTriggered);
955 case AlterationExtend:
956 if (!m_selection.isCaret()
957 && (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
958 && m_frame && !m_frame->editor()->behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
959 // Don't let the selection go across the base position directly. Needed to match mac
960 // behavior when, for instance, word-selecting backwards starting with the caret in
961 // the middle of a word and then word-selecting forward, leaving the caret in the
962 // same place where it was, instead of directly selecting to the end of the word.
963 VisibleSelection newSelection = m_selection;
964 newSelection.setExtent(position);
965 if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
966 position = m_selection.base();
969 // Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
970 // base in place and moving the extent. Matches NSTextView.
971 if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
972 setExtent(position, userTriggered);
974 TextDirection textDirection = directionOfEnclosingBlock();
975 if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
976 setEnd(position, userTriggered);
978 setStart(position, userTriggered);
983 if (granularity == LineGranularity || granularity == ParagraphGranularity)
984 m_xPosForVerticalArrowNavigation = x;
986 if (userTriggered == UserTriggered)
987 m_granularity = CharacterGranularity;
989 setCaretRectNeedsUpdate();
994 // FIXME: Maybe baseline would be better?
995 static bool absoluteCaretY(const VisiblePosition &c, int &y)
997 IntRect rect = c.absoluteCaretBounds();
1000 y = rect.y() + rect.height() / 2;
1004 bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1006 if (!verticalDistance)
1009 if (userTriggered == UserTriggered) {
1010 FrameSelection trialFrameSelection;
1011 trialFrameSelection.setSelection(m_selection);
1012 trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1014 bool change = shouldChangeSelection(trialFrameSelection.selection());
1019 willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1021 VisiblePosition pos;
1022 LayoutUnit xPos = 0;
1024 case AlterationMove:
1025 pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1026 xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1027 m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1029 case AlterationExtend:
1030 pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1031 xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1032 m_selection.setAffinity(DOWNSTREAM);
1037 if (!absoluteCaretY(pos, startY))
1039 if (direction == DirectionUp)
1043 VisiblePosition result;
1044 VisiblePosition next;
1045 for (VisiblePosition p = pos; ; p = next) {
1046 if (direction == DirectionUp)
1047 next = previousLinePosition(p, xPos);
1049 next = nextLinePosition(p, xPos);
1051 if (next.isNull() || next == p)
1054 if (!absoluteCaretY(next, nextY))
1056 if (direction == DirectionUp)
1058 if (nextY - startY > static_cast<int>(verticalDistance))
1060 if (nextY >= lastY) {
1066 if (result.isNull())
1070 case AlterationMove:
1071 moveTo(result, userTriggered, align);
1073 case AlterationExtend:
1074 setExtent(result, userTriggered);
1078 if (userTriggered == UserTriggered)
1079 m_granularity = CharacterGranularity;
1081 m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1086 LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1096 pos = m_selection.start();
1099 pos = m_selection.end();
1102 pos = m_selection.base();
1105 pos = m_selection.extent();
1109 Frame* frame = pos.anchorNode()->document()->frame();
1113 if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1114 VisiblePosition visiblePosition(pos, m_selection.affinity());
1115 // VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1116 // after the selection is created and before this function is called.
1117 x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1118 m_xPosForVerticalArrowNavigation = x;
1120 x = m_xPosForVerticalArrowNavigation;
1125 void FrameSelection::clear()
1127 m_granularity = CharacterGranularity;
1128 setSelection(VisibleSelection());
1131 void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1133 if (m_selection.isBaseFirst())
1134 setBase(pos, trigger);
1136 setExtent(pos, trigger);
1139 void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1141 if (m_selection.isBaseFirst())
1142 setExtent(pos, trigger);
1144 setBase(pos, trigger);
1147 void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1149 const bool selectionHasDirection = true;
1150 setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1153 void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1155 const bool selectionHasDirection = true;
1156 setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1159 void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1161 const bool selectionHasDirection = true;
1162 setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1165 void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1167 const bool selectionHasDirection = true;
1168 setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1171 void CaretBase::clearCaretRect()
1173 m_caretLocalRect = LayoutRect();
1176 bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
1178 document->updateStyleIfNeeded();
1179 m_caretLocalRect = LayoutRect();
1181 m_caretRectNeedsUpdate = false;
1183 if (caretPosition.isNull())
1186 ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
1188 // First compute a rect local to the renderer at the selection start.
1189 RenderObject* renderer;
1190 LayoutRect localRect = caretPosition.localCaretRect(renderer);
1192 // Get the renderer that will be responsible for painting the caret
1193 // (which is either the renderer we just found, or one of its containers).
1194 RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
1196 // Compute an offset between the renderer and the caretPainter.
1197 bool unrooted = false;
1198 while (renderer != caretPainter) {
1199 RenderObject* containerObject = renderer->container();
1200 if (!containerObject) {
1204 localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
1205 renderer = containerObject;
1209 m_caretLocalRect = localRect;
1214 static inline bool caretRendersInsideNode(Node* node)
1216 return node && !isTableElement(node) && !editingIgnoresContent(node);
1219 RenderObject* CaretBase::caretRenderer(Node* node) const
1224 RenderObject* renderer = node->renderer();
1228 // if caretNode is a block and caret is inside it then caret should be painted by that block
1229 bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
1230 return paintedByBlock ? renderer : renderer->containingBlock();
1233 RenderObject* FrameSelection::caretRenderer() const
1235 return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1238 RenderObject* DragCaretController::caretRenderer() const
1240 return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
1243 LayoutRect FrameSelection::localCaretRect()
1245 if (shouldUpdateCaretRect()) {
1246 if (!isCaret() || m_selection.start().isOrphan() || m_selection.end().isOrphan())
1248 else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
1249 m_absCaretBoundsDirty = true;
1252 return localCaretRectWithoutUpdate();
1255 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1256 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect, bool useTransforms) const
1258 IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
1261 RenderObject* caretPainter = caretRenderer(node);
1265 LayoutRect localRect(rect);
1266 if (caretPainter->isBox())
1267 toRenderBox(caretPainter)->flipForWritingMode(localRect);
1269 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1271 return caretPainter->localToAbsoluteQuad(FloatRect(localRect), false, 0, false).enclosingBoundingBox();
1274 return caretPainter->localToAbsoluteQuad(FloatRect(localRect)).enclosingBoundingBox();
1277 IntRect FrameSelection::absoluteCaretBounds()
1279 recomputeCaretRect();
1280 return m_absCaretBounds;
1283 static LayoutRect repaintRectForCaret(LayoutRect caret)
1285 if (caret.isEmpty())
1286 return LayoutRect();
1287 // Ensure that the dirty rect intersects the block that paints the caret even in the case where
1288 // the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
1293 IntRect CaretBase::caretRepaintRect(Node* node) const
1295 #if ENABLE(TIZEN_NOT_USE_TRANSFORM_INFO_WHEN_GETTING_CARET_RECT)
1296 return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()), false);
1298 return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()));
1302 bool FrameSelection::recomputeCaretRect()
1304 if (!shouldUpdateCaretRect())
1310 FrameView* v = m_frame->document()->view();
1314 LayoutRect oldRect = localCaretRectWithoutUpdate();
1315 LayoutRect newRect = localCaretRect();
1316 if (oldRect == newRect && !m_absCaretBoundsDirty)
1319 IntRect oldAbsCaretBounds = m_absCaretBounds;
1320 // FIXME: Rename m_caretRect to m_localCaretRect.
1321 m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1322 m_absCaretBoundsDirty = false;
1324 if (oldAbsCaretBounds == m_absCaretBounds)
1327 #if ENABLE(TEXT_CARET)
1328 IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
1331 // We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
1332 m_absoluteCaretRepaintBounds = caretRepaintRect(m_selection.start().deprecatedNode());
1334 #if ENABLE(TEXT_CARET)
1335 if (RenderView* view = toRenderView(m_frame->document()->renderer())) {
1336 // FIXME: make caret repainting container-aware.
1337 view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
1338 if (shouldRepaintCaret(view, isContentEditable()))
1339 view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
1345 bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
1348 Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
1349 bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
1350 return (caretBrowsing || isContentEditable);
1353 void FrameSelection::invalidateCaretRect()
1358 CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1361 void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
1363 // EDIT FIXME: This is an unfortunate hack.
1364 // Basically, we can't trust this layout position since we
1365 // can't guarantee that the check to see if we are in unrendered
1366 // content will work at this point. We may have to wait for
1367 // a layout and re-render of the document to happen. So, resetting this
1368 // flag will cause another caret layout to happen the first time
1369 // that we try to paint the caret after this call. That one will work since
1370 // it happens after the document has accounted for any editing
1371 // changes which may have been done.
1372 // And, we need to leave this layout here so the caret moves right
1373 // away after clicking.
1374 m_caretRectNeedsUpdate = true;
1376 if (!caretRectChanged) {
1377 RenderView* view = toRenderView(node->document()->renderer());
1378 if (view && shouldRepaintCaret(view, node->isContentEditable()))
1379 view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(node), false);
1383 void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1385 if (m_selection.isCaret() && m_caretPaint)
1386 CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1389 void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1391 #if ENABLE(TEXT_CARET)
1392 if (m_caretVisibility == Hidden)
1395 LayoutRect drawingRect = localCaretRectWithoutUpdate();
1396 RenderObject* renderer = caretRenderer(node);
1397 if (renderer && renderer->isBox())
1398 toRenderBox(renderer)->flipForWritingMode(drawingRect);
1399 drawingRect.moveBy(paintOffset);
1400 LayoutRect caret = intersection(drawingRect, clipRect);
1401 if (caret.isEmpty())
1404 Color caretColor = Color::black;
1405 ColorSpace colorSpace = ColorSpaceDeviceRGB;
1406 Element* element = node->rootEditableElement();
1407 if (element && element->renderer()) {
1408 caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
1409 colorSpace = element->renderer()->style()->colorSpace();
1412 context->fillRect(caret, caretColor, colorSpace);
1415 UNUSED_PARAM(context);
1416 UNUSED_PARAM(paintOffset);
1417 UNUSED_PARAM(clipRect);
1421 void FrameSelection::debugRenderer(RenderObject *r, bool selected) const
1423 if (r->node()->isElementNode()) {
1424 Element* element = static_cast<Element *>(r->node());
1425 fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
1426 } else if (r->isText()) {
1427 RenderText* textRenderer = toRenderText(r);
1428 if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
1429 fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1433 static const int max = 36;
1434 String text = textRenderer->text();
1435 int textLength = text.length();
1438 if (r->node() == m_selection.start().containerNode())
1439 offset = m_selection.start().computeOffsetInContainerNode();
1440 else if (r->node() == m_selection.end().containerNode())
1441 offset = m_selection.end().computeOffsetInContainerNode();
1444 InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
1445 text = text.substring(box->start(), box->len());
1451 // text is shorter than max
1452 if (textLength < max) {
1455 } else if (pos - mid < 0) {
1456 // too few characters to left
1457 show = text.left(max - 3) + "...";
1459 } else if (pos - mid >= 0 && pos + mid <= textLength) {
1460 // enough characters on each side
1461 show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1464 // too few characters on right
1465 show = "..." + text.right(max - 3);
1466 caret = pos - (textLength - show.length());
1469 show.replace('\n', ' ');
1470 show.replace('\r', ' ');
1471 fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1472 fprintf(stderr, " ");
1473 for (int i = 0; i < caret; i++)
1474 fprintf(stderr, " ");
1475 fprintf(stderr, "^\n");
1477 if ((int)text.length() > max)
1478 text = text.left(max - 3) + "...";
1480 text = text.left(max);
1481 fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1486 bool FrameSelection::contains(const LayoutPoint& point)
1488 Document* document = m_frame->document();
1490 // Treat a collapsed selection like no selection.
1493 if (!document->renderer())
1496 HitTestRequest request(HitTestRequest::ReadOnly |
1497 HitTestRequest::Active);
1498 HitTestResult result(point);
1499 document->renderView()->hitTest(request, result);
1500 Node* innerNode = result.innerNode();
1501 if (!innerNode || !innerNode->renderer())
1504 VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1505 if (visiblePos.isNull())
1508 if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1511 Position start(m_selection.visibleStart().deepEquivalent());
1512 Position end(m_selection.visibleEnd().deepEquivalent());
1513 Position p(visiblePos.deepEquivalent());
1515 return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1518 // Workaround for the fact that it's hard to delete a frame.
1519 // Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1520 // Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1521 // for the focus to move to another frame. So instead we call it from places where we are selecting with the
1522 // mouse or the keyboard after setting the selection.
1523 void FrameSelection::selectFrameElementInParentIfFullySelected()
1525 // Find the parent frame; if there is none, then we have nothing to do.
1526 Frame* parent = m_frame->tree()->parent();
1529 Page* page = m_frame->page();
1533 // Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1536 if (!isStartOfDocument(selection().visibleStart()))
1538 if (!isEndOfDocument(selection().visibleEnd()))
1541 // Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1542 Element* ownerElement = m_frame->ownerElement();
1545 ContainerNode* ownerElementParent = ownerElement->parentNode();
1546 if (!ownerElementParent)
1549 // 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.
1550 if (!ownerElementParent->rendererIsEditable())
1553 // Create compute positions before and after the element.
1554 unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1555 VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1556 VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1558 // Focus on the parent frame, and then select from before this element to after.
1559 VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1560 if (parent->selection()->shouldChangeSelection(newSelection)) {
1561 page->focusController()->setFocusedFrame(parent);
1562 parent->selection()->setSelection(newSelection);
1566 void FrameSelection::selectAll()
1568 Document* document = m_frame->document();
1570 if (document->focusedNode() && document->focusedNode()->hasTagName(selectTag)) {
1571 HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedNode());
1572 if (selectElement->canSelectAll()) {
1573 selectElement->selectAll();
1578 RefPtr<Node> root = 0;
1579 Node* selectStartTarget = 0;
1580 if (isContentEditable()) {
1581 root = highestEditableRoot(m_selection.start());
1582 if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1583 selectStartTarget = shadowRoot->shadowAncestorNode();
1585 selectStartTarget = root.get();
1587 root = m_selection.nonBoundaryShadowTreeRootNode();
1589 selectStartTarget = root->shadowAncestorNode();
1591 root = document->documentElement();
1592 selectStartTarget = document->body();
1598 if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
1601 VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1603 if (shouldChangeSelection(newSelection))
1604 setSelection(newSelection);
1606 selectFrameElementInParentIfFullySelected();
1607 notifyRendererOfSelectionChange(UserTriggered);
1610 bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1612 if (!range || !range->startContainer() || !range->endContainer())
1614 ASSERT(range->startContainer()->document() == range->endContainer()->document());
1616 m_frame->document()->updateLayoutIgnorePendingStylesheets();
1618 // Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1619 // they start at the beginning of the next line instead
1620 ExceptionCode ec = 0;
1621 bool collapsed = range->collapsed(ec);
1625 // FIXME: Can we provide extentAffinity?
1626 VisiblePosition visibleStart(range->startPosition(), collapsed ? affinity : DOWNSTREAM);
1627 VisiblePosition visibleEnd(range->endPosition(), SEL_DEFAULT_AFFINITY);
1628 setSelection(VisibleSelection(visibleStart, visibleEnd), ClearTypingStyle | (closeTyping ? CloseTyping : 0));
1632 bool FrameSelection::isInPasswordField() const
1634 HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1635 return textControl && textControl->hasTagName(inputTag) && static_cast<HTMLInputElement*>(textControl)->isPasswordField();
1638 void FrameSelection::focusedOrActiveStateChanged()
1640 bool activeAndFocused = isFocusedAndActive();
1642 // Because RenderObject::selectionBackgroundColor() and
1643 // RenderObject::selectionForegroundColor() check if the frame is active,
1644 // we have to update places those colors were painted.
1645 if (RenderView* view = toRenderView(m_frame->document()->renderer()))
1646 view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
1648 // Caret appears in the active frame.
1649 if (activeAndFocused)
1650 setSelectionFromNone();
1651 setCaretVisibility(activeAndFocused ? Visible : Hidden);
1653 // Update for caps lock state
1654 m_frame->eventHandler()->capsLockStateMayHaveChanged();
1656 // Because StyleResolver::checkOneSelector() and
1657 // RenderTheme::isFocused() check if the frame is active, we have to
1658 // update style and theme state that depended on those.
1659 if (Node* node = m_frame->document()->focusedNode()) {
1660 node->setNeedsStyleRecalc();
1661 if (RenderObject* renderer = node->renderer())
1662 if (renderer && renderer->style()->hasAppearance())
1663 renderer->theme()->stateChanged(renderer, FocusState);
1666 // Secure keyboard entry is set by the active frame.
1667 if (m_frame->document()->useSecureKeyboardEntryWhenActive())
1668 setUseSecureKeyboardEntry(activeAndFocused);
1671 void FrameSelection::pageActivationChanged()
1673 focusedOrActiveStateChanged();
1676 void FrameSelection::updateSecureKeyboardEntryIfActive()
1678 if (m_frame->document() && isFocusedAndActive())
1679 setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1682 void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1685 enableSecureTextInput();
1687 disableSecureTextInput();
1690 void FrameSelection::setFocused(bool flag)
1692 if (m_focused == flag)
1696 focusedOrActiveStateChanged();
1699 bool FrameSelection::isFocusedAndActive() const
1701 return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
1704 inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame)
1706 return frame->editor()->lastEditCommand() && frame->editor()->lastEditCommand()->shouldStopCaretBlinking();
1709 void FrameSelection::updateAppearance()
1711 #if ENABLE(TEXT_CARET)
1712 bool caretRectChanged = recomputeCaretRect();
1714 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1715 bool shouldBlink = caretIsVisible() && isCaret() && (isContentEditable() || caretBrowsing);
1717 // If the caret moved, stop the blink timer so we can restart with a
1718 // black caret in the new location.
1719 if (caretRectChanged || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame))
1720 m_caretBlinkTimer.stop();
1722 // Start blinking with a black caret. Be sure not to restart if we're
1723 // already blinking in the right location.
1724 if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1725 if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
1726 m_caretBlinkTimer.startRepeating(blinkInterval);
1728 if (!m_caretPaint) {
1729 m_caretPaint = true;
1730 invalidateCaretRect();
1735 // We need to update style in case the node containing the selection is made display:none.
1736 m_frame->document()->updateStyleIfNeeded();
1738 RenderView* view = m_frame->contentRenderer();
1742 // Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1743 // assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1744 VisibleSelection selection(m_selection.visibleStart(), m_selection.visibleEnd());
1746 if (!selection.isRange()) {
1747 view->clearSelection();
1751 // Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1752 // Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1753 // as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1754 // and will fill the gap before 'bar'.
1755 Position startPos = selection.start();
1756 Position candidate = startPos.downstream();
1757 if (candidate.isCandidate())
1758 startPos = candidate;
1759 Position endPos = selection.end();
1760 candidate = endPos.upstream();
1761 if (candidate.isCandidate())
1764 // We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1765 // because we don't yet notify the FrameSelection of text removal.
1766 if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1767 RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1768 RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1769 view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1773 void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1775 if (caretVisibility() == visibility)
1777 clearCaretRectIfNeeded();
1778 CaretBase::setCaretVisibility(visibility);
1782 void FrameSelection::clearCaretRectIfNeeded()
1784 #if ENABLE(TEXT_CARET)
1787 m_caretPaint = false;
1788 invalidateCaretRect();
1792 void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1794 #if ENABLE(TEXT_CARET)
1795 ASSERT(caretIsVisible());
1797 bool caretPaint = m_caretPaint;
1798 if (isCaretBlinkingSuspended() && caretPaint)
1800 m_caretPaint = !caretPaint;
1801 invalidateCaretRect();
1805 void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1807 m_frame->document()->updateStyleIfNeeded();
1809 if (!rootEditableElement())
1812 if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1813 textControl->selectionChanged(userTriggered == UserTriggered);
1816 // Helper function that tells whether a particular node is an element that has an entire
1817 // Frame and FrameView, a <frame>, <iframe>, or <object>.
1818 static bool isFrameElement(const Node* n)
1822 RenderObject* renderer = n->renderer();
1823 if (!renderer || !renderer->isWidget())
1825 Widget* widget = toRenderWidget(renderer)->widget();
1826 return widget && widget->isFrameView();
1829 void FrameSelection::setFocusedNodeIfNeeded()
1831 if (isNone() || !isFocused())
1834 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1835 if (caretBrowsing) {
1836 if (Node* anchor = enclosingAnchorElement(base())) {
1837 m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
1842 if (Node* target = rootEditableElement()) {
1843 // Walk up the DOM tree to search for a node to focus.
1845 // We don't want to set focus on a subframe when selecting in a parent frame,
1846 // so add the !isFrameElement check here. There's probably a better way to make this
1847 // work in the long term, but this is the safest fix at this time.
1848 if (target && target->isMouseFocusable() && !isFrameElement(target)) {
1849 m_frame->page()->focusController()->setFocusedNode(target, m_frame);
1852 target = target->parentOrHostNode();
1854 m_frame->document()->setFocusedNode(0);
1858 m_frame->page()->focusController()->setFocusedNode(0, m_frame);
1861 void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1863 #if ENABLE(TEXT_CARET)
1864 if (m_position.deepEquivalent().deprecatedNode()->document()->frame() == frame)
1865 paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
1867 UNUSED_PARAM(frame);
1869 UNUSED_PARAM(paintOffset);
1870 UNUSED_PARAM(clipRect);
1874 PassRefPtr<StylePropertySet> FrameSelection::copyTypingStyle() const
1876 if (!m_typingStyle || !m_typingStyle->style())
1878 return m_typingStyle->style()->copy();
1881 bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
1883 return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
1886 FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
1888 RenderView* root = m_frame->contentRenderer();
1889 FrameView* view = m_frame->view();
1891 return LayoutRect();
1893 LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent);
1894 return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1897 void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
1899 RenderView* root = m_frame->contentRenderer();
1903 FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
1905 Vector<FloatQuad> quads;
1906 toNormalizedRange()->textQuads(quads, true);
1908 size_t size = quads.size();
1909 for (size_t i = 0; i < size; ++i) {
1910 FloatRect intersectionRect = intersection(quads[i].enclosingBoundingBox(), visibleContentRect);
1911 if (!intersectionRect.isEmpty())
1912 rectangles.append(intersectionRect);
1916 // Scans logically forward from "start", including any child frames.
1917 static HTMLFormElement* scanForForm(Node* start)
1919 for (Node* node = start; node; node = node->traverseNextNode()) {
1920 if (node->hasTagName(formTag))
1921 return static_cast<HTMLFormElement*>(node);
1922 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1923 return static_cast<HTMLFormControlElement*>(node)->form();
1924 if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
1925 Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
1926 if (HTMLFormElement* frameResult = scanForForm(childDocument))
1933 // We look for either the form containing the current focus, or for one immediately after it
1934 HTMLFormElement* FrameSelection::currentForm() const
1936 // Start looking either at the active (first responder) node, or where the selection is.
1937 Node* start = m_frame->document()->focusedNode();
1939 start = this->start().deprecatedNode();
1941 // Try walking up the node tree to find a form element.
1943 for (node = start; node; node = node->parentNode()) {
1944 if (node->hasTagName(formTag))
1945 return static_cast<HTMLFormElement*>(node);
1946 if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1947 return static_cast<HTMLFormControlElement*>(node)->form();
1950 // Try walking forward in the node tree to find a form element.
1951 return scanForForm(start);
1954 void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1958 switch (selectionType()) {
1959 case VisibleSelection::NoSelection:
1961 case VisibleSelection::CaretSelection:
1962 rect = absoluteCaretBounds();
1964 case VisibleSelection::RangeSelection:
1965 rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
1969 Position start = this->start();
1970 ASSERT(start.deprecatedNode());
1971 if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
1972 // FIXME: This code only handles scrolling the startContainer's layer, but
1973 // the selection rect could intersect more than just that.
1974 // See <rdar://problem/4799899>.
1975 if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
1980 void FrameSelection::setSelectionFromNone()
1982 // Put a caret inside the body if the entire frame is editable (either the
1983 // entire WebView is editable or designMode is on for this document).
1985 Document* document = m_frame->document();
1986 bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1987 if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
1990 Node* node = document->documentElement();
1991 while (node && !node->hasTagName(bodyTag))
1992 node = node->traverseNextNode();
1994 setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
1997 bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
1999 return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
2002 bool FrameSelection::dispatchSelectStart()
2004 Node* selectStartTarget = m_selection.extent().containerNode();
2005 if (!selectStartTarget)
2008 return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
2011 inline bool FrameSelection::visualWordMovementEnabled() const
2013 Settings* settings = m_frame ? m_frame->settings() : 0;
2014 return settings && settings->visualWordMovementEnabled();
2016 #if ENABLE(TIZEN_CONTEXT_MENU_SELECT)
2017 bool FrameSelection::canEditableSelectRange() const
2019 RefPtr<Node> editableRoot = 0;
2020 if (isContentEditable())
2021 editableRoot = highestEditableRoot(m_selection.start());
2025 if (!comparePositions(firstPositionInNode(editableRoot.get()), lastPositionInNode(editableRoot.get())))
2034 void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
2036 m_selection.formatForDebugger(buffer, length);
2039 void FrameSelection::showTreeForThis() const
2041 m_selection.showTreeForThis();
2050 void showTree(const WebCore::FrameSelection& sel)
2052 sel.showTreeForThis();
2055 void showTree(const WebCore::FrameSelection* sel)
2058 sel->showTreeForThis();