2008-11-04 Anders Carlsson <andersca@apple.com>
[webkit/qt.git] / WebCore / dom / Text.cpp
blob723997ec73f16763dc5b0b56cfbdc0d3124b1943
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.
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.
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.
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.
22 #include "config.h"
23 #include "Text.h"
25 #include "CString.h"
26 #include "Document.h"
27 #include "ExceptionCode.h"
28 #include "RenderText.h"
29 #include "TextBreakIterator.h"
31 #if ENABLE(SVG)
32 #include "RenderSVGInlineText.h"
33 #endif // ENABLE(SVG)
35 namespace WebCore {
37 // DOM Section 1.1.1
39 Text::Text(Document* document, const String& text)
40 : CharacterData(document, text)
44 Text::Text(Document* document)
45 : CharacterData(document)
49 Text::~Text()
53 PassRefPtr<Text> Text::splitText(unsigned offset, ExceptionCode& ec)
55 ec = 0;
57 // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
58 // the number of 16-bit units in data.
59 if (offset > m_data->length()) {
60 ec = INDEX_SIZE_ERR;
61 return 0;
64 RefPtr<StringImpl> oldStr = m_data;
65 RefPtr<Text> newText = createNew(oldStr->substring(offset));
66 m_data = oldStr->substring(0, offset);
68 dispatchModifiedEvent(oldStr.get());
70 if (parentNode())
71 parentNode()->insertBefore(newText.get(), nextSibling(), ec);
72 if (ec)
73 return 0;
75 if (parentNode())
76 document()->textNodeSplit(this);
78 if (renderer())
79 static_cast<RenderText*>(renderer())->setText(m_data);
81 return newText.release();
84 static const Text* earliestLogicallyAdjacentTextNode(const Text* t)
86 const Node* n = t;
87 while ((n = n->previousSibling())) {
88 Node::NodeType type = n->nodeType();
89 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
90 t = static_cast<const Text*>(n);
91 continue;
94 // We would need to visit EntityReference child text nodes if they existed
95 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
96 break;
98 return t;
101 static const Text* latestLogicallyAdjacentTextNode(const Text* t)
103 const Node* n = t;
104 while ((n = n->nextSibling())) {
105 Node::NodeType type = n->nodeType();
106 if (type == Node::TEXT_NODE || type == Node::CDATA_SECTION_NODE) {
107 t = static_cast<const Text*>(n);
108 continue;
111 // We would need to visit EntityReference child text nodes if they existed
112 ASSERT(type != Node::ENTITY_REFERENCE_NODE || !n->hasChildNodes());
113 break;
115 return t;
118 String Text::wholeText() const
120 const Text* startText = earliestLogicallyAdjacentTextNode(this);
121 const Text* endText = latestLogicallyAdjacentTextNode(this);
123 Vector<UChar> result;
124 Node* onePastEndText = endText->nextSibling();
125 for (const Node* n = startText; n != onePastEndText; n = n->nextSibling()) {
126 if (!n->isTextNode())
127 continue;
128 const Text* t = static_cast<const Text*>(n);
129 const String& data = t->data();
130 result.append(data.characters(), data.length());
133 return String::adopt(result);
136 PassRefPtr<Text> Text::replaceWholeText(const String& newText, ExceptionCode&)
138 // Remove all adjacent text nodes, and replace the contents of this one.
140 // Protect startText and endText against mutation event handlers removing the last ref
141 RefPtr<Text> startText = const_cast<Text*>(earliestLogicallyAdjacentTextNode(this));
142 RefPtr<Text> endText = const_cast<Text*>(latestLogicallyAdjacentTextNode(this));
144 RefPtr<Text> protectedThis(this); // Mutation event handlers could cause our last ref to go away
145 Node* parent = parentNode(); // Protect against mutation handlers moving this node during traversal
146 ExceptionCode ignored = 0;
147 for (RefPtr<Node> n = startText; n && n != this && n->isTextNode() && n->parentNode() == parent;) {
148 RefPtr<Node> nodeToRemove(n.release());
149 n = nodeToRemove->nextSibling();
150 parent->removeChild(nodeToRemove.get(), ignored);
153 if (this != endText) {
154 Node* onePastEndText = endText->nextSibling();
155 for (RefPtr<Node> n = nextSibling(); n && n != onePastEndText && n->isTextNode() && n->parentNode() == parent;) {
156 RefPtr<Node> nodeToRemove(n.release());
157 n = nodeToRemove->nextSibling();
158 parent->removeChild(nodeToRemove.get(), ignored);
162 if (newText.isEmpty()) {
163 if (parent && parentNode() == parent)
164 parent->removeChild(this, ignored);
165 return 0;
168 setData(newText, ignored);
169 return protectedThis.release();
172 String Text::nodeName() const
174 return textAtom.string();
177 Node::NodeType Text::nodeType() const
179 return TEXT_NODE;
182 PassRefPtr<Node> Text::cloneNode(bool /*deep*/)
184 return document()->createTextNode(m_data);
187 bool Text::rendererIsNeeded(RenderStyle *style)
189 if (!CharacterData::rendererIsNeeded(style))
190 return false;
192 bool onlyWS = containsOnlyWhitespace();
193 if (!onlyWS)
194 return true;
196 RenderObject *par = parentNode()->renderer();
198 if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol() || par->isFrameSet())
199 return false;
201 if (style->preserveNewline()) // pre/pre-wrap/pre-line always make renderers.
202 return true;
204 RenderObject *prev = previousRenderer();
205 if (prev && prev->isBR()) // <span><br/> <br/></span>
206 return false;
208 if (par->isInlineFlow()) {
209 // <span><div/> <div/></span>
210 if (prev && !prev->isInline())
211 return false;
212 } else {
213 if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline()))
214 return false;
216 RenderObject *first = par->firstChild();
217 while (first && first->isFloatingOrPositioned())
218 first = first->nextSibling();
219 RenderObject *next = nextRenderer();
220 if (!first || next == first)
221 // Whitespace at the start of a block just goes away. Don't even
222 // make a render object for this text.
223 return false;
226 return true;
229 RenderObject *Text::createRenderer(RenderArena *arena, RenderStyle *style)
231 #if ENABLE(SVG)
232 if (parentNode()->isSVGElement())
233 return new (arena) RenderSVGInlineText(this, m_data);
234 #endif // ENABLE(SVG)
236 return new (arena) RenderText(this, m_data);
239 void Text::attach()
241 createRendererIfNeeded();
242 CharacterData::attach();
245 void Text::recalcStyle(StyleChange change)
247 if (change != NoChange && parentNode()) {
248 if (renderer())
249 renderer()->setStyle(parentNode()->renderer()->style());
251 if (changed()) {
252 if (renderer()) {
253 if (renderer()->isText())
254 static_cast<RenderText*>(renderer())->setText(m_data);
255 } else {
256 if (attached())
257 detach();
258 attach();
261 setChanged(NoStyleChange);
264 // DOM Section 1.1.1
265 bool Text::childTypeAllowed(NodeType)
267 return false;
270 PassRefPtr<Text> Text::createNew(PassRefPtr<StringImpl> string)
272 return new Text(document(), string);
275 PassRefPtr<Text> Text::createWithLengthLimit(Document* doc, const String& text, unsigned& charsLeft, unsigned maxChars)
277 if (charsLeft == text.length() && charsLeft <= maxChars) {
278 charsLeft = 0;
279 return new Text(doc, text);
282 unsigned start = text.length() - charsLeft;
283 unsigned end = start + std::min(charsLeft, maxChars);
285 // check we are not on an unbreakable boundary
286 TextBreakIterator* it = characterBreakIterator(text.characters(), text.length());
287 if (end < text.length() && !isTextBreak(it, end))
288 end = textBreakPreceding(it, end);
290 // maxChars of unbreakable characters could lead to infinite loop
291 if (end <= start)
292 end = text.length();
294 String nodeText = text.substring(start, end - start);
295 charsLeft = text.length() - end;
297 return new Text(doc, nodeText);
300 #ifndef NDEBUG
301 void Text::formatForDebugger(char *buffer, unsigned length) const
303 String result;
304 String s;
306 s = nodeName();
307 if (s.length() > 0) {
308 result += s;
311 s = nodeValue();
312 if (s.length() > 0) {
313 if (result.length() > 0)
314 result += "; ";
315 result += "value=";
316 result += s;
319 strncpy(buffer, result.utf8().data(), length - 1);
321 #endif
323 } // namespace WebCore