Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      5  *
      6  * This library is free software; you can redistribute it and/or
      7  * modify it under the terms of the GNU Library General Public
      8  * License as published by the Free Software Foundation; either
      9  * version 2 of the License, or (at your option) any later version.
     10  *
     11  * This library is distributed in the hope that it will be useful,
     12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14  * Library General Public License for more details.
     15  *
     16  * You should have received a copy of the GNU Library General Public License
     17  * along with this library; see the file COPYING.LIB.  If not, write to
     18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19  * Boston, MA 02110-1301, USA.
     20  */
     21 
     22 #include "config.h"
     23 #include "Text.h"
     24 
     25 #include "ExceptionCode.h"
     26 #include "RenderCombineText.h"
     27 #include "RenderText.h"
     28 
     29 #if ENABLE(SVG)
     30 #include "RenderSVGInlineText.h"
     31 #include "SVGNames.h"
     32 #endif
     33 
     34 #include <wtf/text/CString.h>
     35 
     36 #if ENABLE(WML)
     37 #include "WMLDocument.h"
     38 #include "WMLVariables.h"
     39 #endif
     40 
     41 using namespace std;
     42 
     43 namespace WebCore {
     44 
     45 PassRefPtr<Text> Text::create(Document* document, const String& data)
     46 {
     47     return adoptRef(new Text(document, data));
     48 }
     49 
     50 PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec)
     51 {
     52     ec = 0;
     53 
     54     // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
     55     // the number of 16-bit units in data.
     56     if (offset > length()) {
     57         ec = INDEX_SIZE_ERR;
     58         return 0;
     59     }
     60 
     61     RefPtr<StringImpl> oldStr = dataImpl();
     62     RefPtr<Text> newText = virtualCreate(oldStr->substring(offset));
     63     setDataImpl(oldStr->substring(0, offset));
     64 
     65     dispatchModifiedEvent(oldStr.get());
     66 
     67     if (parentNode())
     68         parentNode()->insertBefore(newText.get(), nextSibling(), ec);
     69     if (ec)
     70         return 0;
     71 
     72     if (parentNode())
     73         document()->textNodeSplit(this);
     74 
     75     if (renderer())
     76         toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr->length());
     77 
     78     return newText.release();
     79 }
     80 
     81 static const Text* earliestLogicallyAdjacentTextNode(const Text* t)
     82 {
     83     const Node* n = t;
     84     while ((n = n->previousSibling())) {
     85         Node::NodeType type = n->nodeType();
     86         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
     87             t = static_cast<const Text*>(n);
     88             continue;
     89         }
     90 
     91         // We would need to visit EntityReference child text nodes if they existed
     92         ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
     93         break;
     94     }
     95     return t;
     96 }
     97 
     98 static const Text* latestLogicallyAdjacentTextNode(const Text* t)
     99 {
    100     const Node* n = t;
    101     while ((n = n->nextSibling())) {
    102         Node::NodeType type = n->nodeType();
    103         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
    104             t = static_cast<const Text*>(n);
    105             continue;
    106         }
    107 
    108         // We would need to visit EntityReference child text nodes if they existed
    109         ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
    110         break;
    111     }
    112     return t;
    113 }
    114 
    115 String Text::wholeText() const
    116 {
    117     const Text* startText = earliestLogicallyAdjacentTextNode(this);
    118     const Text* endText = latestLogicallyAdjacentTextNode(this);
    119 
    120     Node* onePastEndText = endText->nextSibling();
    121     unsigned resultLength = 0;
    122     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    123         if (!n->isTextNode())
    124             continue;
    125         const Text* t = static_cast<const Text*>(n);
    126         const String& data = t->data();
    127         if (std::numeric_limits<unsigned>::max() - data.length() < resultLength)
    128             CRASH();
    129         resultLength += data.length();
    130     }
    131     UChar* resultData;
    132     String result = String::createUninitialized(resultLength, resultData);
    133     UChar* p = resultData;
    134     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    135         if (!n->isTextNode())
    136             continue;
    137         const Text* t = static_cast<const Text*>(n);
    138         const String& data = t->data();
    139         unsigned dataLength = data.length();
    140         memcpy(p, data.characters(), dataLength * sizeof(UChar));
    141         p += dataLength;
    142     }
    143     ASSERT(p == resultData + resultLength);
    144 
    145     return result;
    146 }
    147 
    148 PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&)
    149 {
    150     // Remove all adjacent text nodes, and replace the contents of this one.
    151 
    152     // Protect startText and endText against mutation event handlers removing the last ref
    153     RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this));
    154     RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this));
    155 
    156     RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away
    157     ContainerNode* parent = parentNode(); // Protect against mutation handlers moving this node during traversal
    158     ExceptionCode ignored = 0;
    159     for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) {
    160         RefPtr<Node> nodeToRemove(n.release());
    161         n = nodeToRemove->nextSibling();
    162         parent->removeChild(nodeToRemove.get(), ignored);
    163     }
    164 
    165     if (this != endText) {
    166         Node* onePastEndText = endText->nextSibling();
    167         for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) {
    168             RefPtr<Node> nodeToRemove(n.release());
    169             n = nodeToRemove->nextSibling();
    170             parent->removeChild(nodeToRemove.get(), ignored);
    171         }
    172     }
    173 
    174     if (newText.isEmpty()) {
    175         if (parent && parentNode() == parent)
    176             parent->removeChild(this, ignored);
    177         return 0;
    178     }
    179 
    180     setData(newText, ignored);
    181     return protectedThis.release();
    182 }
    183 
    184 String Text::nodeName() const
    185 {
    186     return textAtom.string();
    187 }
    188 
    189 Node::NodeType Text::nodeType() const
    190 {
    191     return TEXT_NODE;
    192 }
    193 
    194 PassRefPtr<Node> Text::cloneNode(bool /*deep*/)
    195 {
    196     return create(document(), data());
    197 }
    198 
    199 bool Text::rendererIsNeeded(RenderStyle *style)
    200 {
    201     if (!CharacterData::rendererIsNeeded(style))
    202         return false;
    203 
    204     bool onlyWS = containsOnlyWhitespace();
    205     if (!onlyWS)
    206         return true;
    207 
    208     RenderObject *par = parentNode()->renderer();
    209 
    210     if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet())
    211         return false;
    212 
    213     if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
    214         return true;
    215 
    216     RenderObject *prev = previousRenderer();
    217     if (prev && prev->isBR()) // <span><br/> <br/></span>
    218         return false;
    219 
    220     if (par->isRenderInline()) {
    221         // <span><div/> <div/></span>
    222         if (prev && !prev->isInline())
    223             return false;
    224     } else {
    225         if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline()))
    226             return false;
    227 
    228         RenderObject *first = par->firstChild();
    229         while (first && first->isFloatingOrPositioned())
    230             first = first->nextSibling();
    231         RenderObject *next = nextRenderer();
    232         if (!first || next == first)
    233             // Whitespace at the start of a block just goes away.  Don't even
    234             // make a render object for this text.
    235             return false;
    236     }
    237 
    238     return true;
    239 }
    240 
    241 RenderObject* Text::createRenderer(RenderArena* arena, RenderStyle* style)
    242 {
    243 #if ENABLE(SVG)
    244     Node* parentOrHost = parentOrHostNode();
    245     if (parentOrHost->isSVGElement()
    246 #if ENABLE(SVG_FOREIGN_OBJECT)
    247         && !parentOrHost->hasTagName(SVGNames::foreignObjectTag)
    248 #endif
    249     )
    250         return new (arena) RenderSVGInlineText(this, dataImpl());
    251 #endif
    252 
    253     if (style->hasTextCombine())
    254         return new (arena) RenderCombineText(this, dataImpl());
    255 
    256     return new (arena) RenderText(this, dataImpl());
    257 }
    258 
    259 void Text::attach()
    260 {
    261 #if ENABLE(WML)
    262     if (document()->isWMLDocument() && !containsOnlyWhitespace()) {
    263         String text = data();
    264         ASSERT(!text.isEmpty());
    265 
    266         text = substituteVariableReferences(text, document());
    267 
    268         ExceptionCode code = 0;
    269         setData(text, code);
    270         ASSERT(!code);
    271     }
    272 #endif
    273 
    274     createRendererIfNeeded();
    275     CharacterData::attach();
    276 }
    277 
    278 void Text::recalcStyle(StyleChange change)
    279 {
    280     if (change != NoChange && parentNode()) {
    281         if (renderer())
    282             renderer()->setStyle(parentNode()->renderer()->style());
    283     }
    284     if (needsStyleRecalc()) {
    285         if (renderer()) {
    286             if (renderer()->isText())
    287                 toRenderText(renderer())->setText(dataImpl());
    288         } else {
    289             if (attached())
    290                 detach();
    291             attach();
    292         }
    293     }
    294     clearNeedsStyleRecalc();
    295 }
    296 
    297 bool Text::childTypeAllowed(NodeType) const
    298 {
    299     return false;
    300 }
    301 
    302 PassRefPtr<Text> Text::virtualCreate(const String& data)
    303 {
    304     return create(document(), data);
    305 }
    306 
    307 PassRefPtr<Text> Text::createWithLengthLimit(Document* document, const String& data, unsigned start, unsigned maxChars)
    308 {
    309     unsigned dataLength = data.length();
    310 
    311     if (!start && dataLength <= maxChars)
    312         return create(document, data);
    313 
    314     RefPtr<Text> result = Text::create(document, String());
    315     result->parserAppendData(data.characters() + start, dataLength - start, maxChars);
    316 
    317     return result;
    318 }
    319 
    320 #ifndef NDEBUG
    321 void Text::formatForDebugger(char *buffer, unsigned length) const
    322 {
    323     String result;
    324     String s;
    325 
    326     s = nodeName();
    327     if (s.length() > 0) {
    328         result += s;
    329     }
    330 
    331     s = data();
    332     if (s.length() > 0) {
    333         if (result.length() > 0)
    334             result += "; ";
    335         result += "value=";
    336         result += s;
    337     }
    338 
    339     strncpy(buffer, result.utf8().data(), length - 1);
    340 }
    341 #endif
    342 
    343 } // namespace WebCore
    344