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