2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / editing / Selection.cpp
blob9b1756796de2c6591c1c3ae3dac69984a51d7cd0
1 /*
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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
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.
26 #include "config.h"
27 #include "Selection.h"
29 #include "CString.h"
30 #include "Document.h"
31 #include "Element.h"
32 #include "htmlediting.h"
33 #include "VisiblePosition.h"
34 #include "visible_units.h"
35 #include "Range.h"
36 #include <wtf/Assertions.h>
37 #include <stdio.h>
39 namespace WebCore {
41 Selection::Selection()
42 : m_affinity(DOWNSTREAM)
43 , m_granularity(CharacterGranularity)
44 , m_state(NONE)
45 , m_baseIsFirst(true)
49 Selection::Selection(const Position& pos, EAffinity affinity)
50 : m_base(pos)
51 , m_extent(pos)
52 , m_affinity(affinity)
53 , m_granularity(CharacterGranularity)
55 validate();
58 Selection::Selection(const Position& base, const Position& extent, EAffinity affinity)
59 : m_base(base)
60 , m_extent(extent)
61 , m_affinity(affinity)
62 , m_granularity(CharacterGranularity)
64 validate();
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)
73 validate();
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)
82 validate();
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)
91 validate();
94 Selection Selection::selectionFromContentsOfNode(Node* node)
96 return Selection(Position(node, 0), Position(node, maxDeepOffset(node)), DOWNSTREAM);
99 void Selection::setBase(const Position& position)
101 m_base = position;
102 validate();
105 void Selection::setBase(const VisiblePosition& visiblePosition)
107 m_base = visiblePosition.deepEquivalent();
108 validate();
111 void Selection::setExtent(const Position& position)
113 m_extent = position;
114 validate();
117 void Selection::setExtent(const VisiblePosition& visiblePosition)
119 m_extent = visiblePosition.deepEquivalent();
120 validate();
123 PassRefPtr<Range> Selection::toRange() const
125 if (isNone())
126 return 0;
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.
135 if (isNone())
136 return 0;
138 Position s, e;
139 if (isCaret()) {
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());
144 e = s;
145 } else {
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
152 // different style.
154 // On a treasure map, <b>X</b> marks the spot.
155 // ^ selected
157 ASSERT(isRange());
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.
163 Position tmp = s;
164 s = e;
165 e = tmp;
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);
174 if (ec) {
175 LOG_ERROR("Exception setting Range start from Selection: %d", ec);
176 return 0;
178 result->setEnd(e.node(), e.offset(), ec);
179 if (ec) {
180 LOG_ERROR("Exception setting Range end from Selection: %d", ec);
181 return 0;
183 return result.release();
186 bool Selection::expandUsingGranularity(TextGranularity granularity)
188 if (isNone())
189 return false;
191 m_granularity = granularity;
192 validate();
193 return true;
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)
203 m_extent = m_base;
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()) {
212 m_base = m_extent;
213 m_baseIsFirst = true;
214 } else if (m_extent.isNull()) {
215 m_extent = m_base;
216 m_baseIsFirst = true;
217 } else {
218 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0;
221 if (m_baseIsFirst) {
222 m_start = m_base;
223 m_end = m_extent;
224 } else {
225 m_start = m_extent;
226 m_end = m_base;
229 // Expand the selection if requested.
230 switch (m_granularity) {
231 case CharacterGranularity:
232 // Don't do any expansion.
233 break;
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.
261 if (isBlock(table))
262 end = end.next(true);
263 else
264 end = wordEnd;
267 if (end.isNull())
268 end = wordEnd;
272 m_end = end.deepEquivalent();
273 break;
275 case SentenceGranularity: {
276 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
277 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
278 break;
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())
288 end = next;
290 m_end = end.deepEquivalent();
291 break;
293 case LineBoundary:
294 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
295 m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent();
296 break;
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.
311 if (isBlock(table))
312 end = end.next(true);
313 // There is no parargraph break after the last paragraph in the last cell of an inline table.
314 else
315 end = visibleParagraphEnd;
318 if (end.isNull())
319 end = visibleParagraphEnd;
321 m_end = end.deepEquivalent();
322 break;
324 case DocumentBoundary:
325 m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent();
326 m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent();
327 break;
328 case ParagraphBoundary:
329 m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent();
330 m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent();
331 break;
332 case SentenceBoundary:
333 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
334 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
335 break;
338 // Make sure we do not have a dangling start or end.
339 if (m_start.isNull())
340 m_start = m_end;
341 if (m_end.isNull())
342 m_end = m_start;
344 adjustForEditableContent();
346 // adjust the state
347 if (m_start.isNull()) {
348 ASSERT(m_end.isNull());
349 m_state = NONE;
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()) {
355 m_state = CARET;
356 } else {
357 m_state = RANGE;
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);
389 m_base = base;
390 m_extent = extent;
391 m_baseIsFirst = comparePositions(base, extent) <= 0;
392 if (m_baseIsFirst) {
393 m_start = base;
394 m_end = extent;
395 } else {
396 m_start = extent;
397 m_end = base;
399 m_state = RANGE;
402 void Selection::adjustForEditableContent()
404 if (m_base.isNull() || m_start.isNull() || m_end.isNull())
405 return;
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)
415 return;
417 // The selection is based in editable content.
418 if (baseRoot) {
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();
427 m_start = m_end;
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();
438 m_end = m_start;
441 // The selection is based in non-editable content.
442 } else {
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();
466 m_base = Position();
467 m_extent = Position();
468 validate();
469 return;
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);
491 if (next.isNull()) {
492 ASSERT_NOT_REACHED();
493 m_base = Position();
494 m_extent = Position();
495 validate();
496 return;
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
529 if (!m_start.node())
530 return;
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());
537 } else {
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");
541 pos = m_end;
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");
549 #ifndef NDEBUG
551 void Selection::formatForDebugger(char* buffer, unsigned length) const
553 String result;
554 String s;
556 if (isNone()) {
557 result = "<none>";
558 } else {
559 const int FormatBufferSize = 1024;
560 char s[FormatBufferSize];
561 result += "from ";
562 start().formatForDebugger(s, FormatBufferSize);
563 result += s;
564 result += " to ";
565 end().formatForDebugger(s, FormatBufferSize);
566 result += s;
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());
580 #endif
582 } // namespace WebCore
584 #ifndef NDEBUG
586 void showTree(const WebCore::Selection& sel)
588 sel.showTreeForThis();
591 void showTree(const WebCore::Selection* sel)
593 if (sel)
594 sel->showTreeForThis();
597 #endif