2 * Copyright (C) 2004, 2005, 2006 Apple Computer, 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 "core/editing/VisibleSelection.h"
29 #include "bindings/core/v8/ExceptionState.h"
30 #include "core/dom/Document.h"
31 #include "core/dom/Element.h"
32 #include "core/dom/Range.h"
33 #include "core/editing/TextIterator.h"
34 #include "core/editing/VisibleUnits.h"
35 #include "core/editing/htmlediting.h"
36 #include "core/rendering/RenderObject.h"
37 #include "platform/geometry/LayoutPoint.h"
38 #include "wtf/Assertions.h"
39 #include "wtf/text/CString.h"
40 #include "wtf/text/StringBuilder.h"
41 #include "wtf/unicode/CharacterNames.h"
49 VisibleSelection::VisibleSelection()
50 : m_affinity(DOWNSTREAM)
51 , m_changeObserver(nullptr)
52 , m_selectionType(NoSelection)
54 , m_isDirectional(false)
58 VisibleSelection::VisibleSelection(const Position& pos, EAffinity affinity, bool isDirectional)
61 , m_affinity(affinity)
62 , m_changeObserver(nullptr)
63 , m_isDirectional(isDirectional)
68 VisibleSelection::VisibleSelection(const Position& base, const Position& extent, EAffinity affinity, bool isDirectional)
71 , m_affinity(affinity)
72 , m_changeObserver(nullptr)
73 , m_isDirectional(isDirectional)
78 VisibleSelection::VisibleSelection(const VisiblePosition& pos, bool isDirectional)
79 : m_base(pos.deepEquivalent())
80 , m_extent(pos.deepEquivalent())
81 , m_affinity(pos.affinity())
82 , m_changeObserver(nullptr)
83 , m_isDirectional(isDirectional)
88 VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePosition& extent, bool isDirectional)
89 : m_base(base.deepEquivalent())
90 , m_extent(extent.deepEquivalent())
91 , m_affinity(base.affinity())
92 , m_changeObserver(nullptr)
93 , m_isDirectional(isDirectional)
98 VisibleSelection::VisibleSelection(const Range* range, EAffinity affinity, bool isDirectional)
99 : m_base(range->startPosition())
100 , m_extent(range->endPosition())
101 , m_affinity(affinity)
102 , m_changeObserver(nullptr)
103 , m_isDirectional(isDirectional)
108 VisibleSelection::VisibleSelection(const VisibleSelection& other)
109 : m_base(other.m_base)
110 , m_extent(other.m_extent)
111 , m_start(other.m_start)
113 , m_affinity(other.m_affinity)
114 , m_changeObserver(nullptr) // Observer is associated with only one VisibleSelection, so this should not be copied.
115 , m_selectionType(other.m_selectionType)
116 , m_baseIsFirst(other.m_baseIsFirst)
117 , m_isDirectional(other.m_isDirectional)
121 VisibleSelection& VisibleSelection::operator=(const VisibleSelection& other)
125 m_base = other.m_base;
126 m_extent = other.m_extent;
127 m_start = other.m_start;
129 m_affinity = other.m_affinity;
130 m_changeObserver = nullptr;
131 m_selectionType = other.m_selectionType;
132 m_baseIsFirst = other.m_baseIsFirst;
133 m_isDirectional = other.m_isDirectional;
137 VisibleSelection::~VisibleSelection()
144 VisibleSelection VisibleSelection::selectionFromContentsOfNode(Node* node)
146 ASSERT(!editingIgnoresContent(node));
147 return VisibleSelection(firstPositionInNode(node), lastPositionInNode(node), DOWNSTREAM);
150 void VisibleSelection::setBase(const Position& position)
152 Position oldBase = m_base;
155 if (m_base != oldBase)
159 void VisibleSelection::setBase(const VisiblePosition& visiblePosition)
161 Position oldBase = m_base;
162 m_base = visiblePosition.deepEquivalent();
164 if (m_base != oldBase)
168 void VisibleSelection::setExtent(const Position& position)
170 Position oldExtent = m_extent;
173 if (m_extent != oldExtent)
177 void VisibleSelection::setExtent(const VisiblePosition& visiblePosition)
179 Position oldExtent = m_extent;
180 m_extent = visiblePosition.deepEquivalent();
182 if (m_extent != oldExtent)
186 PassRefPtrWillBeRawPtr<Range> VisibleSelection::firstRange() const
190 Position start = m_start.parentAnchoredEquivalent();
191 Position end = m_end.parentAnchoredEquivalent();
192 return Range::create(*start.document(), start, end);
195 PassRefPtrWillBeRawPtr<Range> VisibleSelection::toNormalizedRange() const
200 // Make sure we have an updated layout since this function is called
201 // in the course of running edit commands which modify the DOM.
202 // Failing to call this can result in equivalentXXXPosition calls returning
203 // incorrect results.
204 m_start.document()->updateLayout();
206 // Check again, because updating layout can clear the selection.
212 // If the selection is a caret, move the range start upstream. This helps us match
213 // the conventions of text editors tested, which make style determinations based
214 // on the character before the caret, if any.
215 s = m_start.upstream().parentAnchoredEquivalent();
218 // If the selection is a range, select the minimum range that encompasses the selection.
219 // Again, this is to match the conventions of text editors tested, which make style
220 // determinations based on the first character of the selection.
221 // For instance, this operation helps to make sure that the "X" selected below is the
222 // only thing selected. The range should not be allowed to "leak" out to the end of the
223 // previous text node, or to the beginning of the next text node, each of which has a
226 // On a treasure map, <b>X</b> marks the spot.
230 s = m_start.downstream();
231 e = m_end.upstream();
232 if (comparePositions(s, e) > 0) {
233 // Make sure the start is before the end.
234 // The end can wind up before the start if collapsed whitespace is the only thing selected.
239 s = s.parentAnchoredEquivalent();
240 e = e.parentAnchoredEquivalent();
243 if (!s.containerNode() || !e.containerNode())
246 // VisibleSelections are supposed to always be valid. This constructor will ASSERT
247 // if a valid range could not be created, which is fine for this callsite.
248 return Range::create(*s.document(), s, e);
251 bool VisibleSelection::expandUsingGranularity(TextGranularity granularity)
256 // FIXME: Do we need to check all of them?
257 Position oldBase = m_base;
258 Position oldExtent = m_extent;
259 Position oldStart = m_start;
260 Position oldEnd = m_end;
261 validate(granularity);
262 if (m_base != oldBase || m_extent != oldExtent || m_start != oldStart || m_end != oldEnd)
267 static PassRefPtrWillBeRawPtr<Range> makeSearchRange(const Position& pos)
269 Node* node = pos.deprecatedNode();
272 Document& document = node->document();
273 if (!document.documentElement())
275 Element* boundary = enclosingBlockFlowElement(*node);
279 RefPtrWillBeRawPtr<Range> searchRange(Range::create(document));
280 TrackExceptionState exceptionState;
282 Position start(pos.parentAnchoredEquivalent());
283 searchRange->selectNodeContents(boundary, exceptionState);
284 searchRange->setStart(start.containerNode(), start.offsetInContainerNode(), exceptionState);
286 ASSERT(!exceptionState.hadException());
287 if (exceptionState.hadException())
290 return searchRange.release();
293 void VisibleSelection::appendTrailingWhitespace()
295 RefPtrWillBeRawPtr<Range> searchRange = makeSearchRange(m_end);
299 CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
300 bool changed = false;
302 for (; charIt.length(); charIt.advance(1)) {
303 UChar c = charIt.characterAt(0);
304 if ((!isSpaceOrNewline(c) && c != noBreakSpace) || c == '\n')
306 m_end = charIt.range()->endPosition();
313 void VisibleSelection::setBaseAndExtentToDeepEquivalents()
315 // Move the selection to rendered positions, if possible.
316 bool baseAndExtentEqual = m_base == m_extent;
317 if (m_base.isNotNull()) {
318 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent();
319 if (baseAndExtentEqual)
322 if (m_extent.isNotNull() && !baseAndExtentEqual)
323 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
325 // Make sure we do not have a dangling base or extent.
326 if (m_base.isNull() && m_extent.isNull())
327 m_baseIsFirst = true;
328 else if (m_base.isNull()) {
330 m_baseIsFirst = true;
331 } else if (m_extent.isNull()) {
333 m_baseIsFirst = true;
335 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0;
338 void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity)
348 switch (granularity) {
349 case CharacterGranularity:
350 // Don't do any expansion.
352 case WordGranularity: {
353 // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary).
354 // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in
355 // the document, select that last word (LeftWordIfOnBoundary).
356 // Edge case: If the caret is after the last word in a paragraph, select from the the end of the
357 // last word to the line break (also RightWordIfOnBoundary);
358 VisiblePosition start = VisiblePosition(m_start, m_affinity);
359 VisiblePosition originalEnd(m_end, m_affinity);
360 EWordSide side = RightWordIfOnBoundary;
361 if (isEndOfEditableOrNonEditableContent(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start)))
362 side = LeftWordIfOnBoundary;
363 m_start = startOfWord(start, side).deepEquivalent();
364 side = RightWordIfOnBoundary;
365 if (isEndOfEditableOrNonEditableContent(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd)))
366 side = LeftWordIfOnBoundary;
368 VisiblePosition wordEnd(endOfWord(originalEnd, side));
369 VisiblePosition end(wordEnd);
371 if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) {
372 // Select the paragraph break (the space from the end of a paragraph to the start of
373 // the next one) to match TextEdit.
374 end = wordEnd.next();
376 if (Element* table = isFirstPositionAfterTable(end)) {
377 // The paragraph break after the last paragraph in the last cell of a block table ends
378 // at the start of the paragraph after the table.
380 end = end.next(CannotCrossEditingBoundary);
390 m_end = end.deepEquivalent();
393 case SentenceGranularity: {
394 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
395 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
398 case LineGranularity: {
399 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
400 VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity));
401 // If the end of this line is at the end of a paragraph, include the space
402 // after the end of the line in the selection.
403 if (isEndOfParagraph(end)) {
404 VisiblePosition next = end.next();
405 if (next.isNotNull())
408 m_end = end.deepEquivalent();
412 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
413 m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent();
415 case ParagraphGranularity: {
416 VisiblePosition pos(m_start, m_affinity);
417 if (isStartOfLine(pos) && isEndOfEditableOrNonEditableContent(pos))
418 pos = pos.previous();
419 m_start = startOfParagraph(pos).deepEquivalent();
420 VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity));
422 // Include the "paragraph break" (the space from the end of this paragraph to the start
423 // of the next one) in the selection.
424 VisiblePosition end(visibleParagraphEnd.next());
426 if (Element* table = isFirstPositionAfterTable(end)) {
427 // The paragraph break after the last paragraph in the last cell of a block table ends
428 // at the start of the paragraph after the table, not at the position just after the table.
430 end = end.next(CannotCrossEditingBoundary);
431 // There is no parargraph break after the last paragraph in the last cell of an inline table.
433 end = visibleParagraphEnd;
437 end = visibleParagraphEnd;
439 m_end = end.deepEquivalent();
442 case DocumentBoundary:
443 m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent();
444 m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent();
446 case ParagraphBoundary:
447 m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent();
448 m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent();
450 case SentenceBoundary:
451 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
452 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
456 // Make sure we do not have a dangling start or end.
457 if (m_start.isNull())
463 void VisibleSelection::updateSelectionType()
465 if (m_start.isNull()) {
466 ASSERT(m_end.isNull());
467 m_selectionType = NoSelection;
468 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) {
469 m_selectionType = CaretSelection;
471 m_selectionType = RangeSelection;
473 // Affinity only makes sense for a caret
474 if (m_selectionType != CaretSelection)
475 m_affinity = DOWNSTREAM;
478 void VisibleSelection::validate(TextGranularity granularity)
480 setBaseAndExtentToDeepEquivalents();
481 setStartAndEndFromBaseAndExtentRespectingGranularity(granularity);
482 adjustSelectionToAvoidCrossingShadowBoundaries();
483 adjustSelectionToAvoidCrossingEditingBoundaries();
484 updateSelectionType();
486 if (selectionType() == RangeSelection) {
487 // "Constrain" the selection to be the smallest equivalent range of nodes.
488 // This is a somewhat arbitrary choice, but experience shows that it is
489 // useful to make to make the selection "canonical" (if only for
490 // purposes of comparing selections). This is an ideal point of the code
491 // to do this operation, since all selection changes that result in a RANGE
492 // come through here before anyone uses it.
493 // FIXME: Canonicalizing is good, but haven't we already done it (when we
494 // set these two positions to VisiblePosition deepEquivalent()s above)?
495 m_start = m_start.downstream();
496 m_end = m_end.upstream();
498 // FIXME: Position::downstream() or Position::upStream() might violate editing boundaries
499 // if an anchor node has a Shadow DOM. So we adjust selection to avoid crossing editing
500 // boundaries again. See https://bugs.webkit.org/show_bug.cgi?id=87463
501 adjustSelectionToAvoidCrossingEditingBoundaries();
505 // FIXME: This function breaks the invariant of this class.
506 // But because we use VisibleSelection to store values in editing commands for use when
507 // undoing the command, we need to be able to create a selection that while currently
508 // invalid, will be valid once the changes are undone. This is a design problem.
509 // To fix it we either need to change the invariants of VisibleSelection or create a new
510 // class for editing to use that can manipulate selections that are not currently valid.
511 void VisibleSelection::setWithoutValidation(const Position& base, const Position& extent)
513 ASSERT(!base.isNull());
514 ASSERT(!extent.isNull());
515 ASSERT(m_affinity == DOWNSTREAM);
518 m_baseIsFirst = comparePositions(base, extent) <= 0;
526 m_selectionType = base == extent ? CaretSelection : RangeSelection;
530 static Position adjustPositionForEnd(const Position& currentPosition, Node* startContainerNode)
532 TreeScope& treeScope = startContainerNode->treeScope();
534 ASSERT(currentPosition.containerNode()->treeScope() != treeScope);
536 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
537 if (ancestor->contains(startContainerNode))
538 return positionAfterNode(ancestor);
539 return positionBeforeNode(ancestor);
542 if (Node* lastChild = treeScope.rootNode().lastChild())
543 return positionAfterNode(lastChild);
548 static Position adjustPositionForStart(const Position& currentPosition, Node* endContainerNode)
550 TreeScope& treeScope = endContainerNode->treeScope();
552 ASSERT(currentPosition.containerNode()->treeScope() != treeScope);
554 if (Node* ancestor = treeScope.ancestorInThisScope(currentPosition.containerNode())) {
555 if (ancestor->contains(endContainerNode))
556 return positionBeforeNode(ancestor);
557 return positionAfterNode(ancestor);
560 if (Node* firstChild = treeScope.rootNode().firstChild())
561 return positionBeforeNode(firstChild);
566 void VisibleSelection::adjustSelectionToAvoidCrossingShadowBoundaries()
568 if (m_base.isNull() || m_start.isNull() || m_end.isNull())
571 if (m_start.anchorNode()->treeScope() == m_end.anchorNode()->treeScope())
575 m_extent = adjustPositionForEnd(m_end, m_start.containerNode());
578 m_extent = adjustPositionForStart(m_start, m_end.containerNode());
582 ASSERT(m_start.anchorNode()->treeScope() == m_end.anchorNode()->treeScope());
585 void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
587 if (m_base.isNull() || m_start.isNull() || m_end.isNull())
590 ContainerNode* baseRoot = highestEditableRoot(m_base);
591 ContainerNode* startRoot = highestEditableRoot(m_start);
592 ContainerNode* endRoot = highestEditableRoot(m_end);
594 Element* baseEditableAncestor = lowestEditableAncestor(m_base.containerNode());
596 // The base, start and end are all in the same region. No adjustment necessary.
597 if (baseRoot == startRoot && baseRoot == endRoot)
600 // The selection is based in editable content.
602 // If the start is outside the base's editable root, cap it at the start of that root.
603 // If the start is in non-editable content that is inside the base's editable root, put it
604 // at the first editable position after start inside the base's editable root.
605 if (startRoot != baseRoot) {
606 VisiblePosition first = firstEditableVisiblePositionAfterPositionInRoot(m_start, baseRoot);
607 m_start = first.deepEquivalent();
608 if (m_start.isNull()) {
609 ASSERT_NOT_REACHED();
613 // If the end is outside the base's editable root, cap it at the end of that root.
614 // If the end is in non-editable content that is inside the base's root, put it
615 // at the last editable position before the end inside the base's root.
616 if (endRoot != baseRoot) {
617 VisiblePosition last = lastEditableVisiblePositionBeforePositionInRoot(m_end, baseRoot);
618 m_end = last.deepEquivalent();
622 // The selection is based in non-editable content.
624 // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable
625 // pieces in non-editable content are atomic.
627 // The selection ends in editable content or non-editable content inside a different editable ancestor,
628 // move backward until non-editable content inside the same lowest editable ancestor is reached.
629 Element* endEditableAncestor = lowestEditableAncestor(m_end.containerNode());
630 if (endRoot || endEditableAncestor != baseEditableAncestor) {
632 Position p = previousVisuallyDistinctCandidate(m_end);
633 Element* shadowAncestor = endRoot ? endRoot->shadowHost() : 0;
634 if (p.isNull() && shadowAncestor)
635 p = positionAfterNode(shadowAncestor);
636 while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
637 Element* root = editableRootForPosition(p);
638 shadowAncestor = root ? root->shadowHost() : 0;
639 p = isAtomicNode(p.containerNode()) ? positionInParentBeforeNode(*p.containerNode()) : previousVisuallyDistinctCandidate(p);
640 if (p.isNull() && shadowAncestor)
641 p = positionAfterNode(shadowAncestor);
643 VisiblePosition previous(p);
645 if (previous.isNull()) {
646 // The selection crosses an Editing boundary. This is a
647 // programmer error in the editing code. Happy debugging!
648 ASSERT_NOT_REACHED();
650 m_extent = Position();
654 m_end = previous.deepEquivalent();
657 // The selection starts in editable content or non-editable content inside a different editable ancestor,
658 // move forward until non-editable content inside the same lowest editable ancestor is reached.
659 Element* startEditableAncestor = lowestEditableAncestor(m_start.containerNode());
660 if (startRoot || startEditableAncestor != baseEditableAncestor) {
661 Position p = nextVisuallyDistinctCandidate(m_start);
662 Element* shadowAncestor = startRoot ? startRoot->shadowHost() : 0;
663 if (p.isNull() && shadowAncestor)
664 p = positionBeforeNode(shadowAncestor);
665 while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
666 Element* root = editableRootForPosition(p);
667 shadowAncestor = root ? root->shadowHost() : 0;
668 p = isAtomicNode(p.containerNode()) ? positionInParentAfterNode(*p.containerNode()) : nextVisuallyDistinctCandidate(p);
669 if (p.isNull() && shadowAncestor)
670 p = positionBeforeNode(shadowAncestor);
672 VisiblePosition next(p);
675 // The selection crosses an Editing boundary. This is a
676 // programmer error in the editing code. Happy debugging!
677 ASSERT_NOT_REACHED();
679 m_extent = Position();
683 m_start = next.deepEquivalent();
687 // Correct the extent if necessary.
688 if (baseEditableAncestor != lowestEditableAncestor(m_extent.containerNode()))
689 m_extent = m_baseIsFirst ? m_end : m_start;
692 VisiblePosition VisibleSelection::visiblePositionRespectingEditingBoundary(const LayoutPoint& localPoint, Node* targetNode) const
694 if (!targetNode->renderer())
695 return VisiblePosition();
697 LayoutPoint selectionEndPoint = localPoint;
698 Element* editableElement = rootEditableElement();
700 if (editableElement && !editableElement->contains(targetNode)) {
701 if (!editableElement->renderer())
702 return VisiblePosition();
704 FloatPoint absolutePoint = targetNode->renderer()->localToAbsolute(FloatPoint(selectionEndPoint));
705 selectionEndPoint = roundedLayoutPoint(editableElement->renderer()->absoluteToLocal(absolutePoint));
706 targetNode = editableElement;
709 return VisiblePosition(targetNode->renderer()->positionForPoint(selectionEndPoint));
713 bool VisibleSelection::isContentEditable() const
715 return isEditablePosition(start());
718 bool VisibleSelection::hasEditableStyle() const
720 return isEditablePosition(start(), ContentIsEditable, DoNotUpdateStyle);
723 bool VisibleSelection::isContentRichlyEditable() const
725 return isRichlyEditablePosition(start());
728 Element* VisibleSelection::rootEditableElement() const
730 return editableRootForPosition(start());
733 Node* VisibleSelection::nonBoundaryShadowTreeRootNode() const
735 return start().deprecatedNode() ? start().deprecatedNode()->nonBoundaryShadowTreeRootNode() : 0;
738 VisibleSelection::ChangeObserver::ChangeObserver()
742 VisibleSelection::ChangeObserver::~ChangeObserver()
746 void VisibleSelection::setChangeObserver(ChangeObserver& observer)
748 ASSERT(!m_changeObserver);
749 m_changeObserver = &observer;
752 void VisibleSelection::clearChangeObserver()
754 ASSERT(m_changeObserver);
755 m_changeObserver = nullptr;
758 void VisibleSelection::didChange()
760 if (m_changeObserver)
761 m_changeObserver->didChangeVisibleSelection();
764 void VisibleSelection::trace(Visitor* visitor)
766 visitor->trace(m_base);
767 visitor->trace(m_extent);
768 visitor->trace(m_start);
769 visitor->trace(m_end);
770 visitor->trace(m_changeObserver);
773 static bool isValidPosition(const Position& position)
775 if (!position.inDocument())
778 if (position.anchorType() != Position::PositionIsOffsetInAnchor)
781 if (position.offsetInContainerNode() < 0)
784 const unsigned offset = static_cast<unsigned>(position.offsetInContainerNode());
785 const unsigned nodeLength = position.anchorNode()->lengthOfContents();
786 return offset <= nodeLength;
789 void VisibleSelection::validatePositionsIfNeeded()
791 if (!isValidPosition(m_base) || !isValidPosition(m_extent) || !isValidPosition(m_start) || !isValidPosition(m_end))
797 void VisibleSelection::debugPosition() const
799 fprintf(stderr, "VisibleSelection ===============\n");
801 if (!m_start.anchorNode())
802 fputs("pos: null", stderr);
803 else if (m_start == m_end) {
804 fprintf(stderr, "pos: %s ", m_start.anchorNode()->nodeName().utf8().data());
805 m_start.showAnchorTypeAndOffset();
807 fprintf(stderr, "start: %s ", m_start.anchorNode()->nodeName().utf8().data());
808 m_start.showAnchorTypeAndOffset();
809 fprintf(stderr, "end: %s ", m_end.anchorNode()->nodeName().utf8().data());
810 m_end.showAnchorTypeAndOffset();
813 fprintf(stderr, "================================\n");
816 void VisibleSelection::formatForDebugger(char* buffer, unsigned length) const
818 StringBuilder result;
822 result.appendLiteral("<none>");
824 const int FormatBufferSize = 1024;
825 char s[FormatBufferSize];
826 result.appendLiteral("from ");
827 start().formatForDebugger(s, FormatBufferSize);
829 result.appendLiteral(" to ");
830 end().formatForDebugger(s, FormatBufferSize);
834 strncpy(buffer, result.toString().utf8().data(), length - 1);
837 void VisibleSelection::showTreeForThis() const
839 if (start().anchorNode()) {
840 start().anchorNode()->showTreeAndMark(start().anchorNode(), "S", end().anchorNode(), "E");
841 fputs("start: ", stderr);
842 start().showAnchorTypeAndOffset();
843 fputs("end: ", stderr);
844 end().showAnchorTypeAndOffset();
854 void showTree(const blink::VisibleSelection& sel)
856 sel.showTreeForThis();
859 void showTree(const blink::VisibleSelection* sel)
862 sel->showTreeForThis();