• 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 #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