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 "core/dom/Text.h"
     24 
     25 #include "bindings/v8/ExceptionState.h"
     26 #include "bindings/v8/ExceptionStatePlaceholder.h"
     27 #include "core/SVGNames.h"
     28 #include "core/css/resolver/StyleResolver.h"
     29 #include "core/dom/ExceptionCode.h"
     30 #include "core/dom/NodeRenderStyle.h"
     31 #include "core/dom/NodeRenderingTraversal.h"
     32 #include "core/dom/NodeTraversal.h"
     33 #include "core/dom/RenderTreeBuilder.h"
     34 #include "core/dom/shadow/ShadowRoot.h"
     35 #include "core/events/ScopedEventQueue.h"
     36 #include "core/rendering/RenderCombineText.h"
     37 #include "core/rendering/RenderText.h"
     38 #include "core/rendering/svg/RenderSVGInlineText.h"
     39 #include "core/svg/SVGForeignObjectElement.h"
     40 #include "wtf/text/CString.h"
     41 #include "wtf/text/StringBuilder.h"
     42 
     43 namespace WebCore {
     44 
     45 PassRefPtrWillBeRawPtr<Text> Text::create(Document& document, const String& data)
     46 {
     47     return adoptRefWillBeNoop(new Text(document, data, CreateText));
     48 }
     49 
     50 PassRefPtrWillBeRawPtr<Text> Text::createEditingText(Document& document, const String& data)
     51 {
     52     return adoptRefWillBeNoop(new Text(document, data, CreateEditingText));
     53 }
     54 
     55 PassRefPtrWillBeRawPtr<Node> Text::mergeNextSiblingNodesIfPossible()
     56 {
     57     RefPtrWillBeRawPtr<Node> protect(this);
     58 
     59     // Remove empty text nodes.
     60     if (!length()) {
     61         // Care must be taken to get the next node before removing the current node.
     62         RefPtrWillBeRawPtr<Node> nextNode(NodeTraversal::nextPostOrder(*this));
     63         remove(IGNORE_EXCEPTION);
     64         return nextNode.release();
     65     }
     66 
     67     // Merge text nodes.
     68     while (Node* nextSibling = this->nextSibling()) {
     69         if (nextSibling->nodeType() != TEXT_NODE)
     70             break;
     71 
     72         RefPtrWillBeRawPtr<Text> nextText = toText(nextSibling);
     73 
     74         // Remove empty text nodes.
     75         if (!nextText->length()) {
     76             nextText->remove(IGNORE_EXCEPTION);
     77             continue;
     78         }
     79 
     80         // Both non-empty text nodes. Merge them.
     81         unsigned offset = length();
     82         String nextTextData = nextText->data();
     83         String oldTextData = data();
     84         setDataWithoutUpdate(data() + nextTextData);
     85         updateTextRenderer(oldTextData.length(), 0);
     86 
     87         // Empty nextText for layout update.
     88         nextText->setDataWithoutUpdate(emptyString());
     89         nextText->updateTextRenderer(0, nextTextData.length());
     90 
     91         document().didMergeTextNodes(*nextText, offset);
     92 
     93         // Restore nextText for mutation event.
     94         nextText->setDataWithoutUpdate(nextTextData);
     95         nextText->updateTextRenderer(0, 0);
     96 
     97         document().incDOMTreeVersion();
     98         didModifyData(oldTextData);
     99         nextText->remove(IGNORE_EXCEPTION);
    100     }
    101 
    102     return NodeTraversal::nextPostOrder(*this);
    103 }
    104 
    105 PassRefPtrWillBeRawPtr<Text> Text::splitText(unsigned offset, ExceptionState& exceptionState)
    106 {
    107     // IndexSizeError: Raised if the specified offset is negative or greater than
    108     // the number of 16-bit units in data.
    109     if (offset > length()) {
    110         exceptionState.throwDOMException(IndexSizeError, "The offset " + String::number(offset) + " is larger than the Text node's length.");
    111         return nullptr;
    112     }
    113 
    114     EventQueueScope scope;
    115     String oldStr = data();
    116     RefPtrWillBeRawPtr<Text> newText = cloneWithData(oldStr.substring(offset));
    117     setDataWithoutUpdate(oldStr.substring(0, offset));
    118 
    119     didModifyData(oldStr);
    120 
    121     if (parentNode())
    122         parentNode()->insertBefore(newText.get(), nextSibling(), exceptionState);
    123     if (exceptionState.hadException())
    124         return nullptr;
    125 
    126     if (renderer())
    127         toRenderText(renderer())->setTextWithOffset(dataImpl(), 0, oldStr.length());
    128 
    129     if (parentNode())
    130         document().didSplitTextNode(*this);
    131 
    132     return newText.release();
    133 }
    134 
    135 static const Text* earliestLogicallyAdjacentTextNode(const Text* t)
    136 {
    137     const Node* n = t;
    138     while ((n = n->previousSibling())) {
    139         Node::NodeType type = n->nodeType();
    140         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
    141             t = toText(n);
    142             continue;
    143         }
    144 
    145         break;
    146     }
    147     return t;
    148 }
    149 
    150 static const Text* latestLogicallyAdjacentTextNode(const Text* t)
    151 {
    152     const Node* n = t;
    153     while ((n = n->nextSibling())) {
    154         Node::NodeType type = n->nodeType();
    155         if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
    156             t = toText(n);
    157             continue;
    158         }
    159 
    160         break;
    161     }
    162     return t;
    163 }
    164 
    165 String Text::wholeText() const
    166 {
    167     const Text* startText = earliestLogicallyAdjacentTextNode(this);
    168     const Text* endText = latestLogicallyAdjacentTextNode(this);
    169 
    170     Node* onePastEndText = endText->nextSibling();
    171     unsigned resultLength = 0;
    172     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    173         if (!n->isTextNode())
    174             continue;
    175         const String& data = toText(n)->data();
    176         if (std::numeric_limits<unsigned>::max() - data.length() < resultLength)
    177             CRASH();
    178         resultLength += data.length();
    179     }
    180     StringBuilder result;
    181     result.reserveCapacity(resultLength);
    182     for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
    183         if (!n->isTextNode())
    184             continue;
    185         result.append(toText(n)->data());
    186     }
    187     ASSERT(result.length() == resultLength);
    188 
    189     return result.toString();
    190 }
    191 
    192 PassRefPtrWillBeRawPtr<Text> Text::replaceWholeText(const String& newText)
    193 {
    194     // Remove all adjacent text nodes, and replace the contents of this one.
    195 
    196     // Protect startText and endText against mutation event handlers removing the last ref
    197     RefPtrWillBeRawPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this));
    198     RefPtrWillBeRawPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this));
    199 
    200     RefPtrWillBeRawPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away
    201     RefPtrWillBeRawPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal
    202     for (RefPtrWillBeRawPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) {
    203         RefPtrWillBeRawPtr<Node> nodeToRemove(n.release());
    204         n = nodeToRemove->nextSibling();
    205         parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION);
    206     }
    207 
    208     if (this != endText) {
    209         Node* onePastEndText = endText->nextSibling();
    210         for (RefPtrWillBeRawPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) {
    211             RefPtrWillBeRawPtr<Node> nodeToRemove(n.release());
    212             n = nodeToRemove->nextSibling();
    213             parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION);
    214         }
    215     }
    216 
    217     if (newText.isEmpty()) {
    218         if (parent && parentNode() == parent)
    219             parent->removeChild(this, IGNORE_EXCEPTION);
    220         return nullptr;
    221     }
    222 
    223     setData(newText);
    224     return protectedThis.release();
    225 }
    226 
    227 String Text::nodeName() const
    228 {
    229     return "#text";
    230 }
    231 
    232 Node::NodeType Text::nodeType() const
    233 {
    234     return TEXT_NODE;
    235 }
    236 
    237 PassRefPtrWillBeRawPtr<Node> Text::cloneNode(bool /*deep*/)
    238 {
    239     return cloneWithData(data());
    240 }
    241 
    242 bool Text::textRendererIsNeeded(const RenderStyle& style, const RenderObject& parent)
    243 {
    244     if (isEditingText())
    245         return true;
    246 
    247     if (!length())
    248         return false;
    249 
    250     if (style.display() == NONE)
    251         return false;
    252 
    253     if (!containsOnlyWhitespace())
    254         return true;
    255 
    256     if (!parent.canHaveWhitespaceChildren())
    257         return false;
    258 
    259     if (style.preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
    260         return true;
    261 
    262     const RenderObject* prev = NodeRenderingTraversal::previousSiblingRenderer(this);
    263     if (prev && prev->isBR()) // <span><br/> <br/></span>
    264         return false;
    265 
    266     if (parent.isRenderInline()) {
    267         // <span><div/> <div/></span>
    268         if (prev && !prev->isInline())
    269             return false;
    270     } else {
    271         if (parent.isRenderBlock() && !parent.childrenInline() && (!prev || !prev->isInline()))
    272             return false;
    273 
    274         // Avoiding creation of a Renderer for the text node is a non-essential memory optimization.
    275         // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit.
    276         unsigned maxSiblingsToVisit = 50;
    277 
    278         RenderObject* first = parent.slowFirstChild();
    279         while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--)
    280             first = first->nextSibling();
    281         if (!first || NodeRenderingTraversal::nextSiblingRenderer(this) == first)
    282             // Whitespace at the start of a block just goes away.  Don't even
    283             // make a render object for this text.
    284             return false;
    285     }
    286     return true;
    287 }
    288 
    289 static bool isSVGText(Text* text)
    290 {
    291     Node* parentOrShadowHostNode = text->parentOrShadowHostNode();
    292     ASSERT(parentOrShadowHostNode);
    293     return parentOrShadowHostNode->isSVGElement() && !isSVGForeignObjectElement(*parentOrShadowHostNode);
    294 }
    295 
    296 RenderText* Text::createTextRenderer(RenderStyle* style)
    297 {
    298     if (isSVGText(this))
    299         return new RenderSVGInlineText(this, dataImpl());
    300 
    301     if (style->hasTextCombine())
    302         return new RenderCombineText(this, dataImpl());
    303 
    304     return new RenderText(this, dataImpl());
    305 }
    306 
    307 void Text::attach(const AttachContext& context)
    308 {
    309     RenderTreeBuilder(this, context.resolvedStyle).createRendererForTextIfNeeded();
    310     CharacterData::attach(context);
    311 }
    312 
    313 void Text::recalcTextStyle(StyleRecalcChange change, Text* nextTextSibling)
    314 {
    315     if (RenderText* renderer = toRenderText(this->renderer())) {
    316         if (change != NoChange || needsStyleRecalc())
    317             renderer->setStyle(document().ensureStyleResolver().styleForText(this));
    318         if (needsStyleRecalc())
    319             renderer->setText(dataImpl());
    320         clearNeedsStyleRecalc();
    321     } else if (needsStyleRecalc() || needsWhitespaceRenderer()) {
    322         reattach();
    323         reattachWhitespaceSiblings(nextTextSibling);
    324     }
    325 }
    326 
    327 // If a whitespace node had no renderer and goes through a recalcStyle it may
    328 // need to create one if the parent style now has white-space: pre.
    329 bool Text::needsWhitespaceRenderer()
    330 {
    331     ASSERT(!renderer());
    332     if (RenderStyle* style = parentRenderStyle())
    333         return style->preserveNewline();
    334     return false;
    335 }
    336 
    337 void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData, RecalcStyleBehavior recalcStyleBehavior)
    338 {
    339     if (!inActiveDocument())
    340         return;
    341     RenderText* textRenderer = toRenderText(renderer());
    342     if (!textRenderer || !textRendererIsNeeded(*textRenderer->style(), *textRenderer->parent())) {
    343         lazyReattachIfAttached();
    344         // FIXME: Editing should be updated so this is not neccesary.
    345         if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing)
    346             document().updateRenderTreeIfNeeded();
    347         return;
    348     }
    349     textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
    350 }
    351 
    352 PassRefPtrWillBeRawPtr<Text> Text::cloneWithData(const String& data)
    353 {
    354     return create(document(), data);
    355 }
    356 
    357 #ifndef NDEBUG
    358 void Text::formatForDebugger(char *buffer, unsigned length) const
    359 {
    360     StringBuilder result;
    361     String s;
    362 
    363     result.append(nodeName());
    364 
    365     s = data();
    366     if (s.length() > 0) {
    367         if (result.length())
    368             result.appendLiteral("; ");
    369         result.appendLiteral("value=");
    370         result.append(s);
    371     }
    372 
    373     strncpy(buffer, result.toString().utf8().data(), length - 1);
    374 }
    375 #endif
    376 
    377 } // namespace WebCore
    378