1 /*
2 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved.
3 *
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.
12 *
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.
24 */
25
26 #include "config.h"
27 #include "VisibleSelection.h"
28
29 #include "Document.h"
30 #include "Element.h"
31 #include "htmlediting.h"
32 #include "TextIterator.h"
33 #include "VisiblePosition.h"
34 #include "visible_units.h"
35 #include "Range.h"
36 #include <stdio.h>
37 #include <wtf/Assertions.h>
38 #include <wtf/text/CString.h>
39 #include <wtf/unicode/CharacterNames.h>
40
41 namespace WebCore {
42
VisibleSelection()43 VisibleSelection::VisibleSelection()
44 : m_affinity(DOWNSTREAM)
45 , m_selectionType(NoSelection)
46 , m_baseIsFirst(true)
47 {
48 }
49
VisibleSelection(const Position & pos,EAffinity affinity)50 VisibleSelection::VisibleSelection(const Position& pos, EAffinity affinity)
51 : m_base(pos)
52 , m_extent(pos)
53 , m_affinity(affinity)
54 {
55 validate();
56 }
57
VisibleSelection(const Position & base,const Position & extent,EAffinity affinity)58 VisibleSelection::VisibleSelection(const Position& base, const Position& extent, EAffinity affinity)
59 : m_base(base)
60 , m_extent(extent)
61 , m_affinity(affinity)
62 {
63 validate();
64 }
65
VisibleSelection(const VisiblePosition & pos)66 VisibleSelection::VisibleSelection(const VisiblePosition& pos)
67 : m_base(pos.deepEquivalent())
68 , m_extent(pos.deepEquivalent())
69 , m_affinity(pos.affinity())
70 {
71 validate();
72 }
73
VisibleSelection(const VisiblePosition & base,const VisiblePosition & extent)74 VisibleSelection::VisibleSelection(const VisiblePosition& base, const VisiblePosition& extent)
75 : m_base(base.deepEquivalent())
76 , m_extent(extent.deepEquivalent())
77 , m_affinity(base.affinity())
78 {
79 validate();
80 }
81
VisibleSelection(const Range * range,EAffinity affinity)82 VisibleSelection::VisibleSelection(const Range* range, EAffinity affinity)
83 : m_base(range->startPosition())
84 , m_extent(range->endPosition())
85 , m_affinity(affinity)
86 {
87 validate();
88 }
89
selectionFromContentsOfNode(Node * node)90 VisibleSelection VisibleSelection::selectionFromContentsOfNode(Node* node)
91 {
92 ASSERT(!editingIgnoresContent(node));
93 return VisibleSelection(firstPositionInNode(node), lastPositionInNode(node), DOWNSTREAM);
94 }
95
setBase(const Position & position)96 void VisibleSelection::setBase(const Position& position)
97 {
98 m_base = position;
99 validate();
100 }
101
setBase(const VisiblePosition & visiblePosition)102 void VisibleSelection::setBase(const VisiblePosition& visiblePosition)
103 {
104 m_base = visiblePosition.deepEquivalent();
105 validate();
106 }
107
setExtent(const Position & position)108 void VisibleSelection::setExtent(const Position& position)
109 {
110 m_extent = position;
111 validate();
112 }
113
setExtent(const VisiblePosition & visiblePosition)114 void VisibleSelection::setExtent(const VisiblePosition& visiblePosition)
115 {
116 m_extent = visiblePosition.deepEquivalent();
117 validate();
118 }
119
firstRange() const120 PassRefPtr<Range> VisibleSelection::firstRange() const
121 {
122 if (isNone())
123 return 0;
124 Position start = m_start.parentAnchoredEquivalent();
125 Position end = m_end.parentAnchoredEquivalent();
126 return Range::create(start.anchorNode()->document(), start, end);
127 }
128
toNormalizedRange() const129 PassRefPtr<Range> VisibleSelection::toNormalizedRange() const
130 {
131 if (isNone())
132 return 0;
133
134 // Make sure we have an updated layout since this function is called
135 // in the course of running edit commands which modify the DOM.
136 // Failing to call this can result in equivalentXXXPosition calls returning
137 // incorrect results.
138 m_start.anchorNode()->document()->updateLayout();
139
140 // Check again, because updating layout can clear the selection.
141 if (isNone())
142 return 0;
143
144 Position s, e;
145 if (isCaret()) {
146 // If the selection is a caret, move the range start upstream. This helps us match
147 // the conventions of text editors tested, which make style determinations based
148 // on the character before the caret, if any.
149 s = m_start.upstream().parentAnchoredEquivalent();
150 e = s;
151 } else {
152 // If the selection is a range, select the minimum range that encompasses the selection.
153 // Again, this is to match the conventions of text editors tested, which make style
154 // determinations based on the first character of the selection.
155 // For instance, this operation helps to make sure that the "X" selected below is the
156 // only thing selected. The range should not be allowed to "leak" out to the end of the
157 // previous text node, or to the beginning of the next text node, each of which has a
158 // different style.
159 //
160 // On a treasure map, <b>X</b> marks the spot.
161 // ^ selected
162 //
163 ASSERT(isRange());
164 s = m_start.downstream();
165 e = m_end.upstream();
166 if (comparePositions(s, e) > 0) {
167 // Make sure the start is before the end.
168 // The end can wind up before the start if collapsed whitespace is the only thing selected.
169 Position tmp = s;
170 s = e;
171 e = tmp;
172 }
173 s = s.parentAnchoredEquivalent();
174 e = e.parentAnchoredEquivalent();
175 }
176
177 if (!s.containerNode() || !e.containerNode())
178 return 0;
179
180 // VisibleSelections are supposed to always be valid. This constructor will ASSERT
181 // if a valid range could not be created, which is fine for this callsite.
182 return Range::create(s.anchorNode()->document(), s, e);
183 }
184
expandUsingGranularity(TextGranularity granularity)185 bool VisibleSelection::expandUsingGranularity(TextGranularity granularity)
186 {
187 if (isNone())
188 return false;
189
190 validate(granularity);
191 return true;
192 }
193
makeSearchRange(const Position & pos)194 static PassRefPtr<Range> makeSearchRange(const Position& pos)
195 {
196 Node* n = pos.deprecatedNode();
197 if (!n)
198 return 0;
199 Document* d = n->document();
200 Node* de = d->documentElement();
201 if (!de)
202 return 0;
203 Node* boundary = n->enclosingBlockFlowElement();
204 if (!boundary)
205 return 0;
206
207 RefPtr<Range> searchRange(Range::create(d));
208 ExceptionCode ec = 0;
209
210 Position start(pos.parentAnchoredEquivalent());
211 searchRange->selectNodeContents(boundary, ec);
212 searchRange->setStart(start.containerNode(), start.offsetInContainerNode(), ec);
213
214 ASSERT(!ec);
215 if (ec)
216 return 0;
217
218 return searchRange.release();
219 }
220
isAll(EditingBoundaryCrossingRule rule) const221 bool VisibleSelection::isAll(EditingBoundaryCrossingRule rule) const
222 {
223 return !shadowTreeRootNode() && visibleStart().previous(rule).isNull() && visibleEnd().next(rule).isNull();
224 }
225
appendTrailingWhitespace()226 void VisibleSelection::appendTrailingWhitespace()
227 {
228 RefPtr<Range> searchRange = makeSearchRange(m_end);
229 if (!searchRange)
230 return;
231
232 CharacterIterator charIt(searchRange.get(), TextIteratorEmitsCharactersBetweenAllVisiblePositions);
233
234 for (; charIt.length(); charIt.advance(1)) {
235 UChar c = charIt.characters()[0];
236 if ((!isSpaceOrNewline(c) && c != noBreakSpace) || c == '\n')
237 break;
238 m_end = charIt.range()->endPosition();
239 }
240 }
241
setBaseAndExtentToDeepEquivalents()242 void VisibleSelection::setBaseAndExtentToDeepEquivalents()
243 {
244 // Move the selection to rendered positions, if possible.
245 bool baseAndExtentEqual = m_base == m_extent;
246 if (m_base.isNotNull()) {
247 m_base = VisiblePosition(m_base, m_affinity).deepEquivalent();
248 if (baseAndExtentEqual)
249 m_extent = m_base;
250 }
251 if (m_extent.isNotNull() && !baseAndExtentEqual)
252 m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent();
253
254 // Make sure we do not have a dangling base or extent.
255 if (m_base.isNull() && m_extent.isNull())
256 m_baseIsFirst = true;
257 else if (m_base.isNull()) {
258 m_base = m_extent;
259 m_baseIsFirst = true;
260 } else if (m_extent.isNull()) {
261 m_extent = m_base;
262 m_baseIsFirst = true;
263 } else
264 m_baseIsFirst = comparePositions(m_base, m_extent) <= 0;
265 }
266
setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity)267 void VisibleSelection::setStartAndEndFromBaseAndExtentRespectingGranularity(TextGranularity granularity)
268 {
269 if (m_baseIsFirst) {
270 m_start = m_base;
271 m_end = m_extent;
272 } else {
273 m_start = m_extent;
274 m_end = m_base;
275 }
276
277 switch (granularity) {
278 case CharacterGranularity:
279 // Don't do any expansion.
280 break;
281 case WordGranularity: {
282 // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary).
283 // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in
284 // the document, select that last word (LeftWordIfOnBoundary).
285 // Edge case: If the caret is after the last word in a paragraph, select from the the end of the
286 // last word to the line break (also RightWordIfOnBoundary);
287 VisiblePosition start = VisiblePosition(m_start, m_affinity);
288 VisiblePosition originalEnd(m_end, m_affinity);
289 EWordSide side = RightWordIfOnBoundary;
290 if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start)))
291 side = LeftWordIfOnBoundary;
292 m_start = startOfWord(start, side).deepEquivalent();
293 side = RightWordIfOnBoundary;
294 if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd)))
295 side = LeftWordIfOnBoundary;
296
297 VisiblePosition wordEnd(endOfWord(originalEnd, side));
298 VisiblePosition end(wordEnd);
299
300 if (isEndOfParagraph(originalEnd) && !isEmptyTableCell(m_start.deprecatedNode())) {
301 // Select the paragraph break (the space from the end of a paragraph to the start of
302 // the next one) to match TextEdit.
303 end = wordEnd.next();
304
305 if (Node* table = isFirstPositionAfterTable(end)) {
306 // The paragraph break after the last paragraph in the last cell of a block table ends
307 // at the start of the paragraph after the table.
308 if (isBlock(table))
309 end = end.next(CannotCrossEditingBoundary);
310 else
311 end = wordEnd;
312 }
313
314 if (end.isNull())
315 end = wordEnd;
316
317 }
318
319 m_end = end.deepEquivalent();
320 break;
321 }
322 case SentenceGranularity: {
323 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
324 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
325 break;
326 }
327 case LineGranularity: {
328 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
329 VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity));
330 // If the end of this line is at the end of a paragraph, include the space
331 // after the end of the line in the selection.
332 if (isEndOfParagraph(end)) {
333 VisiblePosition next = end.next();
334 if (next.isNotNull())
335 end = next;
336 }
337 m_end = end.deepEquivalent();
338 break;
339 }
340 case LineBoundary:
341 m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent();
342 m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent();
343 break;
344 case ParagraphGranularity: {
345 VisiblePosition pos(m_start, m_affinity);
346 if (isStartOfLine(pos) && isEndOfDocument(pos))
347 pos = pos.previous();
348 m_start = startOfParagraph(pos).deepEquivalent();
349 VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity));
350
351 // Include the "paragraph break" (the space from the end of this paragraph to the start
352 // of the next one) in the selection.
353 VisiblePosition end(visibleParagraphEnd.next());
354
355 if (Node* table = isFirstPositionAfterTable(end)) {
356 // The paragraph break after the last paragraph in the last cell of a block table ends
357 // at the start of the paragraph after the table, not at the position just after the table.
358 if (isBlock(table))
359 end = end.next(CannotCrossEditingBoundary);
360 // There is no parargraph break after the last paragraph in the last cell of an inline table.
361 else
362 end = visibleParagraphEnd;
363 }
364
365 if (end.isNull())
366 end = visibleParagraphEnd;
367
368 m_end = end.deepEquivalent();
369 break;
370 }
371 case DocumentBoundary:
372 m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent();
373 m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent();
374 break;
375 case ParagraphBoundary:
376 m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent();
377 m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent();
378 break;
379 case SentenceBoundary:
380 m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent();
381 m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent();
382 break;
383 case WebKitVisualWordGranularity:
384 break;
385 }
386
387 // Make sure we do not have a dangling start or end.
388 if (m_start.isNull())
389 m_start = m_end;
390 if (m_end.isNull())
391 m_end = m_start;
392 }
393
updateSelectionType()394 void VisibleSelection::updateSelectionType()
395 {
396 if (m_start.isNull()) {
397 ASSERT(m_end.isNull());
398 m_selectionType = NoSelection;
399 } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) {
400 m_selectionType = CaretSelection;
401 } else
402 m_selectionType = RangeSelection;
403
404 // Affinity only makes sense for a caret
405 if (m_selectionType != CaretSelection)
406 m_affinity = DOWNSTREAM;
407 }
408
validate(TextGranularity granularity)409 void VisibleSelection::validate(TextGranularity granularity)
410 {
411 setBaseAndExtentToDeepEquivalents();
412 setStartAndEndFromBaseAndExtentRespectingGranularity(granularity);
413 adjustSelectionToAvoidCrossingEditingBoundaries();
414 updateSelectionType();
415
416 if (selectionType() == RangeSelection) {
417 // "Constrain" the selection to be the smallest equivalent range of nodes.
418 // This is a somewhat arbitrary choice, but experience shows that it is
419 // useful to make to make the selection "canonical" (if only for
420 // purposes of comparing selections). This is an ideal point of the code
421 // to do this operation, since all selection changes that result in a RANGE
422 // come through here before anyone uses it.
423 // FIXME: Canonicalizing is good, but haven't we already done it (when we
424 // set these two positions to VisiblePosition deepEquivalent()s above)?
425 m_start = m_start.downstream();
426 m_end = m_end.upstream();
427 }
428 }
429
430 // FIXME: This function breaks the invariant of this class.
431 // But because we use VisibleSelection to store values in editing commands for use when
432 // undoing the command, we need to be able to create a selection that while currently
433 // invalid, will be valid once the changes are undone. This is a design problem.
434 // To fix it we either need to change the invariants of VisibleSelection or create a new
435 // class for editing to use that can manipulate selections that are not currently valid.
setWithoutValidation(const Position & base,const Position & extent)436 void VisibleSelection::setWithoutValidation(const Position& base, const Position& extent)
437 {
438 ASSERT(!base.isNull());
439 ASSERT(!extent.isNull());
440 ASSERT(m_affinity == DOWNSTREAM);
441 m_base = base;
442 m_extent = extent;
443 m_baseIsFirst = comparePositions(base, extent) <= 0;
444 if (m_baseIsFirst) {
445 m_start = base;
446 m_end = extent;
447 } else {
448 m_start = extent;
449 m_end = base;
450 }
451 m_selectionType = base == extent ? CaretSelection : RangeSelection;
452 }
453
adjustSelectionToAvoidCrossingEditingBoundaries()454 void VisibleSelection::adjustSelectionToAvoidCrossingEditingBoundaries()
455 {
456 if (m_base.isNull() || m_start.isNull() || m_end.isNull())
457 return;
458
459 Node* baseRoot = highestEditableRoot(m_base);
460 Node* startRoot = highestEditableRoot(m_start);
461 Node* endRoot = highestEditableRoot(m_end);
462
463 Node* baseEditableAncestor = lowestEditableAncestor(m_base.containerNode());
464
465 // The base, start and end are all in the same region. No adjustment necessary.
466 if (baseRoot == startRoot && baseRoot == endRoot)
467 return;
468
469 // The selection is based in editable content.
470 if (baseRoot) {
471 // If the start is outside the base's editable root, cap it at the start of that root.
472 // If the start is in non-editable content that is inside the base's editable root, put it
473 // at the first editable position after start inside the base's editable root.
474 if (startRoot != baseRoot) {
475 VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot);
476 m_start = first.deepEquivalent();
477 if (m_start.isNull()) {
478 ASSERT_NOT_REACHED();
479 m_start = m_end;
480 }
481 }
482 // If the end is outside the base's editable root, cap it at the end of that root.
483 // If the end is in non-editable content that is inside the base's root, put it
484 // at the last editable position before the end inside the base's root.
485 if (endRoot != baseRoot) {
486 VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot);
487 m_end = last.deepEquivalent();
488 if (m_end.isNull())
489 m_end = m_start;
490 }
491 // The selection is based in non-editable content.
492 } else {
493 // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable
494 // pieces in non-editable content are atomic.
495
496 // The selection ends in editable content or non-editable content inside a different editable ancestor,
497 // move backward until non-editable content inside the same lowest editable ancestor is reached.
498 Node* endEditableAncestor = lowestEditableAncestor(m_end.containerNode());
499 if (endRoot || endEditableAncestor != baseEditableAncestor) {
500
501 Position p = previousVisuallyDistinctCandidate(m_end);
502 Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0;
503 if (p.isNull() && endRoot && (shadowAncestor != endRoot))
504 p = positionAfterNode(shadowAncestor);
505 while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
506 Node* root = editableRootForPosition(p);
507 shadowAncestor = root ? root->shadowAncestorNode() : 0;
508 p = isAtomicNode(p.containerNode()) ? positionInParentBeforeNode(p.containerNode()) : previousVisuallyDistinctCandidate(p);
509 if (p.isNull() && (shadowAncestor != root))
510 p = positionAfterNode(shadowAncestor);
511 }
512 VisiblePosition previous(p);
513
514 if (previous.isNull()) {
515 // The selection crosses an Editing boundary. This is a
516 // programmer error in the editing code. Happy debugging!
517 ASSERT_NOT_REACHED();
518 m_base = Position();
519 m_extent = Position();
520 validate();
521 return;
522 }
523 m_end = previous.deepEquivalent();
524 }
525
526 // The selection starts in editable content or non-editable content inside a different editable ancestor,
527 // move forward until non-editable content inside the same lowest editable ancestor is reached.
528 Node* startEditableAncestor = lowestEditableAncestor(m_start.containerNode());
529 if (startRoot || startEditableAncestor != baseEditableAncestor) {
530 Position p = nextVisuallyDistinctCandidate(m_start);
531 Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0;
532 if (p.isNull() && startRoot && (shadowAncestor != startRoot))
533 p = positionBeforeNode(shadowAncestor);
534 while (p.isNotNull() && !(lowestEditableAncestor(p.containerNode()) == baseEditableAncestor && !isEditablePosition(p))) {
535 Node* root = editableRootForPosition(p);
536 shadowAncestor = root ? root->shadowAncestorNode() : 0;
537 p = isAtomicNode(p.containerNode()) ? positionInParentAfterNode(p.containerNode()) : nextVisuallyDistinctCandidate(p);
538 if (p.isNull() && (shadowAncestor != root))
539 p = positionBeforeNode(shadowAncestor);
540 }
541 VisiblePosition next(p);
542
543 if (next.isNull()) {
544 // The selection crosses an Editing boundary. This is a
545 // programmer error in the editing code. Happy debugging!
546 ASSERT_NOT_REACHED();
547 m_base = Position();
548 m_extent = Position();
549 validate();
550 return;
551 }
552 m_start = next.deepEquivalent();
553 }
554 }
555
556 // Correct the extent if necessary.
557 if (baseEditableAncestor != lowestEditableAncestor(m_extent.containerNode()))
558 m_extent = m_baseIsFirst ? m_end : m_start;
559 }
560
isContentEditable() const561 bool VisibleSelection::isContentEditable() const
562 {
563 return isEditablePosition(start());
564 }
565
isContentRichlyEditable() const566 bool VisibleSelection::isContentRichlyEditable() const
567 {
568 return isRichlyEditablePosition(start());
569 }
570
rootEditableElement() const571 Element* VisibleSelection::rootEditableElement() const
572 {
573 return editableRootForPosition(start());
574 }
575
shadowTreeRootNode() const576 Node* VisibleSelection::shadowTreeRootNode() const
577 {
578 return start().deprecatedNode() ? start().deprecatedNode()->shadowTreeRootNode() : 0;
579 }
580
581 #ifndef NDEBUG
582
debugPosition() const583 void VisibleSelection::debugPosition() const
584 {
585 fprintf(stderr, "VisibleSelection ===============\n");
586
587 if (!m_start.anchorNode())
588 fputs("pos: null", stderr);
589 else if (m_start == m_end) {
590 fprintf(stderr, "pos: %s ", m_start.anchorNode()->nodeName().utf8().data());
591 m_start.showAnchorTypeAndOffset();
592 } else {
593 fprintf(stderr, "start: %s ", m_start.anchorNode()->nodeName().utf8().data());
594 m_start.showAnchorTypeAndOffset();
595 fprintf(stderr, "end: %s ", m_end.anchorNode()->nodeName().utf8().data());
596 m_end.showAnchorTypeAndOffset();
597 }
598
599 fprintf(stderr, "================================\n");
600 }
601
formatForDebugger(char * buffer,unsigned length) const602 void VisibleSelection::formatForDebugger(char* buffer, unsigned length) const
603 {
604 String result;
605 String s;
606
607 if (isNone()) {
608 result = "<none>";
609 } else {
610 const int FormatBufferSize = 1024;
611 char s[FormatBufferSize];
612 result += "from ";
613 start().formatForDebugger(s, FormatBufferSize);
614 result += s;
615 result += " to ";
616 end().formatForDebugger(s, FormatBufferSize);
617 result += s;
618 }
619
620 strncpy(buffer, result.utf8().data(), length - 1);
621 }
622
showTreeForThis() const623 void VisibleSelection::showTreeForThis() const
624 {
625 if (start().anchorNode()) {
626 start().anchorNode()->showTreeAndMark(start().anchorNode(), "S", end().anchorNode(), "E");
627 fputs("start: ", stderr);
628 start().showAnchorTypeAndOffset();
629 fputs("end: ", stderr);
630 end().showAnchorTypeAndOffset();
631 }
632 }
633
634 #endif
635
636 } // namespace WebCore
637
638 #ifndef NDEBUG
639
showTree(const WebCore::VisibleSelection & sel)640 void showTree(const WebCore::VisibleSelection& sel)
641 {
642 sel.showTreeForThis();
643 }
644
showTree(const WebCore::VisibleSelection * sel)645 void showTree(const WebCore::VisibleSelection* sel)
646 {
647 if (sel)
648 sel->showTreeForThis();
649 }
650
651 #endif
652