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 LayoutUnit::min();
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
Element* FrameSelection::rootEditableElementRespectingShadowTree() const
129
Element* selectionRoot = m_selection.rootEditableElement();
130
if (selectionRoot && selectionRoot->isInShadowTree())
131
selectionRoot = selectionRoot->shadowHost();
132
return selectionRoot;
135
void FrameSelection::moveTo(const VisiblePosition &pos, EUserTriggered userTriggered, CursorAlignOnScroll align)
137
SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
138
setSelection(VisibleSelection(pos.deepEquivalent(), pos.deepEquivalent(), pos.affinity(), m_selection.isDirectional()), options, align);
141
void FrameSelection::moveTo(const VisiblePosition &base, const VisiblePosition &extent, EUserTriggered userTriggered)
143
const bool selectionHasDirection = true;
144
SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
145
setSelection(VisibleSelection(base.deepEquivalent(), extent.deepEquivalent(), base.affinity(), selectionHasDirection), options);
148
void FrameSelection::moveTo(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
150
SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
151
setSelection(VisibleSelection(pos, affinity, m_selection.isDirectional()), options);
154
void FrameSelection::moveTo(const Range *r, EAffinity affinity, EUserTriggered userTriggered)
156
SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
157
VisibleSelection selection = r ? VisibleSelection(r->startPosition(), r->endPosition(), affinity) : VisibleSelection(Position(), Position(), affinity);
158
setSelection(selection, options);
161
void FrameSelection::moveTo(const Position &base, const Position &extent, EAffinity affinity, EUserTriggered userTriggered)
163
const bool selectionHasDirection = true;
164
SetSelectionOptions options = CloseTyping | ClearTypingStyle | userTriggered;
165
setSelection(VisibleSelection(base, extent, affinity, selectionHasDirection), options);
168
void DragCaretController::setCaretPosition(const VisiblePosition& position)
170
if (Node* node = m_position.deepEquivalent().deprecatedNode())
171
invalidateCaretRect(node);
172
m_position = position;
173
setCaretRectNeedsUpdate();
174
Document* document = 0;
175
if (Node* node = m_position.deepEquivalent().deprecatedNode()) {
176
invalidateCaretRect(node);
177
document = node->document();
179
if (m_position.isNull() || m_position.isOrphan())
182
updateCaretRect(document, m_position);
185
static void adjustEndpointsAtBidiBoundary(VisiblePosition& visibleBase, VisiblePosition& visibleExtent)
187
RenderedPosition base(visibleBase);
188
RenderedPosition extent(visibleExtent);
190
if (base.isNull() || extent.isNull() || base.isEquivalent(extent))
193
if (base.atLeftBoundaryOfBidiRun()) {
194
if (!extent.atRightBoundaryOfBidiRun(base.bidiLevelOnRight())
195
&& base.isEquivalent(extent.leftBoundaryOfBidiRun(base.bidiLevelOnRight()))) {
196
visibleBase = base.positionAtLeftBoundaryOfBiDiRun();
202
if (base.atRightBoundaryOfBidiRun()) {
203
if (!extent.atLeftBoundaryOfBidiRun(base.bidiLevelOnLeft())
204
&& base.isEquivalent(extent.rightBoundaryOfBidiRun(base.bidiLevelOnLeft()))) {
205
visibleBase = base.positionAtRightBoundaryOfBiDiRun();
211
if (extent.atLeftBoundaryOfBidiRun() && extent.isEquivalent(base.leftBoundaryOfBidiRun(extent.bidiLevelOnRight()))) {
212
visibleExtent = extent.positionAtLeftBoundaryOfBiDiRun();
216
if (extent.atRightBoundaryOfBidiRun() && extent.isEquivalent(base.rightBoundaryOfBidiRun(extent.bidiLevelOnLeft()))) {
217
visibleExtent = extent.positionAtRightBoundaryOfBiDiRun();
222
void FrameSelection::setNonDirectionalSelectionIfNeeded(const VisibleSelection& passedNewSelection, TextGranularity granularity,
223
EndPointsAdjustmentMode endpointsAdjustmentMode)
225
VisibleSelection newSelection = passedNewSelection;
226
bool isDirectional = shouldAlwaysUseDirectionalSelection(m_frame) || newSelection.isDirectional();
228
VisiblePosition base = m_originalBase.isNotNull() ? m_originalBase : newSelection.visibleBase();
229
VisiblePosition newBase = base;
230
VisiblePosition newExtent = newSelection.visibleExtent();
231
if (endpointsAdjustmentMode == AdjustEndpointsAtBidiBoundary)
232
adjustEndpointsAtBidiBoundary(newBase, newExtent);
234
if (newBase != base || newExtent != newSelection.visibleExtent()) {
235
m_originalBase = base;
236
newSelection.setBase(newBase);
237
newSelection.setExtent(newExtent);
238
} else if (m_originalBase.isNotNull()) {
239
if (m_selection.base() == newSelection.base())
240
newSelection.setBase(m_originalBase);
241
m_originalBase.clear();
244
newSelection.setIsDirectional(isDirectional); // Adjusting base and extent will make newSelection always directional
245
if (m_selection == newSelection || !shouldChangeSelection(newSelection))
248
setSelection(newSelection, granularity);
251
void FrameSelection::setSelection(const VisibleSelection& newSelection, SetSelectionOptions options, CursorAlignOnScroll align, TextGranularity granularity)
253
bool closeTyping = options & CloseTyping;
254
bool shouldClearTypingStyle = options & ClearTypingStyle;
255
EUserTriggered userTriggered = selectionOptionsToUserTriggered(options);
257
VisibleSelection s = newSelection;
258
if (shouldAlwaysUseDirectionalSelection(m_frame))
259
s.setIsDirectional(true);
266
// <http://bugs.webkit.org/show_bug.cgi?id=23464>: Infinite recursion at FrameSelection::setSelection
267
// if document->frame() == m_frame we can get into an infinite loop
268
if (s.base().anchorNode()) {
269
Document* document = s.base().anchorNode()->document();
270
if (document && document->frame() && document->frame() != m_frame && document != m_frame->document()) {
271
document->frame()->selection()->setSelection(s, options, align, granularity);
276
m_granularity = granularity;
279
TypingCommand::closeTyping(m_frame);
281
if (shouldClearTypingStyle)
284
if (m_selection == s) {
285
// Even if selection was not changed, selection offsets may have been changed.
286
notifyRendererOfSelectionChange(userTriggered);
290
VisibleSelection oldSelection = m_selection;
293
setCaretRectNeedsUpdate();
295
if (!s.isNone() && !(options & DoNotSetFocus))
296
setFocusedNodeIfNeeded();
298
if (!(options & DoNotUpdateAppearance)) {
299
#if ENABLE(TEXT_CARET)
300
m_frame->document()->updateLayoutIgnorePendingStylesheets();
302
m_frame->document()->updateStyleIfNeeded();
307
// Always clear the x position used for vertical arrow navigation.
308
// It will be restored by the vertical arrow navigation code if necessary.
309
m_xPosForVerticalArrowNavigation = NoXPosForVerticalArrowNavigation();
310
selectFrameElementInParentIfFullySelected();
311
notifyRendererOfSelectionChange(userTriggered);
312
m_frame->editor()->respondToChangedSelection(oldSelection, options);
313
if (userTriggered == UserTriggered) {
314
ScrollAlignment alignment;
316
if (m_frame->editor()->behavior().shouldCenterAlignWhenSelectionIsRevealed())
317
alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignCenterAlways : ScrollAlignment::alignCenterIfNeeded;
319
alignment = (align == AlignCursorOnScrollAlways) ? ScrollAlignment::alignTopAlways : ScrollAlignment::alignToEdgeIfNeeded;
321
revealSelection(alignment, RevealExtent);
324
notifyAccessibilityForSelectionChange();
325
m_frame->document()->enqueueDocumentEvent(Event::create(eventNames().selectionchangeEvent, false, false));
328
static bool removingNodeRemovesPosition(Node* node, const Position& position)
330
if (!position.anchorNode())
333
if (position.anchorNode() == node)
336
if (!node->isElementNode())
339
Element* element = static_cast<Element*>(node);
340
return element->contains(position.anchorNode()) || element->contains(position.anchorNode()->shadowAncestorNode());
343
static void clearRenderViewSelection(const Position& position)
345
RefPtr<Document> document = position.anchorNode()->document();
346
document->updateStyleIfNeeded();
347
if (RenderView* view = document->renderView())
348
view->clearSelection();
351
void DragCaretController::nodeWillBeRemoved(Node* node)
353
if (!hasCaret() || (node && !node->inDocument()))
356
if (!removingNodeRemovesPosition(node, m_position.deepEquivalent()))
359
clearRenderViewSelection(m_position.deepEquivalent());
363
void FrameSelection::nodeWillBeRemoved(Node* node)
365
// There can't be a selection inside a fragment, so if a fragment's node is being removed,
366
// the selection in the document that created the fragment needs no adjustment.
367
if (isNone() || (node && !node->inDocument()))
370
respondToNodeModification(node, removingNodeRemovesPosition(node, m_selection.base()), removingNodeRemovesPosition(node, m_selection.extent()),
371
removingNodeRemovesPosition(node, m_selection.start()), removingNodeRemovesPosition(node, m_selection.end()));
374
void FrameSelection::respondToNodeModification(Node* node, bool baseRemoved, bool extentRemoved, bool startRemoved, bool endRemoved)
376
bool clearRenderTreeSelection = false;
377
bool clearDOMTreeSelection = false;
379
if (startRemoved || endRemoved) {
380
Position start = m_selection.start();
381
Position end = m_selection.end();
383
updatePositionForNodeRemoval(start, node);
385
updatePositionForNodeRemoval(end, node);
387
if (start.isNotNull() && end.isNotNull()) {
388
if (m_selection.isBaseFirst())
389
m_selection.setWithoutValidation(start, end);
391
m_selection.setWithoutValidation(end, start);
393
clearDOMTreeSelection = true;
395
clearRenderTreeSelection = true;
396
} else if (baseRemoved || extentRemoved) {
397
// The base and/or extent are about to be removed, but the start and end aren't.
398
// Change the base and extent to the start and end, but don't re-validate the
399
// selection, since doing so could move the start and end into the node
400
// that is about to be removed.
401
if (m_selection.isBaseFirst())
402
m_selection.setWithoutValidation(m_selection.start(), m_selection.end());
404
m_selection.setWithoutValidation(m_selection.end(), m_selection.start());
405
} else if (RefPtr<Range> range = m_selection.firstRange()) {
406
ExceptionCode ec = 0;
407
Range::CompareResults compareResult = range->compareNode(node, ec);
408
if (!ec && (compareResult == Range::NODE_BEFORE_AND_AFTER || compareResult == Range::NODE_INSIDE)) {
409
// If we did nothing here, when this node's renderer was destroyed, the rect that it
410
// occupied would be invalidated, but, selection gaps that change as a result of
411
// the removal wouldn't be invalidated.
412
// FIXME: Don't do so much unnecessary invalidation.
413
clearRenderTreeSelection = true;
417
if (clearRenderTreeSelection)
418
clearRenderViewSelection(m_selection.start());
420
if (clearDOMTreeSelection)
421
setSelection(VisibleSelection(), DoNotSetFocus);
424
static void updatePositionAfterAdoptingTextReplacement(Position& position, CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
426
if (!position.anchorNode() || position.anchorNode() != node || position.anchorType() != Position::PositionIsOffsetInAnchor)
429
// See: http://www.w3.org/TR/DOM-Level-2-Traversal-Range/ranges.html#Level-2-Range-Mutation
430
ASSERT(position.offsetInContainerNode() >= 0);
431
unsigned positionOffset = static_cast<unsigned>(position.offsetInContainerNode());
432
// Replacing text can be viewed as a deletion followed by insertion.
433
if (positionOffset >= offset && positionOffset <= offset + oldLength)
434
position.moveToOffset(offset);
436
// Adjust the offset if the position is after the end of the deleted contents
437
// (positionOffset > offset + oldLength) to avoid having a stale offset.
438
if (positionOffset > offset + oldLength)
439
position.moveToOffset(positionOffset - oldLength + newLength);
441
ASSERT(static_cast<unsigned>(position.offsetInContainerNode()) <= node->length());
444
static inline bool nodeIsDetachedFromDocument(Node* node)
447
Node* highest = highestAncestor(node);
448
return highest->nodeType() == Node::DOCUMENT_FRAGMENT_NODE && !highest->isShadowRoot();
451
void FrameSelection::textWasReplaced(CharacterData* node, unsigned offset, unsigned oldLength, unsigned newLength)
453
// The fragment check is a performance optimization. See http://trac.webkit.org/changeset/30062.
454
if (isNone() || !node || nodeIsDetachedFromDocument(node))
457
Position base = m_selection.base();
458
Position extent = m_selection.extent();
459
Position start = m_selection.start();
460
Position end = m_selection.end();
461
updatePositionAfterAdoptingTextReplacement(base, node, offset, oldLength, newLength);
462
updatePositionAfterAdoptingTextReplacement(extent, node, offset, oldLength, newLength);
463
updatePositionAfterAdoptingTextReplacement(start, node, offset, oldLength, newLength);
464
updatePositionAfterAdoptingTextReplacement(end, node, offset, oldLength, newLength);
466
if (base != m_selection.base() || extent != m_selection.extent() || start != m_selection.start() || end != m_selection.end()) {
467
VisibleSelection newSelection;
468
newSelection.setWithoutValidation(base, extent);
469
m_frame->document()->updateLayout();
470
setSelection(newSelection, DoNotSetFocus);
474
TextDirection FrameSelection::directionOfEnclosingBlock()
476
return WebCore::directionOfEnclosingBlock(m_selection.extent());
479
TextDirection FrameSelection::directionOfSelection()
481
InlineBox* startBox = 0;
482
InlineBox* endBox = 0;
484
if (m_selection.start().isNotNull())
485
m_selection.visibleStart().getInlineBoxAndOffset(startBox, unusedOffset);
486
if (m_selection.end().isNotNull())
487
m_selection.visibleEnd().getInlineBoxAndOffset(endBox, unusedOffset);
488
if (startBox && endBox && startBox->direction() == endBox->direction())
489
return startBox->direction();
491
return directionOfEnclosingBlock();
494
void FrameSelection::willBeModified(EAlteration alter, SelectionDirection direction)
496
if (alter != AlterationExtend)
499
Position start = m_selection.start();
500
Position end = m_selection.end();
502
bool baseIsStart = true;
504
if (m_selection.isDirectional()) {
505
// Make base and extent match start and end so we extend the user-visible selection.
506
// This only matters for cases where base and extend point to different positions than
507
// start and end (e.g. after a double-click to select a word).
508
if (m_selection.isBaseFirst())
515
if (directionOfSelection() == LTR)
520
case DirectionForward:
524
if (directionOfSelection() == LTR)
529
case DirectionBackward:
535
m_selection.setBase(start);
536
m_selection.setExtent(end);
538
m_selection.setBase(end);
539
m_selection.setExtent(start);
543
VisiblePosition FrameSelection::positionForPlatform(bool isGetStart) const
545
Settings* settings = m_frame ? m_frame->settings() : 0;
546
if (settings && settings->editingBehaviorType() == EditingMacBehavior)
547
return isGetStart ? m_selection.visibleStart() : m_selection.visibleEnd();
548
// Linux and Windows always extend selections from the extent endpoint.
549
// FIXME: VisibleSelection should be fixed to ensure as an invariant that
550
// base/extent always point to the same nodes as start/end, but which points
551
// to which depends on the value of isBaseFirst. Then this can be changed
552
// to just return m_sel.extent().
553
return m_selection.isBaseFirst() ? m_selection.visibleEnd() : m_selection.visibleStart();
556
VisiblePosition FrameSelection::startForPlatform() const
558
return positionForPlatform(true);
561
VisiblePosition FrameSelection::endForPlatform() const
563
return positionForPlatform(false);
566
#if ENABLE(USERSELECT_ALL)
567
static void adjustPositionForUserSelectAll(VisiblePosition& pos, bool isForward)
569
if (Node* rootUserSelectAll = Position::rootUserSelectAllForNode(pos.deepEquivalent().anchorNode()))
570
pos = isForward ? positionAfterNode(rootUserSelectAll).downstream(CanCrossEditingBoundary) : positionBeforeNode(rootUserSelectAll).upstream(CanCrossEditingBoundary);
574
VisiblePosition FrameSelection::modifyExtendingRight(TextGranularity granularity)
576
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
578
// The difference between modifyExtendingRight and modifyExtendingForward is:
579
// modifyExtendingForward always extends forward logically.
580
// modifyExtendingRight behaves the same as modifyExtendingForward except for extending character or word,
581
// it extends forward logically if the enclosing block is LTR direction,
582
// but it extends backward logically if the enclosing block is RTL direction.
583
switch (granularity) {
584
case CharacterGranularity:
585
if (directionOfEnclosingBlock() == LTR)
586
pos = pos.next(CannotCrossEditingBoundary);
588
pos = pos.previous(CannotCrossEditingBoundary);
590
case WordGranularity:
591
if (directionOfEnclosingBlock() == LTR)
592
pos = nextWordPosition(pos);
594
pos = previousWordPosition(pos);
597
if (directionOfEnclosingBlock() == LTR)
598
pos = modifyExtendingForward(granularity);
600
pos = modifyExtendingBackward(granularity);
602
case SentenceGranularity:
603
case LineGranularity:
604
case ParagraphGranularity:
605
case SentenceBoundary:
606
case ParagraphBoundary:
607
case DocumentBoundary:
608
// FIXME: implement all of the above?
609
pos = modifyExtendingForward(granularity);
612
#if ENABLE(USERSELECT_ALL)
613
adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
618
VisiblePosition FrameSelection::modifyExtendingForward(TextGranularity granularity)
620
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
621
switch (granularity) {
622
case CharacterGranularity:
623
pos = pos.next(CannotCrossEditingBoundary);
625
case WordGranularity:
626
pos = nextWordPosition(pos);
628
case SentenceGranularity:
629
pos = nextSentencePosition(pos);
631
case LineGranularity:
632
pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
634
case ParagraphGranularity:
635
pos = nextParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
637
case SentenceBoundary:
638
pos = endOfSentence(endForPlatform());
641
pos = logicalEndOfLine(endForPlatform());
643
case ParagraphBoundary:
644
pos = endOfParagraph(endForPlatform());
646
case DocumentBoundary:
647
pos = endForPlatform();
648
if (isEditablePosition(pos.deepEquivalent()))
649
pos = endOfEditableContent(pos);
651
pos = endOfDocument(pos);
654
#if ENABLE(USERSELECT_ALL)
655
adjustPositionForUserSelectAll(pos, directionOfEnclosingBlock() == LTR);
660
VisiblePosition FrameSelection::modifyMovingRight(TextGranularity granularity)
663
switch (granularity) {
664
case CharacterGranularity:
666
if (directionOfSelection() == LTR)
667
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
669
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
671
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).right(true);
673
case WordGranularity: {
675
// Visual word movement relies on isWordTextBreak which is not implemented in WinCE and QT.
676
// https://bugs.webkit.org/show_bug.cgi?id=81136.
677
bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
678
pos = rightWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
682
case SentenceGranularity:
683
case LineGranularity:
684
case ParagraphGranularity:
685
case SentenceBoundary:
686
case ParagraphBoundary:
687
case DocumentBoundary:
688
// FIXME: Implement all of the above.
689
pos = modifyMovingForward(granularity);
692
pos = rightBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
698
VisiblePosition FrameSelection::modifyMovingForward(TextGranularity granularity)
701
// FIXME: Stay in editable content for the less common granularities.
702
switch (granularity) {
703
case CharacterGranularity:
705
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
707
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).next(CannotCrossEditingBoundary);
709
case WordGranularity:
710
pos = nextWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
712
case SentenceGranularity:
713
pos = nextSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
715
case LineGranularity: {
716
// down-arrowing from a range selection that ends at the start of a line needs
717
// to leave the selection at that line start (no need to call nextLinePosition!)
718
pos = endForPlatform();
719
if (!isRange() || !isStartOfLine(pos))
720
pos = nextLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(START));
723
case ParagraphGranularity:
724
pos = nextParagraphPosition(endForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
726
case SentenceBoundary:
727
pos = endOfSentence(endForPlatform());
730
pos = logicalEndOfLine(endForPlatform());
732
case ParagraphBoundary:
733
pos = endOfParagraph(endForPlatform());
735
case DocumentBoundary:
736
pos = endForPlatform();
737
if (isEditablePosition(pos.deepEquivalent()))
738
pos = endOfEditableContent(pos);
740
pos = endOfDocument(pos);
746
VisiblePosition FrameSelection::modifyExtendingLeft(TextGranularity granularity)
748
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
750
// The difference between modifyExtendingLeft and modifyExtendingBackward is:
751
// modifyExtendingBackward always extends backward logically.
752
// modifyExtendingLeft behaves the same as modifyExtendingBackward except for extending character or word,
753
// it extends backward logically if the enclosing block is LTR direction,
754
// but it extends forward logically if the enclosing block is RTL direction.
755
switch (granularity) {
756
case CharacterGranularity:
757
if (directionOfEnclosingBlock() == LTR)
758
pos = pos.previous(CannotCrossEditingBoundary);
760
pos = pos.next(CannotCrossEditingBoundary);
762
case WordGranularity:
763
if (directionOfEnclosingBlock() == LTR)
764
pos = previousWordPosition(pos);
766
pos = nextWordPosition(pos);
769
if (directionOfEnclosingBlock() == LTR)
770
pos = modifyExtendingBackward(granularity);
772
pos = modifyExtendingForward(granularity);
774
case SentenceGranularity:
775
case LineGranularity:
776
case ParagraphGranularity:
777
case SentenceBoundary:
778
case ParagraphBoundary:
779
case DocumentBoundary:
780
pos = modifyExtendingBackward(granularity);
783
#if ENABLE(USERSELECT_ALL)
784
adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
789
VisiblePosition FrameSelection::modifyExtendingBackward(TextGranularity granularity)
791
VisiblePosition pos(m_selection.extent(), m_selection.affinity());
793
// Extending a selection backward by word or character from just after a table selects
794
// the table. This "makes sense" from the user perspective, esp. when deleting.
795
// It was done here instead of in VisiblePosition because we want VPs to iterate
797
switch (granularity) {
798
case CharacterGranularity:
799
pos = pos.previous(CannotCrossEditingBoundary);
801
case WordGranularity:
802
pos = previousWordPosition(pos);
804
case SentenceGranularity:
805
pos = previousSentencePosition(pos);
807
case LineGranularity:
808
pos = previousLinePosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
810
case ParagraphGranularity:
811
pos = previousParagraphPosition(pos, lineDirectionPointForBlockDirectionNavigation(EXTENT));
813
case SentenceBoundary:
814
pos = startOfSentence(startForPlatform());
817
pos = logicalStartOfLine(startForPlatform());
819
case ParagraphBoundary:
820
pos = startOfParagraph(startForPlatform());
822
case DocumentBoundary:
823
pos = startForPlatform();
824
if (isEditablePosition(pos.deepEquivalent()))
825
pos = startOfEditableContent(pos);
827
pos = startOfDocument(pos);
830
#if ENABLE(USERSELECT_ALL)
831
adjustPositionForUserSelectAll(pos, !(directionOfEnclosingBlock() == LTR));
836
VisiblePosition FrameSelection::modifyMovingLeft(TextGranularity granularity)
839
switch (granularity) {
840
case CharacterGranularity:
842
if (directionOfSelection() == LTR)
843
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
845
pos = VisiblePosition(m_selection.end(), m_selection.affinity());
847
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).left(true);
849
case WordGranularity: {
851
bool skipsSpaceWhenMovingRight = m_frame && m_frame->editor()->behavior().shouldSkipSpaceWhenMovingRight();
852
pos = leftWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()), skipsSpaceWhenMovingRight);
856
case SentenceGranularity:
857
case LineGranularity:
858
case ParagraphGranularity:
859
case SentenceBoundary:
860
case ParagraphBoundary:
861
case DocumentBoundary:
862
// FIXME: Implement all of the above.
863
pos = modifyMovingBackward(granularity);
866
pos = leftBoundaryOfLine(startForPlatform(), directionOfEnclosingBlock());
872
VisiblePosition FrameSelection::modifyMovingBackward(TextGranularity granularity)
875
switch (granularity) {
876
case CharacterGranularity:
878
pos = VisiblePosition(m_selection.start(), m_selection.affinity());
880
pos = VisiblePosition(m_selection.extent(), m_selection.affinity()).previous(CannotCrossEditingBoundary);
882
case WordGranularity:
883
pos = previousWordPosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
885
case SentenceGranularity:
886
pos = previousSentencePosition(VisiblePosition(m_selection.extent(), m_selection.affinity()));
888
case LineGranularity:
889
pos = previousLinePosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
891
case ParagraphGranularity:
892
pos = previousParagraphPosition(startForPlatform(), lineDirectionPointForBlockDirectionNavigation(START));
894
case SentenceBoundary:
895
pos = startOfSentence(startForPlatform());
898
pos = logicalStartOfLine(startForPlatform());
900
case ParagraphBoundary:
901
pos = startOfParagraph(startForPlatform());
903
case DocumentBoundary:
904
pos = startForPlatform();
905
if (isEditablePosition(pos.deepEquivalent()))
906
pos = startOfEditableContent(pos);
908
pos = startOfDocument(pos);
914
static bool isBoundary(TextGranularity granularity)
916
return granularity == LineBoundary || granularity == ParagraphBoundary || granularity == DocumentBoundary;
919
bool FrameSelection::modify(EAlteration alter, SelectionDirection direction, TextGranularity granularity, EUserTriggered userTriggered)
921
if (userTriggered == UserTriggered) {
922
FrameSelection trialFrameSelection;
923
trialFrameSelection.setSelection(m_selection);
924
trialFrameSelection.modify(alter, direction, granularity, NotUserTriggered);
926
bool change = shouldChangeSelection(trialFrameSelection.selection());
930
if (trialFrameSelection.selection().isRange() && m_selection.isCaret() && !dispatchSelectStart())
934
willBeModified(alter, direction);
936
bool wasRange = m_selection.isRange();
937
Position originalStartPosition = m_selection.start();
938
VisiblePosition position;
941
if (alter == AlterationMove)
942
position = modifyMovingRight(granularity);
944
position = modifyExtendingRight(granularity);
946
case DirectionForward:
947
if (alter == AlterationExtend)
948
position = modifyExtendingForward(granularity);
950
position = modifyMovingForward(granularity);
953
if (alter == AlterationMove)
954
position = modifyMovingLeft(granularity);
956
position = modifyExtendingLeft(granularity);
958
case DirectionBackward:
959
if (alter == AlterationExtend)
960
position = modifyExtendingBackward(granularity);
962
position = modifyMovingBackward(granularity);
966
if (position.isNull())
969
if (isSpatialNavigationEnabled(m_frame))
970
if (!wasRange && alter == AlterationMove && position == originalStartPosition)
973
// Some of the above operations set an xPosForVerticalArrowNavigation.
974
// Setting a selection will clear it, so save it to possibly restore later.
975
// Note: the START position type is arbitrary because it is unused, it would be
976
// the requested position type if there were no xPosForVerticalArrowNavigation set.
977
LayoutUnit x = lineDirectionPointForBlockDirectionNavigation(START);
978
m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
982
moveTo(position, userTriggered);
984
case AlterationExtend:
986
if (!m_selection.isCaret()
987
&& (granularity == WordGranularity || granularity == ParagraphGranularity || granularity == LineGranularity)
988
&& m_frame && !m_frame->editor()->behavior().shouldExtendSelectionByWordOrLineAcrossCaret()) {
989
// Don't let the selection go across the base position directly. Needed to match mac
990
// behavior when, for instance, word-selecting backwards starting with the caret in
991
// the middle of a word and then word-selecting forward, leaving the caret in the
992
// same place where it was, instead of directly selecting to the end of the word.
993
VisibleSelection newSelection = m_selection;
994
newSelection.setExtent(position);
995
if (m_selection.isBaseFirst() != newSelection.isBaseFirst())
996
position = m_selection.base();
999
// Standard Mac behavior when extending to a boundary is grow the selection rather than leaving the
1000
// base in place and moving the extent. Matches NSTextView.
1001
if (!m_frame || !m_frame->editor()->behavior().shouldAlwaysGrowSelectionWhenExtendingToBoundary() || m_selection.isCaret() || !isBoundary(granularity))
1002
setExtent(position, userTriggered);
1004
TextDirection textDirection = directionOfEnclosingBlock();
1005
if (direction == DirectionForward || (textDirection == LTR && direction == DirectionRight) || (textDirection == RTL && direction == DirectionLeft))
1006
setEnd(position, userTriggered);
1008
setStart(position, userTriggered);
1013
if (granularity == LineGranularity || granularity == ParagraphGranularity)
1014
m_xPosForVerticalArrowNavigation = x;
1016
if (userTriggered == UserTriggered)
1017
m_granularity = CharacterGranularity;
1019
setCaretRectNeedsUpdate();
1024
// FIXME: Maybe baseline would be better?
1025
static bool absoluteCaretY(const VisiblePosition &c, int &y)
1027
IntRect rect = c.absoluteCaretBounds();
1030
y = rect.y() + rect.height() / 2;
1034
bool FrameSelection::modify(EAlteration alter, unsigned verticalDistance, VerticalDirection direction, EUserTriggered userTriggered, CursorAlignOnScroll align)
1036
if (!verticalDistance)
1039
if (userTriggered == UserTriggered) {
1040
FrameSelection trialFrameSelection;
1041
trialFrameSelection.setSelection(m_selection);
1042
trialFrameSelection.modify(alter, verticalDistance, direction, NotUserTriggered);
1044
bool change = shouldChangeSelection(trialFrameSelection.selection());
1049
willBeModified(alter, direction == DirectionUp ? DirectionBackward : DirectionForward);
1051
VisiblePosition pos;
1052
LayoutUnit xPos = 0;
1054
case AlterationMove:
1055
pos = VisiblePosition(direction == DirectionUp ? m_selection.start() : m_selection.end(), m_selection.affinity());
1056
xPos = lineDirectionPointForBlockDirectionNavigation(direction == DirectionUp ? START : END);
1057
m_selection.setAffinity(direction == DirectionUp ? UPSTREAM : DOWNSTREAM);
1059
case AlterationExtend:
1060
pos = VisiblePosition(m_selection.extent(), m_selection.affinity());
1061
xPos = lineDirectionPointForBlockDirectionNavigation(EXTENT);
1062
m_selection.setAffinity(DOWNSTREAM);
1067
if (!absoluteCaretY(pos, startY))
1069
if (direction == DirectionUp)
1073
VisiblePosition result;
1074
VisiblePosition next;
1075
for (VisiblePosition p = pos; ; p = next) {
1076
if (direction == DirectionUp)
1077
next = previousLinePosition(p, xPos);
1079
next = nextLinePosition(p, xPos);
1081
if (next.isNull() || next == p)
1084
if (!absoluteCaretY(next, nextY))
1086
if (direction == DirectionUp)
1088
if (nextY - startY > static_cast<int>(verticalDistance))
1090
if (nextY >= lastY) {
1096
if (result.isNull())
1100
case AlterationMove:
1101
moveTo(result, userTriggered, align);
1103
case AlterationExtend:
1104
setExtent(result, userTriggered);
1108
if (userTriggered == UserTriggered)
1109
m_granularity = CharacterGranularity;
1111
m_selection.setIsDirectional(shouldAlwaysUseDirectionalSelection(m_frame) || alter == AlterationExtend);
1116
LayoutUnit FrameSelection::lineDirectionPointForBlockDirectionNavigation(EPositionType type)
1126
pos = m_selection.start();
1129
pos = m_selection.end();
1132
pos = m_selection.base();
1135
pos = m_selection.extent();
1139
Frame* frame = pos.anchorNode()->document()->frame();
1143
if (m_xPosForVerticalArrowNavigation == NoXPosForVerticalArrowNavigation()) {
1144
VisiblePosition visiblePosition(pos, m_selection.affinity());
1145
// VisiblePosition creation can fail here if a node containing the selection becomes visibility:hidden
1146
// after the selection is created and before this function is called.
1147
x = visiblePosition.isNotNull() ? visiblePosition.lineDirectionPointForBlockDirectionNavigation() : 0;
1148
m_xPosForVerticalArrowNavigation = x;
1150
x = m_xPosForVerticalArrowNavigation;
1155
void FrameSelection::clear()
1157
m_granularity = CharacterGranularity;
1158
setSelection(VisibleSelection());
1161
void FrameSelection::prepareForDestruction()
1163
m_granularity = CharacterGranularity;
1165
#if ENABLE(TEXT_CARET)
1166
m_caretBlinkTimer.stop();
1169
RenderView* view = m_frame->contentRenderer();
1171
view->clearSelection();
1173
setSelection(VisibleSelection(), CloseTyping | ClearTypingStyle | DoNotUpdateAppearance);
1176
void FrameSelection::setStart(const VisiblePosition &pos, EUserTriggered trigger)
1178
if (m_selection.isBaseFirst())
1179
setBase(pos, trigger);
1181
setExtent(pos, trigger);
1184
void FrameSelection::setEnd(const VisiblePosition &pos, EUserTriggered trigger)
1186
if (m_selection.isBaseFirst())
1187
setExtent(pos, trigger);
1189
setBase(pos, trigger);
1192
void FrameSelection::setBase(const VisiblePosition &pos, EUserTriggered userTriggered)
1194
const bool selectionHasDirection = true;
1195
setSelection(VisibleSelection(pos.deepEquivalent(), m_selection.extent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1198
void FrameSelection::setExtent(const VisiblePosition &pos, EUserTriggered userTriggered)
1200
const bool selectionHasDirection = true;
1201
setSelection(VisibleSelection(m_selection.base(), pos.deepEquivalent(), pos.affinity(), selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1204
void FrameSelection::setBase(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1206
const bool selectionHasDirection = true;
1207
setSelection(VisibleSelection(pos, m_selection.extent(), affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1210
void FrameSelection::setExtent(const Position &pos, EAffinity affinity, EUserTriggered userTriggered)
1212
const bool selectionHasDirection = true;
1213
setSelection(VisibleSelection(m_selection.base(), pos, affinity, selectionHasDirection), CloseTyping | ClearTypingStyle | userTriggered);
1216
void CaretBase::clearCaretRect()
1218
m_caretLocalRect = LayoutRect();
1221
bool CaretBase::updateCaretRect(Document* document, const VisiblePosition& caretPosition)
1223
document->updateStyleIfNeeded();
1224
m_caretLocalRect = LayoutRect();
1226
m_caretRectNeedsUpdate = false;
1228
if (caretPosition.isNull())
1231
ASSERT(caretPosition.deepEquivalent().deprecatedNode()->renderer());
1233
// First compute a rect local to the renderer at the selection start.
1234
RenderObject* renderer;
1235
LayoutRect localRect = caretPosition.localCaretRect(renderer);
1237
// Get the renderer that will be responsible for painting the caret
1238
// (which is either the renderer we just found, or one of its containers).
1239
RenderObject* caretPainter = caretRenderer(caretPosition.deepEquivalent().deprecatedNode());
1241
// Compute an offset between the renderer and the caretPainter.
1242
bool unrooted = false;
1243
while (renderer != caretPainter) {
1244
RenderObject* containerObject = renderer->container();
1245
if (!containerObject) {
1249
localRect.move(renderer->offsetFromContainer(containerObject, localRect.location()));
1250
renderer = containerObject;
1254
m_caretLocalRect = localRect;
1259
static inline bool caretRendersInsideNode(Node* node)
1261
return node && !isTableElement(node) && !editingIgnoresContent(node);
1264
RenderObject* CaretBase::caretRenderer(Node* node) const
1269
RenderObject* renderer = node->renderer();
1273
// if caretNode is a block and caret is inside it then caret should be painted by that block
1274
bool paintedByBlock = renderer->isBlockFlow() && caretRendersInsideNode(node);
1275
return paintedByBlock ? renderer : renderer->containingBlock();
1278
RenderObject* FrameSelection::caretRenderer() const
1280
return CaretBase::caretRenderer(m_selection.start().deprecatedNode());
1283
RenderObject* DragCaretController::caretRenderer() const
1285
return CaretBase::caretRenderer(m_position.deepEquivalent().deprecatedNode());
1288
static bool isNonOrphanedCaret(const VisibleSelection& selection)
1290
return selection.isCaret() && !selection.start().isOrphan() && !selection.end().isOrphan();
1293
LayoutRect FrameSelection::localCaretRect()
1295
if (shouldUpdateCaretRect()) {
1296
if (!isNonOrphanedCaret(m_selection))
1298
else if (updateCaretRect(m_frame->document(), VisiblePosition(m_selection.start(), m_selection.affinity())))
1299
m_absCaretBoundsDirty = true;
1302
return localCaretRectWithoutUpdate();
1305
IntRect CaretBase::absoluteBoundsForLocalRect(Node* node, const LayoutRect& rect) const
1307
RenderObject* caretPainter = caretRenderer(node);
1311
LayoutRect localRect(rect);
1312
if (caretPainter->isBox())
1313
toRenderBox(caretPainter)->flipForWritingMode(localRect);
1314
return caretPainter->localToAbsoluteQuad(FloatRect(localRect), SnapOffsetForTransforms).enclosingBoundingBox();
1317
IntRect FrameSelection::absoluteCaretBounds()
1319
recomputeCaretRect();
1320
return m_absCaretBounds;
1323
static LayoutRect repaintRectForCaret(LayoutRect caret)
1325
if (caret.isEmpty())
1326
return LayoutRect();
1327
// Ensure that the dirty rect intersects the block that paints the caret even in the case where
1328
// the caret itself is just outside the block. See <https://bugs.webkit.org/show_bug.cgi?id=19086>.
1334
IntRect CaretBase::caretRepaintRect(Node* node) const
1336
return absoluteBoundsForLocalRect(node, repaintRectForCaret(localCaretRectWithoutUpdate()));
1339
bool FrameSelection::recomputeCaretRect()
1341
if (!shouldUpdateCaretRect())
1347
FrameView* v = m_frame->document()->view();
1351
LayoutRect oldRect = localCaretRectWithoutUpdate();
1352
LayoutRect newRect = localCaretRect();
1353
if (oldRect == newRect && !m_absCaretBoundsDirty)
1356
IntRect oldAbsCaretBounds = m_absCaretBounds;
1357
// FIXME: Rename m_caretRect to m_localCaretRect.
1358
m_absCaretBounds = absoluteBoundsForLocalRect(m_selection.start().deprecatedNode(), localCaretRectWithoutUpdate());
1359
m_absCaretBoundsDirty = false;
1361
if (oldAbsCaretBounds == m_absCaretBounds)
1364
#if ENABLE(TEXT_CARET)
1365
IntRect oldAbsoluteCaretRepaintBounds = m_absoluteCaretRepaintBounds;
1368
// We believe that we need to inflate the local rect before transforming it to obtain the repaint bounds.
1369
m_absoluteCaretRepaintBounds = caretRepaintRect(m_selection.start().deprecatedNode());
1371
#if ENABLE(TEXT_CARET)
1372
if (RenderView* view = m_frame->document()->renderView()) {
1373
// FIXME: make caret repainting container-aware.
1374
view->repaintRectangleInViewAndCompositedLayers(oldAbsoluteCaretRepaintBounds, false);
1375
if (shouldRepaintCaret(view, isContentEditable()))
1376
view->repaintRectangleInViewAndCompositedLayers(m_absoluteCaretRepaintBounds, false);
1382
bool CaretBase::shouldRepaintCaret(const RenderView* view, bool isContentEditable) const
1385
Frame* frame = view->frameView() ? view->frameView()->frame() : 0; // The frame where the selection started.
1386
bool caretBrowsing = frame && frame->settings() && frame->settings()->caretBrowsingEnabled();
1387
return (caretBrowsing || isContentEditable);
1390
void FrameSelection::invalidateCaretRect()
1395
CaretBase::invalidateCaretRect(m_selection.start().deprecatedNode(), recomputeCaretRect());
1398
void CaretBase::invalidateCaretRect(Node* node, bool caretRectChanged)
1400
// EDIT FIXME: This is an unfortunate hack.
1401
// Basically, we can't trust this layout position since we
1402
// can't guarantee that the check to see if we are in unrendered
1403
// content will work at this point. We may have to wait for
1404
// a layout and re-render of the document to happen. So, resetting this
1405
// flag will cause another caret layout to happen the first time
1406
// that we try to paint the caret after this call. That one will work since
1407
// it happens after the document has accounted for any editing
1408
// changes which may have been done.
1409
// And, we need to leave this layout here so the caret moves right
1410
// away after clicking.
1411
m_caretRectNeedsUpdate = true;
1413
if (caretRectChanged)
1416
if (RenderView* view = node->document()->renderView()) {
1417
if (shouldRepaintCaret(view, node->isContentEditable(Node::UserSelectAllIsAlwaysNonEditable)))
1418
view->repaintRectangleInViewAndCompositedLayers(caretRepaintRect(node), false);
1422
void FrameSelection::paintCaret(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect)
1424
if (m_selection.isCaret() && m_caretPaint)
1425
CaretBase::paintCaret(m_selection.start().deprecatedNode(), context, paintOffset, clipRect);
1428
void CaretBase::paintCaret(Node* node, GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1430
#if ENABLE(TEXT_CARET)
1431
if (m_caretVisibility == Hidden)
1434
LayoutRect drawingRect = localCaretRectWithoutUpdate();
1435
RenderObject* renderer = caretRenderer(node);
1436
if (renderer && renderer->isBox())
1437
toRenderBox(renderer)->flipForWritingMode(drawingRect);
1438
drawingRect.moveBy(paintOffset);
1439
LayoutRect caret = intersection(drawingRect, clipRect);
1440
if (caret.isEmpty())
1443
Color caretColor = Color::black;
1444
ColorSpace colorSpace = ColorSpaceDeviceRGB;
1445
Element* element = node->rootEditableElement();
1446
if (element && element->renderer()) {
1447
caretColor = element->renderer()->style()->visitedDependentColor(CSSPropertyColor);
1448
colorSpace = element->renderer()->style()->colorSpace();
1451
context->fillRect(caret, caretColor, colorSpace);
1454
UNUSED_PARAM(context);
1455
UNUSED_PARAM(paintOffset);
1456
UNUSED_PARAM(clipRect);
1460
void FrameSelection::debugRenderer(RenderObject *r, bool selected) const
1462
if (r->node()->isElementNode()) {
1463
Element* element = static_cast<Element *>(r->node());
1464
fprintf(stderr, "%s%s\n", selected ? "==> " : " ", element->localName().string().utf8().data());
1465
} else if (r->isText()) {
1466
RenderText* textRenderer = toRenderText(r);
1467
if (!textRenderer->textLength() || !textRenderer->firstTextBox()) {
1468
fprintf(stderr, "%s#text (empty)\n", selected ? "==> " : " ");
1472
static const int max = 36;
1473
String text = textRenderer->text();
1474
int textLength = text.length();
1477
if (r->node() == m_selection.start().containerNode())
1478
offset = m_selection.start().computeOffsetInContainerNode();
1479
else if (r->node() == m_selection.end().containerNode())
1480
offset = m_selection.end().computeOffsetInContainerNode();
1483
InlineTextBox* box = textRenderer->findNextInlineTextBox(offset, pos);
1484
text = text.substring(box->start(), box->len());
1490
// text is shorter than max
1491
if (textLength < max) {
1494
} else if (pos - mid < 0) {
1495
// too few characters to left
1496
show = text.left(max - 3) + "...";
1498
} else if (pos - mid >= 0 && pos + mid <= textLength) {
1499
// enough characters on each side
1500
show = "..." + text.substring(pos - mid + 3, max - 6) + "...";
1503
// too few characters on right
1504
show = "..." + text.right(max - 3);
1505
caret = pos - (textLength - show.length());
1508
show.replace('\n', ' ');
1509
show.replace('\r', ' ');
1510
fprintf(stderr, "==> #text : \"%s\" at offset %d\n", show.utf8().data(), pos);
1511
fprintf(stderr, " ");
1512
for (int i = 0; i < caret; i++)
1513
fprintf(stderr, " ");
1514
fprintf(stderr, "^\n");
1516
if ((int)text.length() > max)
1517
text = text.left(max - 3) + "...";
1519
text = text.left(max);
1520
fprintf(stderr, " #text : \"%s\"\n", text.utf8().data());
1525
bool FrameSelection::contains(const LayoutPoint& point)
1527
Document* document = m_frame->document();
1529
// Treat a collapsed selection like no selection.
1532
if (!document->renderer())
1535
HitTestRequest request(HitTestRequest::ReadOnly |
1536
HitTestRequest::Active);
1537
HitTestResult result(point);
1538
document->renderView()->hitTest(request, result);
1539
Node* innerNode = result.innerNode();
1540
if (!innerNode || !innerNode->renderer())
1543
VisiblePosition visiblePos(innerNode->renderer()->positionForPoint(result.localPoint()));
1544
if (visiblePos.isNull())
1547
if (m_selection.visibleStart().isNull() || m_selection.visibleEnd().isNull())
1550
Position start(m_selection.visibleStart().deepEquivalent());
1551
Position end(m_selection.visibleEnd().deepEquivalent());
1552
Position p(visiblePos.deepEquivalent());
1554
return comparePositions(start, p) <= 0 && comparePositions(p, end) <= 0;
1557
// Workaround for the fact that it's hard to delete a frame.
1558
// Call this after doing user-triggered selections to make it easy to delete the frame you entirely selected.
1559
// Can't do this implicitly as part of every setSelection call because in some contexts it might not be good
1560
// for the focus to move to another frame. So instead we call it from places where we are selecting with the
1561
// mouse or the keyboard after setting the selection.
1562
void FrameSelection::selectFrameElementInParentIfFullySelected()
1564
// Find the parent frame; if there is none, then we have nothing to do.
1565
Frame* parent = m_frame->tree()->parent();
1568
Page* page = m_frame->page();
1572
// Check if the selection contains the entire frame contents; if not, then there is nothing to do.
1575
if (!isStartOfDocument(selection().visibleStart()))
1577
if (!isEndOfDocument(selection().visibleEnd()))
1580
// Get to the <iframe> or <frame> (or even <object>) element in the parent frame.
1581
Element* ownerElement = m_frame->ownerElement();
1584
ContainerNode* ownerElementParent = ownerElement->parentNode();
1585
if (!ownerElementParent)
1588
// 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.
1589
if (!ownerElementParent->rendererIsEditable())
1592
// Create compute positions before and after the element.
1593
unsigned ownerElementNodeIndex = ownerElement->nodeIndex();
1594
VisiblePosition beforeOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex, Position::PositionIsOffsetInAnchor)));
1595
VisiblePosition afterOwnerElement(VisiblePosition(Position(ownerElementParent, ownerElementNodeIndex + 1, Position::PositionIsOffsetInAnchor), VP_UPSTREAM_IF_POSSIBLE));
1597
// Focus on the parent frame, and then select from before this element to after.
1598
VisibleSelection newSelection(beforeOwnerElement, afterOwnerElement);
1599
if (parent->selection()->shouldChangeSelection(newSelection)) {
1600
page->focusController()->setFocusedFrame(parent);
1601
parent->selection()->setSelection(newSelection);
1605
void FrameSelection::selectAll()
1607
Document* document = m_frame->document();
1609
if (document->focusedNode() && document->focusedNode()->hasTagName(selectTag)) {
1610
HTMLSelectElement* selectElement = toHTMLSelectElement(document->focusedNode());
1611
if (selectElement->canSelectAll()) {
1612
selectElement->selectAll();
1617
RefPtr<Node> root = 0;
1618
Node* selectStartTarget = 0;
1619
if (isContentEditable()) {
1620
root = highestEditableRoot(m_selection.start());
1621
if (Node* shadowRoot = m_selection.nonBoundaryShadowTreeRootNode())
1622
selectStartTarget = shadowRoot->shadowAncestorNode();
1624
selectStartTarget = root.get();
1626
root = m_selection.nonBoundaryShadowTreeRootNode();
1628
selectStartTarget = root->shadowAncestorNode();
1630
root = document->documentElement();
1631
selectStartTarget = document->body();
1637
if (selectStartTarget && !selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true)))
1640
VisibleSelection newSelection(VisibleSelection::selectionFromContentsOfNode(root.get()));
1642
if (shouldChangeSelection(newSelection))
1643
setSelection(newSelection);
1645
selectFrameElementInParentIfFullySelected();
1646
notifyRendererOfSelectionChange(UserTriggered);
1649
bool FrameSelection::setSelectedRange(Range* range, EAffinity affinity, bool closeTyping)
1651
if (!range || !range->startContainer() || !range->endContainer())
1653
ASSERT(range->startContainer()->document() == range->endContainer()->document());
1655
m_frame->document()->updateLayoutIgnorePendingStylesheets();
1657
// Non-collapsed ranges are not allowed to start at the end of a line that is wrapped,
1658
// they start at the beginning of the next line instead
1659
ExceptionCode ec = 0;
1660
bool collapsed = range->collapsed(ec);
1664
// FIXME: Can we provide extentAffinity?
1665
VisiblePosition visibleStart(range->startPosition(), collapsed ? affinity : DOWNSTREAM);
1666
VisiblePosition visibleEnd(range->endPosition(), SEL_DEFAULT_AFFINITY);
1667
setSelection(VisibleSelection(visibleStart, visibleEnd), ClearTypingStyle | (closeTyping ? CloseTyping : 0));
1671
bool FrameSelection::isInPasswordField() const
1673
HTMLTextFormControlElement* textControl = enclosingTextFormControl(start());
1674
return textControl && textControl->hasTagName(inputTag) && static_cast<HTMLInputElement*>(textControl)->isPasswordField();
1677
void FrameSelection::focusedOrActiveStateChanged()
1679
bool activeAndFocused = isFocusedAndActive();
1681
// Because RenderObject::selectionBackgroundColor() and
1682
// RenderObject::selectionForegroundColor() check if the frame is active,
1683
// we have to update places those colors were painted.
1684
if (RenderView* view = m_frame->document()->renderView())
1685
view->repaintRectangleInViewAndCompositedLayers(enclosingIntRect(bounds()));
1687
// Caret appears in the active frame.
1688
if (activeAndFocused)
1689
setSelectionFromNone();
1690
setCaretVisibility(activeAndFocused ? Visible : Hidden);
1692
// Update for caps lock state
1693
m_frame->eventHandler()->capsLockStateMayHaveChanged();
1695
// Because StyleResolver::checkOneSelector() and
1696
// RenderTheme::isFocused() check if the frame is active, we have to
1697
// update style and theme state that depended on those.
1698
if (Node* node = m_frame->document()->focusedNode()) {
1699
node->setNeedsStyleRecalc();
1700
if (RenderObject* renderer = node->renderer())
1701
if (renderer && renderer->style()->hasAppearance())
1702
renderer->theme()->stateChanged(renderer, FocusState);
1705
// Secure keyboard entry is set by the active frame.
1706
if (m_frame->document()->useSecureKeyboardEntryWhenActive())
1707
setUseSecureKeyboardEntry(activeAndFocused);
1710
void FrameSelection::pageActivationChanged()
1712
focusedOrActiveStateChanged();
1715
void FrameSelection::updateSecureKeyboardEntryIfActive()
1717
if (m_frame->document() && isFocusedAndActive())
1718
setUseSecureKeyboardEntry(m_frame->document()->useSecureKeyboardEntryWhenActive());
1721
void FrameSelection::setUseSecureKeyboardEntry(bool enable)
1724
enableSecureTextInput();
1726
disableSecureTextInput();
1729
void FrameSelection::setFocused(bool flag)
1731
if (m_focused == flag)
1735
focusedOrActiveStateChanged();
1738
bool FrameSelection::isFocusedAndActive() const
1740
return m_focused && m_frame->page() && m_frame->page()->focusController()->isActive();
1743
inline static bool shouldStopBlinkingDueToTypingCommand(Frame* frame)
1745
return frame->editor()->lastEditCommand() && frame->editor()->lastEditCommand()->shouldStopCaretBlinking();
1748
void FrameSelection::updateAppearance()
1750
#if ENABLE(TEXT_CARET)
1751
bool caretRectChangedOrCleared = recomputeCaretRect();
1753
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1754
bool shouldBlink = caretIsVisible() && isCaret() && (isContentEditable() || caretBrowsing);
1756
// If the caret moved, stop the blink timer so we can restart with a
1757
// black caret in the new location.
1758
if (caretRectChangedOrCleared || !shouldBlink || shouldStopBlinkingDueToTypingCommand(m_frame))
1759
m_caretBlinkTimer.stop();
1761
// Start blinking with a black caret. Be sure not to restart if we're
1762
// already blinking in the right location.
1763
if (shouldBlink && !m_caretBlinkTimer.isActive()) {
1764
if (double blinkInterval = m_frame->page()->theme()->caretBlinkInterval())
1765
m_caretBlinkTimer.startRepeating(blinkInterval);
1767
if (!m_caretPaint) {
1768
m_caretPaint = true;
1769
invalidateCaretRect();
1774
RenderView* view = m_frame->contentRenderer();
1778
// Construct a new VisibleSolution, since m_selection is not necessarily valid, and the following steps
1779
// assume a valid selection. See <https://bugs.webkit.org/show_bug.cgi?id=69563> and <rdar://problem/10232866>.
1780
VisibleSelection selection(m_selection.visibleStart(), m_selection.visibleEnd());
1782
if (!selection.isRange()) {
1783
view->clearSelection();
1787
// Use the rightmost candidate for the start of the selection, and the leftmost candidate for the end of the selection.
1788
// Example: foo <a>bar</a>. Imagine that a line wrap occurs after 'foo', and that 'bar' is selected. If we pass [foo, 3]
1789
// as the start of the selection, the selection painting code will think that content on the line containing 'foo' is selected
1790
// and will fill the gap before 'bar'.
1791
Position startPos = selection.start();
1792
Position candidate = startPos.downstream();
1793
if (candidate.isCandidate())
1794
startPos = candidate;
1795
Position endPos = selection.end();
1796
candidate = endPos.upstream();
1797
if (candidate.isCandidate())
1800
// We can get into a state where the selection endpoints map to the same VisiblePosition when a selection is deleted
1801
// because we don't yet notify the FrameSelection of text removal.
1802
if (startPos.isNotNull() && endPos.isNotNull() && selection.visibleStart() != selection.visibleEnd()) {
1803
RenderObject* startRenderer = startPos.deprecatedNode()->renderer();
1804
RenderObject* endRenderer = endPos.deprecatedNode()->renderer();
1805
view->setSelection(startRenderer, startPos.deprecatedEditingOffset(), endRenderer, endPos.deprecatedEditingOffset());
1809
void FrameSelection::setCaretVisibility(CaretVisibility visibility)
1811
if (caretVisibility() == visibility)
1814
#if ENABLE(TEXT_CARET)
1815
m_frame->document()->updateLayoutIgnorePendingStylesheets();
1817
m_caretPaint = false;
1818
invalidateCaretRect();
1820
CaretBase::setCaretVisibility(visibility);
1822
m_frame->document()->updateStyleIfNeeded();
1828
void FrameSelection::caretBlinkTimerFired(Timer<FrameSelection>*)
1830
#if ENABLE(TEXT_CARET)
1831
ASSERT(caretIsVisible());
1833
bool caretPaint = m_caretPaint;
1834
if (isCaretBlinkingSuspended() && caretPaint)
1836
m_caretPaint = !caretPaint;
1837
invalidateCaretRect();
1841
void FrameSelection::notifyRendererOfSelectionChange(EUserTriggered userTriggered)
1843
m_frame->document()->updateStyleIfNeeded();
1845
if (!rootEditableElement())
1848
if (HTMLTextFormControlElement* textControl = enclosingTextFormControl(start()))
1849
textControl->selectionChanged(userTriggered == UserTriggered);
1852
// Helper function that tells whether a particular node is an element that has an entire
1853
// Frame and FrameView, a <frame>, <iframe>, or <object>.
1854
static bool isFrameElement(const Node* n)
1858
RenderObject* renderer = n->renderer();
1859
if (!renderer || !renderer->isWidget())
1861
Widget* widget = toRenderWidget(renderer)->widget();
1862
return widget && widget->isFrameView();
1865
void FrameSelection::setFocusedNodeIfNeeded()
1867
if (isNone() || !isFocused())
1870
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
1871
if (caretBrowsing) {
1872
if (Node* anchor = enclosingAnchorElement(base())) {
1873
m_frame->page()->focusController()->setFocusedNode(anchor, m_frame);
1878
if (Node* target = rootEditableElement()) {
1879
// Walk up the DOM tree to search for a node to focus.
1881
// We don't want to set focus on a subframe when selecting in a parent frame,
1882
// so add the !isFrameElement check here. There's probably a better way to make this
1883
// work in the long term, but this is the safest fix at this time.
1884
if (target && target->isMouseFocusable() && !isFrameElement(target)) {
1885
m_frame->page()->focusController()->setFocusedNode(target, m_frame);
1888
target = target->parentOrHostNode();
1890
m_frame->document()->setFocusedNode(0);
1894
m_frame->page()->focusController()->setFocusedNode(0, m_frame);
1897
void DragCaretController::paintDragCaret(Frame* frame, GraphicsContext* p, const LayoutPoint& paintOffset, const LayoutRect& clipRect) const
1899
#if ENABLE(TEXT_CARET)
1900
if (m_position.deepEquivalent().deprecatedNode()->document()->frame() == frame)
1901
paintCaret(m_position.deepEquivalent().deprecatedNode(), p, paintOffset, clipRect);
1903
UNUSED_PARAM(frame);
1905
UNUSED_PARAM(paintOffset);
1906
UNUSED_PARAM(clipRect);
1910
PassRefPtr<StylePropertySet> FrameSelection::copyTypingStyle() const
1912
if (!m_typingStyle || !m_typingStyle->style())
1914
return m_typingStyle->style()->copy();
1917
bool FrameSelection::shouldDeleteSelection(const VisibleSelection& selection) const
1919
return m_frame->editor()->client()->shouldDeleteRange(selection.toNormalizedRange().get());
1922
FloatRect FrameSelection::bounds(bool clipToVisibleContent) const
1924
RenderView* root = m_frame->contentRenderer();
1925
FrameView* view = m_frame->view();
1927
return LayoutRect();
1929
LayoutRect selectionRect = root->selectionBounds(clipToVisibleContent);
1930
return clipToVisibleContent ? intersection(selectionRect, view->visibleContentRect()) : selectionRect;
1933
void FrameSelection::getClippedVisibleTextRectangles(Vector<FloatRect>& rectangles) const
1935
RenderView* root = m_frame->contentRenderer();
1939
FloatRect visibleContentRect = m_frame->view()->visibleContentRect();
1941
Vector<FloatQuad> quads;
1942
toNormalizedRange()->textQuads(quads, true);
1944
size_t size = quads.size();
1945
for (size_t i = 0; i < size; ++i) {
1946
FloatRect intersectionRect = intersection(quads[i].enclosingBoundingBox(), visibleContentRect);
1947
if (!intersectionRect.isEmpty())
1948
rectangles.append(intersectionRect);
1952
// Scans logically forward from "start", including any child frames.
1953
static HTMLFormElement* scanForForm(Node* start)
1955
for (Node* node = start; node; node = node->traverseNextNode()) {
1956
if (node->hasTagName(formTag))
1957
return static_cast<HTMLFormElement*>(node);
1958
if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1959
return static_cast<HTMLFormControlElement*>(node)->form();
1960
if (node->hasTagName(frameTag) || node->hasTagName(iframeTag)) {
1961
Node* childDocument = static_cast<HTMLFrameElementBase*>(node)->contentDocument();
1962
if (HTMLFormElement* frameResult = scanForForm(childDocument))
1969
// We look for either the form containing the current focus, or for one immediately after it
1970
HTMLFormElement* FrameSelection::currentForm() const
1972
// Start looking either at the active (first responder) node, or where the selection is.
1973
Node* start = m_frame->document()->focusedNode();
1975
start = this->start().deprecatedNode();
1977
// Try walking up the node tree to find a form element.
1979
for (node = start; node; node = node->parentNode()) {
1980
if (node->hasTagName(formTag))
1981
return static_cast<HTMLFormElement*>(node);
1982
if (node->isHTMLElement() && toHTMLElement(node)->isFormControlElement())
1983
return static_cast<HTMLFormControlElement*>(node)->form();
1986
// Try walking forward in the node tree to find a form element.
1987
return scanForForm(start);
1990
void FrameSelection::revealSelection(const ScrollAlignment& alignment, RevealExtentOption revealExtentOption)
1994
switch (selectionType()) {
1995
case VisibleSelection::NoSelection:
1997
case VisibleSelection::CaretSelection:
1998
rect = absoluteCaretBounds();
2000
case VisibleSelection::RangeSelection:
2001
rect = revealExtentOption == RevealExtent ? VisiblePosition(extent()).absoluteCaretBounds() : enclosingIntRect(bounds(false));
2005
Position start = this->start();
2006
ASSERT(start.deprecatedNode());
2007
if (start.deprecatedNode() && start.deprecatedNode()->renderer()) {
2008
// FIXME: This code only handles scrolling the startContainer's layer, but
2009
// the selection rect could intersect more than just that.
2010
// See <rdar://problem/4799899>.
2011
if (start.deprecatedNode()->renderer()->scrollRectToVisible(rect, alignment, alignment))
2016
void FrameSelection::setSelectionFromNone()
2018
// Put a caret inside the body if the entire frame is editable (either the
2019
// entire WebView is editable or designMode is on for this document).
2021
Document* document = m_frame->document();
2022
bool caretBrowsing = m_frame->settings() && m_frame->settings()->caretBrowsingEnabled();
2023
if (!isNone() || !(document->rendererIsEditable() || caretBrowsing))
2026
Node* node = document->documentElement();
2027
while (node && !node->hasTagName(bodyTag))
2028
node = node->traverseNextNode();
2030
setSelection(VisibleSelection(firstPositionInOrBeforeNode(node), DOWNSTREAM));
2033
bool FrameSelection::shouldChangeSelection(const VisibleSelection& newSelection) const
2035
return m_frame->editor()->shouldChangeSelection(selection(), newSelection, newSelection.affinity(), false);
2038
bool FrameSelection::dispatchSelectStart()
2040
Node* selectStartTarget = m_selection.extent().containerNode();
2041
if (!selectStartTarget)
2044
return selectStartTarget->dispatchEvent(Event::create(eventNames().selectstartEvent, true, true));
2047
inline bool FrameSelection::visualWordMovementEnabled() const
2049
Settings* settings = m_frame ? m_frame->settings() : 0;
2050
return settings && settings->visualWordMovementEnabled();
2055
void FrameSelection::formatForDebugger(char* buffer, unsigned length) const
2057
m_selection.formatForDebugger(buffer, length);
2060
void FrameSelection::showTreeForThis() const
2062
m_selection.showTreeForThis();
2071
void showTree(const WebCore::FrameSelection& sel)
2073
sel.showTreeForThis();
2076
void showTree(const WebCore::FrameSelection* sel)
2079
sel->showTreeForThis();