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 "CString.h"
     26 #include "ExceptionCode.h"
     27 #include "RenderText.h"
     28 #include "TextBreakIterator.h"
     29 
     30 #if ENABLE(SVG)
     31 #include "RenderSVGInlineText.h"
     32 #include "SVGNames.h"
     33 #endif
     34 
     35 #if ENABLE(WML)
     36 #include "WMLDocument.h"
     37 #include "WMLVariables.h"
     38 #endif
     39 
     40 using namespace std;
     41 
     42 namespace WebCore {
     43 
     44 Text::Text(Document* document, const String& data)
     45     : CharacterData(document, data, CreateText)
     46 {
     47 }
     48 
     49 PassRefPtr<Text> Text::create(Document* document, const String& data)
     50 {
     51     return adoptRef(new Text(document, data));
     52 }
     53 
     54 PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec)
     55 {
     56     ec = 0;
     57 
     58     // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
     59     // the number of 16-bit units in data.
     60     if (offset > length()) {
     61         ec = INDEX_SIZE_ERR;
     62         return 0;
     63     }
     64 
     65     RefPtr<StringImpl> oldStr = dataImpl();
     66     RefPtr<Text> newText = virtualCreate(oldStr->substring(offset));
     67     setDataImpl(oldStr->substring(0, offset));
     68 
     69     dispatchModifiedEvent(oldStr.get());
     70 
     71     if (parentNode())
     72         parentNode()->insertBefore(newText.get(), nextSibling(), ec);
     73     if (ec)
     74         return 0;
     75 
     76     if (parentNode())
     77         document()->textNodeSplit(this);
     78 
     79     if (renderer())
     80         toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr->length());
     81 
     82     return newText.release();
     83 }
     84 
     85 static const Text* earliestLogicallyAdjacentTextNode(const Text* t)
     86 {
     87     const Node* n = t;
     88     while ((n = n->previousSibling())) {
     89         Node::NodeType type = n->nodeType();
     90         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
     91             t = static_cast<const Text*>(n);
     92             continue;
     93         }
     94 
     95         // We would need to visit EntityReference child text nodes if they existed
     96         ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
     97         break;
     98     }
     99     return t;
    100 }
    101 
    102 static const Text* latestLogicallyAdjacentTextNode(const Text* t)
    103 {
    104     const Node* n = t;
    105     while ((n = n->nextSibling())) {
    106         Node::NodeType type = n->nodeType();
    107         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
    108             t = static_cast<const Text*>(n);
    109             continue;
    110         }
    111 
    112         // We would need to visit EntityReference child text nodes if they existed
    113         ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
    114         break;
    115     }
    116     return t;
    117 }
    118 
    119 String Text::wholeText() const
    120 {
    121     const Text* startText = earliestLogicallyAdjacentTextNode(this);
    122     const Text* endText = latestLogicallyAdjacentTextNode(this);
    123 
    124     Node* onePastEndText = endText->nextSibling();
    125     unsigned resultLength = 0;
    126     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    127         if (!n->isTextNode())
    128             continue;
    129         const Text* t = static_cast<const Text*>(n);
    130         const String& data = t->data();
    131         if (std::numeric_limits<unsigned>::max() - data.length() < resultLength)
    132             CRASH();
    133         resultLength += data.length();
    134     }
    135     UChar* resultData;
    136     String result = String::createUninitialized(resultLength, resultData);
    137     UChar* p = resultData;
    138     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    139         if (!n->isTextNode())
    140             continue;
    141         const Text* t = static_cast<const Text*>(n);
    142         const String& data = t->data();
    143         unsigned dataLength = data.length();
    144         memcpy(p, data.characters(), dataLength * sizeof(UChar));
    145         p += dataLength;
    146     }
    147     ASSERT(p == resultData + resultLength);
    148 
    149     return result;
    150 }
    151 
    152 PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&)
    153 {
    154     // Remove all adjacent text nodes, and replace the contents of this one.
    155 
    156     // Protect startText and endText against mutation event handlers removing the last ref
    157     RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this));
    158     RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this));
    159 
    160     RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away
    161     Node* parent = parentNode(); // Protect against mutation handlers moving this node during traversal
    162     ExceptionCode ignored = 0;
    163     for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) {
    164         RefPtr<Node> nodeToRemove(n.release());
    165         n = nodeToRemove->nextSibling();
    166         parent->removeChild(nodeToRemove.get(), ignored);
    167     }
    168 
    169     if (this != endText) {
    170         Node* onePastEndText = endText->nextSibling();
    171         for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) {
    172             RefPtr<Node> nodeToRemove(n.release());
    173             n = nodeToRemove->nextSibling();
    174             parent->removeChild(nodeToRemove.get(), ignored);
    175         }
    176     }
    177 
    178     if (newText.isEmpty()) {
    179         if (parent && parentNode() == parent)
    180             parent->removeChild(this, ignored);
    181         return 0;
    182     }
    183 
    184     setData(newText, ignored);
    185     return protectedThis.release();
    186 }
    187 
    188 String Text::nodeName() const
    189 {
    190     return textAtom.string();
    191 }
    192 
    193 Node::NodeType Text::nodeType() const
    194 {
    195     return TEXT_NODE;
    196 }
    197 
    198 PassRefPtr<Node> Text::cloneNode(bool /*deep*/)
    199 {
    200     return create(document(), data());
    201 }
    202 
    203 bool Text::rendererIsNeeded(RenderStyle *style)
    204 {
    205     if (!CharacterData::rendererIsNeeded(style))
    206         return false;
    207 
    208     bool onlyWS = containsOnlyWhitespace();
    209     if (!onlyWS)
    210         return true;
    211 
    212     RenderObject *par = parentNode()->renderer();
    213 
    214     if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet())
    215         return false;
    216 
    217     if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
    218         return true;
    219 
    220     RenderObject *prev = previousRenderer();
    221     if (prev && prev->isBR()) // <span><br/> <br/></span>
    222         return false;
    223 
    224     if (par->isRenderInline()) {
    225         // <span><div/> <div/></span>
    226         if (prev && !prev->isInline())
    227             return false;
    228     } else {
    229         if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline()))
    230             return false;
    231 
    232         RenderObject *first = par->firstChild();
    233         while (first && first->isFloatingOrPositioned())
    234             first = first->nextSibling();
    235         RenderObject *next = nextRenderer();
    236         if (!first || next == first)
    237             // Whitespace at the start of a block just goes away.  Don't even
    238             // make a render object for this text.
    239             return false;
    240     }
    241 
    242     return true;
    243 }
    244 
    245 RenderObject* Text::createRenderer(RenderArena* arena, RenderStyle*)
    246 {
    247 #if ENABLE(SVG)
    248     if (parentNode()->isSVGElement()
    249 #if ENABLE(SVG_FOREIGN_OBJECT)
    250         && !parentNode()->hasTagName(SVGNames::foreignObjectTag)
    251 #endif
    252     )
    253         return new (arena) RenderSVGInlineText(this, dataImpl());
    254 #endif
    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     setNeedsStyleRecalc(NoStyleChange);
    295 }
    296 
    297 bool Text::childTypeAllowed(NodeType)
    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& charsLeft, unsigned maxChars)
    308 {
    309     unsigned dataLength = data.length();
    310 
    311     if (charsLeft == dataLength && charsLeft <= maxChars) {
    312         charsLeft = 0;
    313         return create(document, data);
    314     }
    315 
    316     unsigned start = dataLength - charsLeft;
    317     unsigned end = start + min(charsLeft, maxChars);
    318 
    319     // Check we are not on an unbreakable boundary.
    320     // Some text break iterator implementations work best if the passed buffer is as small as possible,
    321     // see <https://bugs.webkit.org/show_bug.cgi?id=29092>.
    322     // We need at least two characters look-ahead to account for UTF-16 surrogates.
    323     if (end < dataLength) {
    324         TextBreakIterator* it = characterBreakIterator(data.characters() + start, (end + 2 > dataLength) ? dataLength - start : end - start + 2);
    325         if (!isTextBreak(it, end - start))
    326             end = textBreakPreceding(it, end - start) + start;
    327     }
    328 
    329     // If we have maxChars of unbreakable characters the above could lead to
    330     // an infinite loop.
    331     // FIXME: It would be better to just have the old value of end before calling
    332     // textBreakPreceding rather than this, because this exceeds the length limit.
    333     if (end <= start)
    334         end = dataLength;
    335 
    336     charsLeft = dataLength - end;
    337     return create(document, data.substring(start, end - start));
    338 }
    339 
    340 #ifndef NDEBUG
    341 void Text::formatForDebugger(char *buffer, unsigned length) const
    342 {
    343     String result;
    344     String s;
    345 
    346     s = nodeName();
    347     if (s.length() > 0) {
    348         result += s;
    349     }
    350 
    351     s = data();
    352     if (s.length() > 0) {
    353         if (result.length() > 0)
    354             result += "; ";
    355         result += "value=";
    356         result += s;
    357     }
    358 
    359     strncpy(buffer, result.utf8().data(), length - 1);
    360 }
    361 #endif
    362 
    363 } // namespace WebCore
    364