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