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