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