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