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 "Selection.h"
32 #include "htmlediting.h"
33 #include "VisiblePosition.h"
34 #include "visible_units.h"
36 #include <wtf/Assertions.h>
41 Selection::Selection()
42 : m_affinity(DOWNSTREAM
)
43 , m_granularity(CharacterGranularity
)
49 Selection::Selection(const Position
& pos
, EAffinity affinity
)
52 , m_affinity(affinity
)
53 , m_granularity(CharacterGranularity
)
58 Selection::Selection(const Position
& base
, const Position
& extent
, EAffinity affinity
)
61 , m_affinity(affinity
)
62 , m_granularity(CharacterGranularity
)
67 Selection::Selection(const VisiblePosition
& pos
)
68 : m_base(pos
.deepEquivalent())
69 , m_extent(pos
.deepEquivalent())
70 , m_affinity(pos
.affinity())
71 , m_granularity(CharacterGranularity
)
76 Selection::Selection(const VisiblePosition
& base
, const VisiblePosition
& extent
)
77 : m_base(base
.deepEquivalent())
78 , m_extent(extent
.deepEquivalent())
79 , m_affinity(base
.affinity())
80 , m_granularity(CharacterGranularity
)
85 Selection::Selection(const Range
* range
, EAffinity affinity
)
86 : m_base(range
->startPosition())
87 , m_extent(range
->endPosition())
88 , m_affinity(affinity
)
89 , m_granularity(CharacterGranularity
)
94 Selection
Selection::selectionFromContentsOfNode(Node
* node
)
96 return Selection(Position(node
, 0), Position(node
, maxDeepOffset(node
)), DOWNSTREAM
);
99 void Selection::setBase(const Position
& position
)
105 void Selection::setBase(const VisiblePosition
& visiblePosition
)
107 m_base
= visiblePosition
.deepEquivalent();
111 void Selection::setExtent(const Position
& position
)
117 void Selection::setExtent(const VisiblePosition
& visiblePosition
)
119 m_extent
= visiblePosition
.deepEquivalent();
123 PassRefPtr
<Range
> Selection::toRange() const
128 // Make sure we have an updated layout since this function is called
129 // in the course of running edit commands which modify the DOM.
130 // Failing to call this can result in equivalentXXXPosition calls returning
131 // incorrect results.
132 m_start
.node()->document()->updateLayout();
134 // Check again, because updating layout can clear the selection.
140 // If the selection is a caret, move the range start upstream. This helps us match
141 // the conventions of text editors tested, which make style determinations based
142 // on the character before the caret, if any.
143 s
= rangeCompliantEquivalent(m_start
.upstream());
146 // If the selection is a range, select the minimum range that encompasses the selection.
147 // Again, this is to match the conventions of text editors tested, which make style
148 // determinations based on the first character of the selection.
149 // For instance, this operation helps to make sure that the "X" selected below is the
150 // only thing selected. The range should not be allowed to "leak" out to the end of the
151 // previous text node, or to the beginning of the next text node, each of which has a
154 // On a treasure map, <b>X</b> marks the spot.
158 s
= m_start
.downstream();
159 e
= m_end
.upstream();
160 if (Range::compareBoundaryPoints(s
.node(), s
.offset(), e
.node(), e
.offset()) > 0) {
161 // Make sure the start is before the end.
162 // The end can wind up before the start if collapsed whitespace is the only thing selected.
167 s
= rangeCompliantEquivalent(s
);
168 e
= rangeCompliantEquivalent(e
);
171 ExceptionCode ec
= 0;
172 RefPtr
<Range
> result(Range::create(s
.node()->document()));
173 result
->setStart(s
.node(), s
.offset(), ec
);
175 LOG_ERROR("Exception setting Range start from Selection: %d", ec
);
178 result
->setEnd(e
.node(), e
.offset(), ec
);
180 LOG_ERROR("Exception setting Range end from Selection: %d", ec
);
183 return result
.release();
186 bool Selection::expandUsingGranularity(TextGranularity granularity
)
191 m_granularity
= granularity
;
196 void Selection::validate()
198 // Move the selection to rendered positions, if possible.
199 bool baseAndExtentEqual
= m_base
== m_extent
;
200 if (m_base
.isNotNull()) {
201 m_base
= VisiblePosition(m_base
, m_affinity
).deepEquivalent();
202 if (baseAndExtentEqual
)
205 if (m_extent
.isNotNull() && !baseAndExtentEqual
)
206 m_extent
= VisiblePosition(m_extent
, m_affinity
).deepEquivalent();
208 // Make sure we do not have a dangling base or extent.
209 if (m_base
.isNull() && m_extent
.isNull())
210 m_baseIsFirst
= true;
211 else if (m_base
.isNull()) {
213 m_baseIsFirst
= true;
214 } else if (m_extent
.isNull()) {
216 m_baseIsFirst
= true;
218 m_baseIsFirst
= comparePositions(m_base
, m_extent
) <= 0;
229 // Expand the selection if requested.
230 switch (m_granularity
) {
231 case CharacterGranularity
:
232 // Don't do any expansion.
234 case WordGranularity
: {
235 // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary).
236 // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in
237 // the document, select that last word (LeftWordIfOnBoundary).
238 // Edge case: If the caret is after the last word in a paragraph, select from the the end of the
239 // last word to the line break (also RightWordIfOnBoundary);
240 VisiblePosition start
= VisiblePosition(m_start
, m_affinity
);
241 VisiblePosition
originalEnd(m_end
, m_affinity
);
242 EWordSide side
= RightWordIfOnBoundary
;
243 if (isEndOfDocument(start
) || (isEndOfLine(start
) && !isStartOfLine(start
) && !isEndOfParagraph(start
)))
244 side
= LeftWordIfOnBoundary
;
245 m_start
= startOfWord(start
, side
).deepEquivalent();
246 side
= RightWordIfOnBoundary
;
247 if (isEndOfDocument(originalEnd
) || (isEndOfLine(originalEnd
) && !isStartOfLine(originalEnd
) && !isEndOfParagraph(originalEnd
)))
248 side
= LeftWordIfOnBoundary
;
250 VisiblePosition
wordEnd(endOfWord(originalEnd
, side
));
251 VisiblePosition
end(wordEnd
);
253 if (isEndOfParagraph(originalEnd
)) {
254 // Select the paragraph break (the space from the end of a paragraph to the start of
255 // the next one) to match TextEdit.
256 end
= wordEnd
.next();
258 if (Node
* table
= isFirstPositionAfterTable(end
)) {
259 // The paragraph break after the last paragraph in the last cell of a block table ends
260 // at the start of the paragraph after the table.
262 end
= end
.next(true);
272 m_end
= end
.deepEquivalent();
275 case SentenceGranularity
: {
276 m_start
= startOfSentence(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
277 m_end
= endOfSentence(VisiblePosition(m_end
, m_affinity
)).deepEquivalent();
280 case LineGranularity
: {
281 m_start
= startOfLine(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
282 VisiblePosition end
= endOfLine(VisiblePosition(m_end
, m_affinity
));
283 // If the end of this line is at the end of a paragraph, include the space
284 // after the end of the line in the selection.
285 if (isEndOfParagraph(end
)) {
286 VisiblePosition next
= end
.next();
287 if (next
.isNotNull())
290 m_end
= end
.deepEquivalent();
294 m_start
= startOfLine(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
295 m_end
= endOfLine(VisiblePosition(m_end
, m_affinity
)).deepEquivalent();
297 case ParagraphGranularity
: {
298 VisiblePosition
pos(m_start
, m_affinity
);
299 if (isStartOfLine(pos
) && isEndOfDocument(pos
))
300 pos
= pos
.previous();
301 m_start
= startOfParagraph(pos
).deepEquivalent();
302 VisiblePosition visibleParagraphEnd
= endOfParagraph(VisiblePosition(m_end
, m_affinity
));
304 // Include the "paragraph break" (the space from the end of this paragraph to the start
305 // of the next one) in the selection.
306 VisiblePosition
end(visibleParagraphEnd
.next());
308 if (Node
* table
= isFirstPositionAfterTable(end
)) {
309 // The paragraph break after the last paragraph in the last cell of a block table ends
310 // at the start of the paragraph after the table, not at the position just after the table.
312 end
= end
.next(true);
313 // There is no parargraph break after the last paragraph in the last cell of an inline table.
315 end
= visibleParagraphEnd
;
319 end
= visibleParagraphEnd
;
321 m_end
= end
.deepEquivalent();
324 case DocumentBoundary
:
325 m_start
= startOfDocument(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
326 m_end
= endOfDocument(VisiblePosition(m_end
, m_affinity
)).deepEquivalent();
328 case ParagraphBoundary
:
329 m_start
= startOfParagraph(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
330 m_end
= endOfParagraph(VisiblePosition(m_end
, m_affinity
)).deepEquivalent();
332 case SentenceBoundary
:
333 m_start
= startOfSentence(VisiblePosition(m_start
, m_affinity
)).deepEquivalent();
334 m_end
= endOfSentence(VisiblePosition(m_end
, m_affinity
)).deepEquivalent();
338 // Make sure we do not have a dangling start or end.
339 if (m_start
.isNull())
344 adjustForEditableContent();
347 if (m_start
.isNull()) {
348 ASSERT(m_end
.isNull());
351 // enforce downstream affinity if not caret, as affinity only
352 // makes sense for caret
353 m_affinity
= DOWNSTREAM
;
354 } else if (m_start
== m_end
|| m_start
.upstream() == m_end
.upstream()) {
359 // enforce downstream affinity if not caret, as affinity only
360 // makes sense for caret
361 m_affinity
= DOWNSTREAM
;
363 // "Constrain" the selection to be the smallest equivalent range of nodes.
364 // This is a somewhat arbitrary choice, but experience shows that it is
365 // useful to make to make the selection "canonical" (if only for
366 // purposes of comparing selections). This is an ideal point of the code
367 // to do this operation, since all selection changes that result in a RANGE
368 // come through here before anyone uses it.
369 // FIXME: Canonicalizing is good, but haven't we already done it (when we
370 // set these two positions to VisiblePosition deepEquivalent()s above)?
371 m_start
= m_start
.downstream();
372 m_end
= m_end
.upstream();
376 // FIXME: This function breaks the invariant of this class.
377 // But because we use Selection to store values in editing commands for use when
378 // undoing the command, we need to be able to create a selection that while currently
379 // invalid, will be valid once the changes are undone. This is a design problem.
380 // To fix it we either need to change the invariants of Selection or create a new
381 // class for editing to use that can manipulate selections that are not currently valid.
382 void Selection::setWithoutValidation(const Position
& base
, const Position
& extent
)
384 ASSERT(!base
.isNull());
385 ASSERT(!extent
.isNull());
386 ASSERT(base
!= extent
);
387 ASSERT(m_affinity
== DOWNSTREAM
);
388 ASSERT(m_granularity
== CharacterGranularity
);
391 m_baseIsFirst
= comparePositions(base
, extent
) <= 0;
402 void Selection::adjustForEditableContent()
404 if (m_base
.isNull() || m_start
.isNull() || m_end
.isNull())
407 Node
* baseRoot
= highestEditableRoot(m_base
);
408 Node
* startRoot
= highestEditableRoot(m_start
);
409 Node
* endRoot
= highestEditableRoot(m_end
);
411 Node
* baseEditableAncestor
= lowestEditableAncestor(m_base
.node());
413 // The base, start and end are all in the same region. No adjustment necessary.
414 if (baseRoot
== startRoot
&& baseRoot
== endRoot
)
417 // The selection is based in editable content.
419 // If the start is outside the base's editable root, cap it at the start of that root.
420 // If the start is in non-editable content that is inside the base's editable root, put it
421 // at the first editable position after start inside the base's editable root.
422 if (startRoot
!= baseRoot
) {
423 VisiblePosition first
= firstEditablePositionAfterPositionInRoot(m_start
, baseRoot
);
424 m_start
= first
.deepEquivalent();
425 if (m_start
.isNull()) {
426 ASSERT_NOT_REACHED();
430 // If the end is outside the base's editable root, cap it at the end of that root.
431 // If the end is in non-editable content that is inside the base's root, put it
432 // at the last editable position before the end inside the base's root.
433 if (endRoot
!= baseRoot
) {
434 VisiblePosition last
= lastEditablePositionBeforePositionInRoot(m_end
, baseRoot
);
435 m_end
= last
.deepEquivalent();
436 if (m_end
.isNull()) {
437 ASSERT_NOT_REACHED();
441 // The selection is based in non-editable content.
443 // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable
444 // pieces in non-editable content are atomic.
446 // The selection ends in editable content or non-editable content inside a different editable ancestor,
447 // move backward until non-editable content inside the same lowest editable ancestor is reached.
448 Node
* endEditableAncestor
= lowestEditableAncestor(m_end
.node());
449 if (endRoot
|| endEditableAncestor
!= baseEditableAncestor
) {
451 Position p
= previousVisuallyDistinctCandidate(m_end
);
452 Node
* shadowAncestor
= endRoot
? endRoot
->shadowAncestorNode() : 0;
453 if (p
.isNull() && endRoot
&& (shadowAncestor
!= endRoot
))
454 p
= Position(shadowAncestor
, maxDeepOffset(shadowAncestor
));
455 while (p
.isNotNull() && !(lowestEditableAncestor(p
.node()) == baseEditableAncestor
&& !isEditablePosition(p
))) {
456 Node
* root
= editableRootForPosition(p
);
457 shadowAncestor
= root
? root
->shadowAncestorNode() : 0;
458 p
= isAtomicNode(p
.node()) ? positionBeforeNode(p
.node()) : previousVisuallyDistinctCandidate(p
);
459 if (p
.isNull() && (shadowAncestor
!= root
))
460 p
= Position(shadowAncestor
, maxDeepOffset(shadowAncestor
));
462 VisiblePosition
previous(p
);
464 if (previous
.isNull()) {
465 ASSERT_NOT_REACHED();
467 m_extent
= Position();
471 m_end
= previous
.deepEquivalent();
474 // The selection starts in editable content or non-editable content inside a different editable ancestor,
475 // move forward until non-editable content inside the same lowest editable ancestor is reached.
476 Node
* startEditableAncestor
= lowestEditableAncestor(m_start
.node());
477 if (startRoot
|| startEditableAncestor
!= baseEditableAncestor
) {
478 Position p
= nextVisuallyDistinctCandidate(m_start
);
479 Node
* shadowAncestor
= startRoot
? startRoot
->shadowAncestorNode() : 0;
480 if (p
.isNull() && startRoot
&& (shadowAncestor
!= startRoot
))
481 p
= Position(shadowAncestor
, 0);
482 while (p
.isNotNull() && !(lowestEditableAncestor(p
.node()) == baseEditableAncestor
&& !isEditablePosition(p
))) {
483 Node
* root
= editableRootForPosition(p
);
484 shadowAncestor
= root
? root
->shadowAncestorNode() : 0;
485 p
= isAtomicNode(p
.node()) ? positionAfterNode(p
.node()) : nextVisuallyDistinctCandidate(p
);
486 if (p
.isNull() && (shadowAncestor
!= root
))
487 p
= Position(shadowAncestor
, 0);
489 VisiblePosition
next(p
);
492 ASSERT_NOT_REACHED();
494 m_extent
= Position();
498 m_start
= next
.deepEquivalent();
502 // Correct the extent if necessary.
503 if (baseEditableAncestor
!= lowestEditableAncestor(m_extent
.node()))
504 m_extent
= m_baseIsFirst
? m_end
: m_start
;
507 bool Selection::isContentEditable() const
509 return isEditablePosition(start());
512 bool Selection::isContentRichlyEditable() const
514 return isRichlyEditablePosition(start());
517 Element
* Selection::rootEditableElement() const
519 return editableRootForPosition(start());
522 Node
* Selection::shadowTreeRootNode() const
524 return start().node() ? start().node()->shadowTreeRootNode() : 0;
527 void Selection::debugPosition() const
532 fprintf(stderr
, "Selection =================\n");
534 if (m_start
== m_end
) {
535 Position pos
= m_start
;
536 fprintf(stderr
, "pos: %s %p:%d\n", pos
.node()->nodeName().utf8().data(), pos
.node(), pos
.offset());
538 Position pos
= m_start
;
539 fprintf(stderr
, "start: %s %p:%d\n", pos
.node()->nodeName().utf8().data(), pos
.node(), pos
.offset());
540 fprintf(stderr
, "-----------------------------------\n");
542 fprintf(stderr
, "end: %s %p:%d\n", pos
.node()->nodeName().utf8().data(), pos
.node(), pos
.offset());
543 fprintf(stderr
, "-----------------------------------\n");
546 fprintf(stderr
, "================================\n");
551 void Selection::formatForDebugger(char* buffer
, unsigned length
) const
559 const int FormatBufferSize
= 1024;
560 char s
[FormatBufferSize
];
562 start().formatForDebugger(s
, FormatBufferSize
);
565 end().formatForDebugger(s
, FormatBufferSize
);
569 strncpy(buffer
, result
.utf8().data(), length
- 1);
572 void Selection::showTreeForThis() const
574 if (start().node()) {
575 start().node()->showTreeAndMark(start().node(), "S", end().node(), "E");
576 fprintf(stderr
, "start offset: %d, end offset: %d\n", start().offset(), end().offset());
582 } // namespace WebCore
586 void showTree(const WebCore::Selection
& sel
)
588 sel
.showTreeForThis();
591 void showTree(const WebCore::Selection
* sel
)
594 sel
->showTreeForThis();