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