1/*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 *           (C) 1999 Antti Koivisto (koivisto@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/core/v8/ExceptionState.h"
26#include "bindings/core/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
43namespace blink {
44
45PassRefPtrWillBeRawPtr<Text> Text::create(Document& document, const String& data)
46{
47    return adoptRefWillBeNoop(new Text(document, data, CreateText));
48}
49
50PassRefPtrWillBeRawPtr<Text> Text::createEditingText(Document& document, const String& data)
51{
52    return adoptRefWillBeNoop(new Text(document, data, CreateEditingText));
53}
54
55PassRefPtrWillBeRawPtr<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
105PassRefPtrWillBeRawPtr<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        renderer()->setTextWithOffset(dataImpl(), 0, oldStr.length());
128
129    if (parentNode())
130        document().didSplitTextNode(*this);
131
132    return newText.release();
133}
134
135static const Text* earliestLogicallyAdjacentTextNode(const Text* t)
136{
137    for (const Node* n = t->previousSibling(); n; n = n->previousSibling()) {
138        Node::NodeType type = n->nodeType();
139        if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
140            t = toText(n);
141            continue;
142        }
143
144        break;
145    }
146    return t;
147}
148
149static const Text* latestLogicallyAdjacentTextNode(const Text* t)
150{
151    for (const Node* n = t->nextSibling(); n; n = n->nextSibling()) {
152        Node::NodeType type = n->nodeType();
153        if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
154            t = toText(n);
155            continue;
156        }
157
158        break;
159    }
160    return t;
161}
162
163String Text::wholeText() const
164{
165    const Text* startText = earliestLogicallyAdjacentTextNode(this);
166    const Text* endText = latestLogicallyAdjacentTextNode(this);
167
168    Node* onePastEndText = endText->nextSibling();
169    unsigned resultLength = 0;
170    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
171        if (!n->isTextNode())
172            continue;
173        const String& data = toText(n)->data();
174        if (std::numeric_limits<unsigned>::max() - data.length() < resultLength)
175            CRASH();
176        resultLength += data.length();
177    }
178    StringBuilder result;
179    result.reserveCapacity(resultLength);
180    for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
181        if (!n->isTextNode())
182            continue;
183        result.append(toText(n)->data());
184    }
185    ASSERT(result.length() == resultLength);
186
187    return result.toString();
188}
189
190PassRefPtrWillBeRawPtr<Text> Text::replaceWholeText(const String& newText)
191{
192    // Remove all adjacent text nodes, and replace the contents of this one.
193
194    // Protect startText and endText against mutation event handlers removing the last ref
195    RefPtrWillBeRawPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this));
196    RefPtrWillBeRawPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this));
197
198    RefPtrWillBeRawPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away
199    RefPtrWillBeRawPtr<ContainerNode> parent = parentNode(); // Protect against mutation handlers moving this node during traversal
200    for (RefPtrWillBeRawPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) {
201        RefPtrWillBeRawPtr<Node> nodeToRemove(n.release());
202        n = nodeToRemove->nextSibling();
203        parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION);
204    }
205
206    if (this != endText) {
207        Node* onePastEndText = endText->nextSibling();
208        for (RefPtrWillBeRawPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) {
209            RefPtrWillBeRawPtr<Node> nodeToRemove(n.release());
210            n = nodeToRemove->nextSibling();
211            parent->removeChild(nodeToRemove.get(), IGNORE_EXCEPTION);
212        }
213    }
214
215    if (newText.isEmpty()) {
216        if (parent && parentNode() == parent)
217            parent->removeChild(this, IGNORE_EXCEPTION);
218        return nullptr;
219    }
220
221    setData(newText);
222    return protectedThis.release();
223}
224
225String Text::nodeName() const
226{
227    return "#text";
228}
229
230Node::NodeType Text::nodeType() const
231{
232    return TEXT_NODE;
233}
234
235PassRefPtrWillBeRawPtr<Node> Text::cloneNode(bool /*deep*/)
236{
237    return cloneWithData(data());
238}
239
240bool Text::textRendererIsNeeded(const RenderStyle& style, const RenderObject& parent)
241{
242    if (isEditingText())
243        return true;
244
245    if (!length())
246        return false;
247
248    if (style.display() == NONE)
249        return false;
250
251    if (!containsOnlyWhitespace())
252        return true;
253
254    if (!parent.canHaveWhitespaceChildren())
255        return false;
256
257    if (style.preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
258        return true;
259
260    const RenderObject* prev = NodeRenderingTraversal::previousSiblingRenderer(this);
261    if (prev && prev->isBR()) // <span><br/> <br/></span>
262        return false;
263
264    if (parent.isRenderInline()) {
265        // <span><div/> <div/></span>
266        if (prev && !prev->isInline())
267            return false;
268    } else {
269        if (parent.isRenderBlock() && !parent.childrenInline() && (!prev || !prev->isInline()))
270            return false;
271
272        // Avoiding creation of a Renderer for the text node is a non-essential memory optimization.
273        // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit.
274        unsigned maxSiblingsToVisit = 50;
275
276        RenderObject* first = parent.slowFirstChild();
277        while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--)
278            first = first->nextSibling();
279        if (!first || NodeRenderingTraversal::nextSiblingRenderer(this) == first)
280            // Whitespace at the start of a block just goes away.  Don't even
281            // make a render object for this text.
282            return false;
283    }
284    return true;
285}
286
287static bool isSVGText(Text* text)
288{
289    Node* parentOrShadowHostNode = text->parentOrShadowHostNode();
290    ASSERT(parentOrShadowHostNode);
291    return parentOrShadowHostNode->isSVGElement() && !isSVGForeignObjectElement(*parentOrShadowHostNode);
292}
293
294RenderText* Text::createTextRenderer(RenderStyle* style)
295{
296    if (isSVGText(this))
297        return new RenderSVGInlineText(this, dataImpl());
298
299    if (style->hasTextCombine())
300        return new RenderCombineText(this, dataImpl());
301
302    return new RenderText(this, dataImpl());
303}
304
305void Text::attach(const AttachContext& context)
306{
307    RenderTreeBuilder(this, context.resolvedStyle).createRendererForTextIfNeeded();
308    CharacterData::attach(context);
309}
310
311void Text::recalcTextStyle(StyleRecalcChange change, Text* nextTextSibling)
312{
313    if (RenderText* renderer = this->renderer()) {
314        if (change != NoChange || needsStyleRecalc())
315            renderer->setStyle(document().ensureStyleResolver().styleForText(this));
316        if (needsStyleRecalc())
317            renderer->setText(dataImpl());
318        clearNeedsStyleRecalc();
319    } else if (needsStyleRecalc() || needsWhitespaceRenderer()) {
320        reattach();
321        if (this->renderer())
322            reattachWhitespaceSiblings(nextTextSibling);
323    }
324}
325
326// If a whitespace node had no renderer and goes through a recalcStyle it may
327// need to create one if the parent style now has white-space: pre.
328bool Text::needsWhitespaceRenderer()
329{
330    ASSERT(!renderer());
331    if (RenderStyle* style = parentRenderStyle())
332        return style->preserveNewline();
333    return false;
334}
335
336void Text::updateTextRenderer(unsigned offsetOfReplacedData, unsigned lengthOfReplacedData, RecalcStyleBehavior recalcStyleBehavior)
337{
338    if (!inActiveDocument())
339        return;
340    RenderText* textRenderer = renderer();
341    if (!textRenderer || !textRendererIsNeeded(*textRenderer->style(), *textRenderer->parent())) {
342        lazyReattachIfAttached();
343        // FIXME: Editing should be updated so this is not neccesary.
344        if (recalcStyleBehavior == DeprecatedRecalcStyleImmediatlelyForEditing)
345            document().updateRenderTreeIfNeeded();
346        return;
347    }
348    textRenderer->setTextWithOffset(dataImpl(), offsetOfReplacedData, lengthOfReplacedData);
349}
350
351PassRefPtrWillBeRawPtr<Text> Text::cloneWithData(const String& data)
352{
353    return create(document(), data);
354}
355
356#ifndef NDEBUG
357void Text::formatForDebugger(char *buffer, unsigned length) const
358{
359    StringBuilder result;
360    String s;
361
362    result.append(nodeName());
363
364    s = data();
365    if (s.length() > 0) {
366        if (result.length())
367            result.appendLiteral("; ");
368        result.appendLiteral("value=");
369        result.append(s);
370    }
371
372    strncpy(buffer, result.toString().utf8().data(), length - 1);
373}
374#endif
375
376} // namespace blink
377