Fix crash when text is contained inside a table-colgroup.
[WebKit-https.git] / WebCore / khtml / xml / dom_textimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
6  * Copyright (C) 2003 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include "dom/dom_exception.h"
25 #include "css/cssstyleselector.h"
26 #include "xml/dom2_eventsimpl.h"
27 #include "xml/dom_textimpl.h"
28 #include "xml/dom_docimpl.h"
29
30 #include "misc/htmlhashes.h"
31 #include "rendering/render_text.h"
32
33 #include <kdebug.h>
34
35 using namespace DOM;
36 using namespace khtml;
37
38
39 CharacterDataImpl::CharacterDataImpl(DocumentPtr *doc)
40     : NodeImpl(doc)
41 {
42     str = 0;
43 }
44
45 CharacterDataImpl::CharacterDataImpl(DocumentPtr *doc, const DOMString &_text)
46     : NodeImpl(doc)
47 {
48     str = _text.impl ? _text.impl : new DOMStringImpl((QChar*)0, 0);
49     str->ref();
50 }
51
52 CharacterDataImpl::~CharacterDataImpl()
53 {
54     if(str) str->deref();
55 }
56
57 DOMString CharacterDataImpl::data() const
58 {
59     return str;
60 }
61
62 void CharacterDataImpl::setData( const DOMString &_data, int &exceptioncode )
63 {
64     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly
65     if (isReadOnly()) {
66         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
67         return;
68     }
69
70     if(str == _data.impl) return; // ### fire DOMCharacterDataModified if modified?
71     DOMStringImpl *oldStr = str;
72     str = _data.impl;
73     if(str) str->ref();
74     if (m_render)
75       (static_cast<RenderText*>(m_render))->setText(str);
76     
77     dispatchModifiedEvent(oldStr);
78     if(oldStr) oldStr->deref();
79     
80     getDocument()->removeAllMarkers(this);
81 }
82
83 unsigned long CharacterDataImpl::length() const
84 {
85     return str->l;
86 }
87
88 DOMString CharacterDataImpl::substringData( const unsigned long offset, const unsigned long count, int &exceptioncode )
89 {
90     exceptioncode = 0;
91     checkCharDataOperation(offset, exceptioncode);
92     if (exceptioncode)
93         return DOMString();
94
95     return str->substring(offset,count);
96 }
97
98 void CharacterDataImpl::appendData( const DOMString &arg, int &exceptioncode )
99 {
100     exceptioncode = 0;
101
102     // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
103     if (isReadOnly()) {
104         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
105         return;
106     }
107
108     DOMStringImpl *oldStr = str;
109     str = str->copy();
110     str->ref();
111     str->append(arg.impl);
112     if (m_render)
113       (static_cast<RenderText*>(m_render))->setTextWithOffset(str, oldStr->l, 0);
114     
115     dispatchModifiedEvent(oldStr);
116     oldStr->deref();
117 }
118
119 void CharacterDataImpl::insertData( const unsigned long offset, const DOMString &arg, int &exceptioncode )
120 {
121     exceptioncode = 0;
122     checkCharDataOperation(offset, exceptioncode);
123     if (exceptioncode)
124         return;
125
126     DOMStringImpl *oldStr = str;
127     str = str->copy();
128     str->ref();
129     str->insert(arg.impl, offset);
130     if (m_render)
131       (static_cast<RenderText*>(m_render))->setTextWithOffset(str, offset, 0);
132     
133     dispatchModifiedEvent(oldStr);
134     oldStr->deref();
135     
136     // update the markers for spell checking and grammar checking
137     uint length = arg.length();
138     getDocument()->shiftMarkers(this, offset, length);
139 }
140
141 void CharacterDataImpl::deleteData( const unsigned long offset, const unsigned long count, int &exceptioncode )
142 {
143     exceptioncode = 0;
144     checkCharDataOperation(offset, exceptioncode);
145     if (exceptioncode)
146         return;
147
148     DOMStringImpl *oldStr = str;
149     str = str->copy();
150     str->ref();
151     str->remove(offset,count);
152     if (m_render)
153       (static_cast<RenderText*>(m_render))->setTextWithOffset(str, offset, count);
154     
155     dispatchModifiedEvent(oldStr);
156     oldStr->deref();
157
158     // update the markers for spell checking and grammar checking
159     getDocument()->removeAllMarkers(this, offset, count);
160     getDocument()->shiftMarkers(this, offset + count, -count);
161 }
162
163 void CharacterDataImpl::replaceData( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode )
164 {
165     exceptioncode = 0;
166     checkCharDataOperation(offset, exceptioncode);
167     if (exceptioncode)
168         return;
169
170     unsigned long realCount;
171     if (offset + count > str->l)
172         realCount = str->l-offset;
173     else
174         realCount = count;
175
176     DOMStringImpl *oldStr = str;
177     str = str->copy();
178     str->ref();
179     str->remove(offset,realCount);
180     str->insert(arg.impl, offset);
181     if (m_render)
182       (static_cast<RenderText*>(m_render))->setTextWithOffset(str, offset, count);
183     
184     dispatchModifiedEvent(oldStr);
185     oldStr->deref();
186     
187     // update the markers for spell checking and grammar checking
188     int diff = arg.length() - count;
189     getDocument()->removeAllMarkers(this, offset, count);
190     getDocument()->shiftMarkers(this, offset + count, diff);
191 }
192
193 DOMString CharacterDataImpl::nodeValue() const
194 {
195     return str;
196 }
197
198 bool CharacterDataImpl::containsOnlyWhitespace(unsigned int from, unsigned int len) const
199 {
200     if (str)
201         return str->containsOnlyWhitespace(from, len);
202     return true;
203 }
204
205 bool CharacterDataImpl::containsOnlyWhitespace() const
206 {
207     if (str)
208         return str->containsOnlyWhitespace();
209     return true;
210 }
211
212 void CharacterDataImpl::setNodeValue( const DOMString &_nodeValue, int &exceptioncode )
213 {
214     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
215     setData(_nodeValue, exceptioncode);
216 }
217
218 void CharacterDataImpl::dispatchModifiedEvent(DOMStringImpl *prevValue)
219 {
220     if (parentNode())
221         parentNode()->childrenChanged();
222     if (!getDocument()->hasListenerType(DocumentImpl::DOMCHARACTERDATAMODIFIED_LISTENER))
223         return;
224
225     DOMStringImpl *newValue = str->copy();
226     newValue->ref();
227     int exceptioncode = 0;
228     dispatchEvent(new MutationEventImpl(EventImpl::DOMCHARACTERDATAMODIFIED_EVENT,
229                   true,false,0,prevValue,newValue,DOMString(),0),exceptioncode);
230     newValue->deref();
231     dispatchSubtreeModifiedEvent();
232 }
233
234 void CharacterDataImpl::checkCharDataOperation( const unsigned long offset, int &exceptioncode )
235 {
236     exceptioncode = 0;
237
238     // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than the number of 16-bit
239     // units in data.
240     if (offset > str->l) {
241         exceptioncode = DOMException::INDEX_SIZE_ERR;
242         return;
243     }
244
245     // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly
246     if (isReadOnly()) {
247         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
248         return;
249     }
250 }
251
252 long CharacterDataImpl::maxOffset() const 
253 {
254     return (long)length();
255 }
256
257 long CharacterDataImpl::caretMinOffset() const 
258 {
259     RenderText *r = static_cast<RenderText *>(renderer());
260     return r && r->isText() ? r->caretMinOffset() : 0;
261 }
262
263 long CharacterDataImpl::caretMaxOffset() const 
264 {
265     RenderText *r = static_cast<RenderText *>(renderer());
266     return r && r->isText() ? r->caretMaxOffset() : (long)length();
267 }
268
269 unsigned long CharacterDataImpl::caretMaxRenderedOffset() const 
270 {
271     RenderText *r = static_cast<RenderText *>(renderer());
272     return r ? r->caretMaxRenderedOffset() : length();
273 }
274
275 #ifndef NDEBUG
276 void CharacterDataImpl::dump(QTextStream *stream, QString ind) const
277 {
278     *stream << " str=\"" << DOMString(str).string().ascii() << "\"";
279
280     NodeImpl::dump(stream,ind);
281 }
282 #endif
283
284 // ---------------------------------------------------------------------------
285
286 CommentImpl::CommentImpl(DocumentPtr *doc, const DOMString &_text)
287     : CharacterDataImpl(doc, _text)
288 {
289 }
290
291 CommentImpl::CommentImpl(DocumentPtr *doc)
292     : CharacterDataImpl(doc)
293 {
294 }
295
296 CommentImpl::~CommentImpl()
297 {
298 }
299
300 DOMString CommentImpl::nodeName() const
301 {
302     return "#comment";
303 }
304
305 unsigned short CommentImpl::nodeType() const
306 {
307     return Node::COMMENT_NODE;
308 }
309
310 NodeImpl *CommentImpl::cloneNode(bool /*deep*/)
311 {
312     return getDocument()->createComment( str );
313 }
314
315 NodeImpl::Id CommentImpl::id() const
316 {
317     return ID_COMMENT;
318 }
319
320 // DOM Section 1.1.1
321 bool CommentImpl::childTypeAllowed( unsigned short /*type*/ )
322 {
323     return false;
324 }
325
326 DOMString CommentImpl::toString() const
327 {
328     // FIXME: substitute entity references as needed!
329     return DOMString("<!--") + nodeValue() + "-->";
330 }
331
332 // ---------------------------------------------------------------------------
333
334 // ### allow having children in text nodes for entities, comments etc.
335
336 TextImpl::TextImpl(DocumentPtr *doc, const DOMString &_text)
337     : CharacterDataImpl(doc, _text)
338 {
339 }
340
341 TextImpl::TextImpl(DocumentPtr *doc)
342     : CharacterDataImpl(doc)
343 {
344 }
345
346 TextImpl::~TextImpl()
347 {
348 }
349
350 TextImpl *TextImpl::splitText( const unsigned long offset, int &exceptioncode )
351 {
352     exceptioncode = 0;
353
354     // INDEX_SIZE_ERR: Raised if the specified offset is negative or greater than
355     // the number of 16-bit units in data.
356
357     // ### we explicitly check for a negative long that has been cast to an unsigned long
358     // ... this can happen if JS code passes in -1 - we need to catch this earlier! (in the
359     // kjs bindings)
360     if (offset > str->l || (long)offset < 0) {
361         exceptioncode = DOMException::INDEX_SIZE_ERR;
362         return 0;
363     }
364
365     // NO_MODIFICATION_ALLOWED_ERR: Raised if this node is readonly.
366     if (isReadOnly()) {
367         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
368         return 0;
369     }
370
371     DOMStringImpl *oldStr = str;
372     TextImpl *newText = createNew(str->substring(offset,str->l-offset));
373     str = str->copy();
374     str->ref();
375     str->remove(offset,str->l-offset);
376
377     dispatchModifiedEvent(oldStr);
378     oldStr->deref();
379
380     if (parentNode())
381         parentNode()->insertBefore(newText,nextSibling(), exceptioncode );
382     if ( exceptioncode )
383         return 0;
384
385     if (m_render)
386         (static_cast<RenderText*>(m_render))->setText(str);
387     return newText;
388 }
389
390 DOMString TextImpl::nodeName() const
391 {
392   return "#text";
393 }
394
395 unsigned short TextImpl::nodeType() const
396 {
397     return Node::TEXT_NODE;
398 }
399
400 NodeImpl *TextImpl::cloneNode(bool /*deep*/)
401 {
402     return getDocument()->createTextNode(str);
403 }
404
405 bool TextImpl::rendererIsNeeded(RenderStyle *style)
406 {
407     if (!CharacterDataImpl::rendererIsNeeded(style)) {
408         return false;
409     }
410     bool onlyWS = containsOnlyWhitespace();
411     if (!onlyWS) {
412         return true;
413     }
414
415     RenderObject *par = parentNode()->renderer();
416     
417     if (par->isTable() || par->isTableRow() || par->isTableSection() || par->isTableCol()) {
418         return false;
419     }
420     
421     if (style->whiteSpace() == PRE) {
422         return true;
423     }
424     
425     if (par->isInline()) {
426         // <span><div/> <div/></span>
427         RenderObject *prev = previousRenderer();
428         if (prev && prev->isRenderBlock()) {
429             return false;
430         }
431     } else {
432         RenderObject *prev = previousRenderer();
433         if (par->isRenderBlock() && !par->childrenInline() && (!prev || !prev->isInline())) {
434             return false;
435         }
436         
437         RenderObject *first = par->firstChild();
438         RenderObject *next = nextRenderer();
439         if (!first || next == first) {
440             // Whitespace at the start of a block just goes away.  Don't even
441             // make a render object for this text.
442             return false;
443         }
444     }
445     
446     return true;
447 }
448
449 RenderObject *TextImpl::createRenderer(RenderArena *arena, RenderStyle *style)
450 {
451     return new (arena) RenderText(this, str);
452 }
453
454 void TextImpl::attach()
455 {
456     createRendererIfNeeded();
457     CharacterDataImpl::attach();
458 }
459
460 NodeImpl::Id TextImpl::id() const
461 {
462     return ID_TEXT;
463 }
464
465 void TextImpl::recalcStyle( StyleChange change )
466 {
467 //      qDebug("textImpl::recalcStyle");
468     if (change != NoChange && parentNode()) {
469 //      qDebug("DomText::recalcStyle");
470         if(m_render)
471             m_render->setStyle(parentNode()->renderer()->style());
472     }
473     if ( changed() && m_render && m_render->isText() )
474         static_cast<RenderText*>(m_render)->setText(str);
475     setChanged( false );
476 }
477
478 // DOM Section 1.1.1
479 bool TextImpl::childTypeAllowed( unsigned short /*type*/ )
480 {
481     return false;
482 }
483
484 TextImpl *TextImpl::createNew(DOMStringImpl *_str)
485 {
486     return new TextImpl(docPtr(),_str);
487 }
488
489 DOMString TextImpl::toString() const
490 {
491     // FIXME: substitute entity references as needed!
492     return nodeValue();
493 }
494
495 #ifndef NDEBUG
496 void TextImpl::formatForDebugger(char *buffer, unsigned length) const
497 {
498     DOMString result;
499     DOMString s;
500     
501     s = nodeName();
502     if (s.length() > 0) {
503         result += s;
504     }
505           
506     s = nodeValue();
507     if (s.length() > 0) {
508         if (result.length() > 0)
509             result += "; ";
510         result += "value=";
511         result += s;
512     }
513           
514     strncpy(buffer, result.string().latin1(), length - 1);
515 }
516 #endif
517
518 // ---------------------------------------------------------------------------
519
520 CDATASectionImpl::CDATASectionImpl(DocumentPtr *impl, const DOMString &_text) : TextImpl(impl,_text)
521 {
522 }
523
524 CDATASectionImpl::CDATASectionImpl(DocumentPtr *impl) : TextImpl(impl)
525 {
526 }
527
528 CDATASectionImpl::~CDATASectionImpl()
529 {
530 }
531
532 DOMString CDATASectionImpl::nodeName() const
533 {
534   return "#cdata-section";
535 }
536
537 unsigned short CDATASectionImpl::nodeType() const
538 {
539     return Node::CDATA_SECTION_NODE;
540 }
541
542 NodeImpl *CDATASectionImpl::cloneNode(bool /*deep*/)
543 {
544     return getDocument()->createCDATASection(str);
545 }
546
547 // DOM Section 1.1.1
548 bool CDATASectionImpl::childTypeAllowed( unsigned short /*type*/ )
549 {
550     return false;
551 }
552
553 TextImpl *CDATASectionImpl::createNew(DOMStringImpl *_str)
554 {
555     return new CDATASectionImpl(docPtr(),_str);
556 }
557
558 DOMString CDATASectionImpl::toString() const
559 {
560     // FIXME: substitute entity references as needed!
561     return DOMString("<![CDATA[") + nodeValue() + "]]>";
562 }
563
564 // ---------------------------------------------------------------------------
565
566 EditingTextImpl::EditingTextImpl(DocumentPtr *impl, const DOMString &text)
567     : TextImpl(impl, text)
568 {
569 }
570
571 EditingTextImpl::EditingTextImpl(DocumentPtr *impl)
572     : TextImpl(impl)
573 {
574 }
575
576 EditingTextImpl::~EditingTextImpl()
577 {
578 }
579
580 bool EditingTextImpl::rendererIsNeeded(RenderStyle *style)
581 {
582     return true;
583 }
584