Home | History | Annotate | Download | only in page
      1 /*
      2  * Copyright (C) 2007, 2009 Apple 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  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 
     30 #include "config.h"
     31 #include "DOMSelection.h"
     32 
     33 #include "ExceptionCode.h"
     34 #include "Frame.h"
     35 #include "Node.h"
     36 #include "PlatformString.h"
     37 #include "Range.h"
     38 #include "SelectionController.h"
     39 #include "TextIterator.h"
     40 #include "htmlediting.h"
     41 
     42 namespace WebCore {
     43 
     44 static Node* selectionShadowAncestor(Frame* frame)
     45 {
     46     Node* node = frame->selection()->selection().base().anchorNode();
     47     if (!node)
     48         return 0;
     49     Node* shadowAncestor = node->shadowAncestorNode();
     50     if (shadowAncestor == node)
     51         return 0;
     52     return shadowAncestor;
     53 }
     54 
     55 DOMSelection::DOMSelection(Frame* frame)
     56     : m_frame(frame)
     57 {
     58 }
     59 
     60 Frame* DOMSelection::frame() const
     61 {
     62     return m_frame;
     63 }
     64 
     65 void DOMSelection::disconnectFrame()
     66 {
     67     m_frame = 0;
     68 }
     69 
     70 const VisibleSelection& DOMSelection::visibleSelection() const
     71 {
     72     ASSERT(m_frame);
     73     return m_frame->selection()->selection();
     74 }
     75 
     76 static Position anchorPosition(const VisibleSelection& selection)
     77 {
     78     Position anchor = selection.isBaseFirst() ? selection.start() : selection.end();
     79     return rangeCompliantEquivalent(anchor);
     80 }
     81 
     82 static Position focusPosition(const VisibleSelection& selection)
     83 {
     84     Position focus = selection.isBaseFirst() ? selection.end() : selection.start();
     85     return rangeCompliantEquivalent(focus);
     86 }
     87 
     88 static Position basePosition(const VisibleSelection& selection)
     89 {
     90     return rangeCompliantEquivalent(selection.base());
     91 }
     92 
     93 static Position extentPosition(const VisibleSelection& selection)
     94 {
     95     return rangeCompliantEquivalent(selection.extent());
     96 }
     97 
     98 Node* DOMSelection::anchorNode() const
     99 {
    100     if (!m_frame)
    101         return 0;
    102     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    103         return shadowAncestor->parentNode();
    104     return anchorPosition(visibleSelection()).node();
    105 }
    106 
    107 int DOMSelection::anchorOffset() const
    108 {
    109     if (!m_frame)
    110         return 0;
    111     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    112         return shadowAncestor->nodeIndex();
    113     return anchorPosition(visibleSelection()).deprecatedEditingOffset();
    114 }
    115 
    116 Node* DOMSelection::focusNode() const
    117 {
    118     if (!m_frame)
    119         return 0;
    120     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    121         return shadowAncestor->parentNode();
    122     return focusPosition(visibleSelection()).node();
    123 }
    124 
    125 int DOMSelection::focusOffset() const
    126 {
    127     if (!m_frame)
    128         return 0;
    129     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    130         return shadowAncestor->nodeIndex();
    131     return focusPosition(visibleSelection()).deprecatedEditingOffset();
    132 }
    133 
    134 Node* DOMSelection::baseNode() const
    135 {
    136     if (!m_frame)
    137         return 0;
    138     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    139         return shadowAncestor->parentNode();
    140     return basePosition(visibleSelection()).node();
    141 }
    142 
    143 int DOMSelection::baseOffset() const
    144 {
    145     if (!m_frame)
    146         return 0;
    147     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    148         return shadowAncestor->nodeIndex();
    149     return basePosition(visibleSelection()).deprecatedEditingOffset();
    150 }
    151 
    152 Node* DOMSelection::extentNode() const
    153 {
    154     if (!m_frame)
    155         return 0;
    156     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    157         return shadowAncestor->parentNode();
    158     return extentPosition(visibleSelection()).node();
    159 }
    160 
    161 int DOMSelection::extentOffset() const
    162 {
    163     if (!m_frame)
    164         return 0;
    165     if (Node* shadowAncestor = selectionShadowAncestor(m_frame))
    166         return shadowAncestor->nodeIndex();
    167     return extentPosition(visibleSelection()).deprecatedEditingOffset();
    168 }
    169 
    170 bool DOMSelection::isCollapsed() const
    171 {
    172     if (!m_frame || selectionShadowAncestor(m_frame))
    173         return true;
    174     return !m_frame->selection()->isRange();
    175 }
    176 
    177 String DOMSelection::type() const
    178 {
    179     if (!m_frame)
    180         return String();
    181 
    182     SelectionController* selection = m_frame->selection();
    183 
    184     // This is a WebKit DOM extension, incompatible with an IE extension
    185     // IE has this same attribute, but returns "none", "text" and "control"
    186     // http://msdn.microsoft.com/en-us/library/ms534692(VS.85).aspx
    187     if (selection->isNone())
    188         return "None";
    189     if (selection->isCaret())
    190         return "Caret";
    191     return "Range";
    192 }
    193 
    194 int DOMSelection::rangeCount() const
    195 {
    196     if (!m_frame)
    197         return 0;
    198     return m_frame->selection()->isNone() ? 0 : 1;
    199 }
    200 
    201 void DOMSelection::collapse(Node* node, int offset, ExceptionCode& ec)
    202 {
    203     if (!m_frame)
    204         return;
    205 
    206     if (offset < 0) {
    207         ec = INDEX_SIZE_ERR;
    208         return;
    209     }
    210     m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
    211 }
    212 
    213 void DOMSelection::collapseToEnd()
    214 {
    215     if (!m_frame)
    216         return;
    217 
    218     const VisibleSelection& selection = m_frame->selection()->selection();
    219     m_frame->selection()->moveTo(VisiblePosition(selection.end(), DOWNSTREAM));
    220 }
    221 
    222 void DOMSelection::collapseToStart()
    223 {
    224     if (!m_frame)
    225         return;
    226 
    227     const VisibleSelection& selection = m_frame->selection()->selection();
    228     m_frame->selection()->moveTo(VisiblePosition(selection.start(), DOWNSTREAM));
    229 }
    230 
    231 void DOMSelection::empty()
    232 {
    233     if (!m_frame)
    234         return;
    235     m_frame->selection()->clear();
    236 }
    237 
    238 void DOMSelection::setBaseAndExtent(Node* baseNode, int baseOffset, Node* extentNode, int extentOffset, ExceptionCode& ec)
    239 {
    240     if (!m_frame)
    241         return;
    242 
    243     if (baseOffset < 0 || extentOffset < 0) {
    244         ec = INDEX_SIZE_ERR;
    245         return;
    246     }
    247     VisiblePosition visibleBase = VisiblePosition(baseNode, baseOffset, DOWNSTREAM);
    248     VisiblePosition visibleExtent = VisiblePosition(extentNode, extentOffset, DOWNSTREAM);
    249 
    250     m_frame->selection()->moveTo(visibleBase, visibleExtent);
    251 }
    252 
    253 void DOMSelection::setPosition(Node* node, int offset, ExceptionCode& ec)
    254 {
    255     if (!m_frame)
    256         return;
    257     if (offset < 0) {
    258         ec = INDEX_SIZE_ERR;
    259         return;
    260     }
    261     m_frame->selection()->moveTo(VisiblePosition(node, offset, DOWNSTREAM));
    262 }
    263 
    264 void DOMSelection::modify(const String& alterString, const String& directionString, const String& granularityString)
    265 {
    266     if (!m_frame)
    267         return;
    268 
    269     SelectionController::EAlteration alter;
    270     if (equalIgnoringCase(alterString, "extend"))
    271         alter = SelectionController::EXTEND;
    272     else if (equalIgnoringCase(alterString, "move"))
    273         alter = SelectionController::MOVE;
    274     else
    275         return;
    276 
    277     SelectionController::EDirection direction;
    278     if (equalIgnoringCase(directionString, "forward"))
    279         direction = SelectionController::FORWARD;
    280     else if (equalIgnoringCase(directionString, "backward"))
    281         direction = SelectionController::BACKWARD;
    282     else if (equalIgnoringCase(directionString, "left"))
    283         direction = SelectionController::LEFT;
    284     else if (equalIgnoringCase(directionString, "right"))
    285         direction = SelectionController::RIGHT;
    286     else
    287         return;
    288 
    289     TextGranularity granularity;
    290     if (equalIgnoringCase(granularityString, "character"))
    291         granularity = CharacterGranularity;
    292     else if (equalIgnoringCase(granularityString, "word"))
    293         granularity = WordGranularity;
    294     else if (equalIgnoringCase(granularityString, "sentence"))
    295         granularity = SentenceGranularity;
    296     else if (equalIgnoringCase(granularityString, "line"))
    297         granularity = LineGranularity;
    298     else if (equalIgnoringCase(granularityString, "paragraph"))
    299         granularity = ParagraphGranularity;
    300     else if (equalIgnoringCase(granularityString, "lineboundary"))
    301         granularity = LineBoundary;
    302     else if (equalIgnoringCase(granularityString, "sentenceboundary"))
    303         granularity = SentenceBoundary;
    304     else if (equalIgnoringCase(granularityString, "paragraphboundary"))
    305         granularity = ParagraphBoundary;
    306     else if (equalIgnoringCase(granularityString, "documentboundary"))
    307         granularity = DocumentBoundary;
    308     else
    309         return;
    310 
    311     m_frame->selection()->modify(alter, direction, granularity, false);
    312 }
    313 
    314 void DOMSelection::extend(Node* node, int offset, ExceptionCode& ec)
    315 {
    316     if (!m_frame)
    317         return;
    318 
    319     if (!node) {
    320         ec = TYPE_MISMATCH_ERR;
    321         return;
    322     }
    323     if (offset < 0 || offset > (node->offsetInCharacters() ? caretMaxOffset(node) : (int)node->childNodeCount())) {
    324         ec = INDEX_SIZE_ERR;
    325         return;
    326     }
    327 
    328     SelectionController* selection = m_frame->selection();
    329     selection->expandUsingGranularity(CharacterGranularity);
    330     selection->setExtent(VisiblePosition(node, offset, DOWNSTREAM));
    331 }
    332 
    333 PassRefPtr<Range> DOMSelection::getRangeAt(int index, ExceptionCode& ec)
    334 {
    335     if (!m_frame)
    336         return 0;
    337 
    338     if (index < 0 || index >= rangeCount()) {
    339         ec = INDEX_SIZE_ERR;
    340         return 0;
    341     }
    342 
    343     // If you're hitting this, you've added broken multi-range selection support
    344     ASSERT(rangeCount() == 1);
    345 
    346     if (Node* shadowAncestor = selectionShadowAncestor(m_frame)) {
    347         Node* container = shadowAncestor->parentNode();
    348         int offset = shadowAncestor->nodeIndex();
    349         return Range::create(shadowAncestor->document(), container, offset, container, offset);
    350     }
    351 
    352     const VisibleSelection& selection = m_frame->selection()->selection();
    353     return selection.firstRange();
    354 }
    355 
    356 void DOMSelection::removeAllRanges()
    357 {
    358     if (!m_frame)
    359         return;
    360     m_frame->selection()->clear();
    361 }
    362 
    363 void DOMSelection::addRange(Range* r)
    364 {
    365     if (!m_frame)
    366         return;
    367     if (!r)
    368         return;
    369 
    370     SelectionController* selection = m_frame->selection();
    371 
    372     if (selection->isNone()) {
    373         selection->setSelection(VisibleSelection(r));
    374         return;
    375     }
    376 
    377     RefPtr<Range> range = selection->selection().toNormalizedRange();
    378     ExceptionCode ec = 0;
    379     if (r->compareBoundaryPoints(Range::START_TO_START, range.get(), ec) == -1) {
    380         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
    381         if (r->compareBoundaryPoints(Range::START_TO_END, range.get(), ec) > -1) {
    382             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
    383                 // The original range and r intersect.
    384                 selection->setSelection(VisibleSelection(r->startPosition(), range->endPosition(), DOWNSTREAM));
    385             else
    386                 // r contains the original range.
    387                 selection->setSelection(VisibleSelection(r));
    388         }
    389     } else {
    390         // We don't support discontiguous selection. We don't do anything if r and range don't intersect.
    391         if (r->compareBoundaryPoints(Range::END_TO_START, range.get(), ec) < 1) {
    392             if (r->compareBoundaryPoints(Range::END_TO_END, range.get(), ec) == -1)
    393                 // The original range contains r.
    394                 selection->setSelection(VisibleSelection(range.get()));
    395             else
    396                 // The original range and r intersect.
    397                 selection->setSelection(VisibleSelection(range->startPosition(), r->endPosition(), DOWNSTREAM));
    398         }
    399     }
    400 }
    401 
    402 void DOMSelection::deleteFromDocument()
    403 {
    404     if (!m_frame)
    405         return;
    406 
    407     SelectionController* selection = m_frame->selection();
    408 
    409     if (selection->isNone())
    410         return;
    411 
    412     if (isCollapsed())
    413         selection->modify(SelectionController::EXTEND, SelectionController::BACKWARD, CharacterGranularity);
    414 
    415     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
    416 
    417     ExceptionCode ec = 0;
    418     selectedRange->deleteContents(ec);
    419     ASSERT(!ec);
    420 
    421     setBaseAndExtent(selectedRange->startContainer(ec), selectedRange->startOffset(ec), selectedRange->startContainer(ec), selectedRange->startOffset(ec), ec);
    422     ASSERT(!ec);
    423 }
    424 
    425 bool DOMSelection::containsNode(const Node* n, bool allowPartial) const
    426 {
    427     if (!m_frame)
    428         return false;
    429 
    430     SelectionController* selection = m_frame->selection();
    431 
    432     if (!n || selection->isNone())
    433         return false;
    434 
    435     Node* parentNode = n->parentNode();
    436     unsigned nodeIndex = n->nodeIndex();
    437     RefPtr<Range> selectedRange = selection->selection().toNormalizedRange();
    438 
    439     if (!parentNode)
    440         return false;
    441 
    442     ExceptionCode ec = 0;
    443     bool nodeFullySelected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) >= 0
    444         && Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) <= 0;
    445     ASSERT(!ec);
    446     if (nodeFullySelected)
    447         return true;
    448 
    449     bool nodeFullyUnselected = Range::compareBoundaryPoints(parentNode, nodeIndex, selectedRange->endContainer(ec), selectedRange->endOffset(ec)) > 0
    450         || Range::compareBoundaryPoints(parentNode, nodeIndex + 1, selectedRange->startContainer(ec), selectedRange->startOffset(ec)) < 0;
    451     ASSERT(!ec);
    452     if (nodeFullyUnselected)
    453         return false;
    454 
    455     return allowPartial || n->isTextNode();
    456 }
    457 
    458 void DOMSelection::selectAllChildren(Node* n, ExceptionCode& ec)
    459 {
    460     if (!n)
    461         return;
    462 
    463     // This doesn't (and shouldn't) select text node characters.
    464     setBaseAndExtent(n, 0, n, n->childNodeCount(), ec);
    465 }
    466 
    467 String DOMSelection::toString()
    468 {
    469     if (!m_frame)
    470         return String();
    471 
    472     return plainText(m_frame->selection()->selection().toNormalizedRange().get());
    473 }
    474 
    475 } // namespace WebCore
    476