Fix for 3833123, setting a cell's colspan does not update rendering like it should.
[WebKit-https.git] / WebCore / khtml / html / html_tableimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *           (C) 1997 Torben Weis (weis@kde.org)
6  *           (C) 1998 Waldo Bastian (bastian@kde.org)
7  *           (C) 1999 Lars Knoll (knoll@kde.org)
8  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
9  * Copyright (C) 2003 Apple Computer, Inc.
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Library General Public
13  * License as published by the Free Software Foundation; either
14  * version 2 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public License
22  * along with this library; see the file COPYING.LIB.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24  * Boston, MA 02111-1307, USA.
25  */
26
27 #include "html/html_documentimpl.h"
28 #include "html/html_tableimpl.h"
29
30 #include "dom/dom_exception.h"
31 #include "dom/dom_node.h"
32
33 #include "misc/htmlhashes.h"
34 #include "khtmlview.h"
35 #include "khtml_part.h"
36
37 #include "css/cssstyleselector.h"
38 #include "css/cssproperties.h"
39 #include "css/cssvalues.h"
40 #include "css/csshelper.h"
41 #include "css_valueimpl.h"
42 #include "css/css_stylesheetimpl.h"
43
44 #include "rendering/render_table.h"
45
46 #include <kdebug.h>
47 #include <kglobal.h>
48
49 using namespace khtml;
50 using namespace DOM;
51
52 HTMLTableElementImpl::HTMLTableElementImpl(DocumentPtr *doc)
53   : HTMLElementImpl(doc)
54 {
55     tCaption = 0;
56     head = 0;
57     foot = 0;
58     firstBody = 0;
59
60 #if 0
61     rules = None;
62     frame = Void;
63 #endif
64  
65     padding = 1;
66     
67     m_noBorder = true;
68     m_solid = false;
69 }
70
71 HTMLTableElementImpl::~HTMLTableElementImpl()
72 {
73 }
74
75 NodeImpl::Id HTMLTableElementImpl::id() const
76 {
77     return ID_TABLE;
78 }
79
80 NodeImpl* HTMLTableElementImpl::setCaption( HTMLTableCaptionElementImpl *c )
81 {
82     int exceptioncode = 0;
83     NodeImpl* r;
84     if(tCaption) {
85         replaceChild ( c, tCaption, exceptioncode );
86         r = c;
87     }
88     else
89         r = insertBefore( c, firstChild(), exceptioncode );
90     tCaption = c;
91     return r;
92 }
93
94 NodeImpl* HTMLTableElementImpl::setTHead( HTMLTableSectionElementImpl *s )
95 {
96     int exceptioncode = 0;
97     NodeImpl* r;
98     if(head) {
99         replaceChild( s, head, exceptioncode );
100         r = s;
101     }
102     else if( foot )
103         r = insertBefore( s, foot, exceptioncode );
104     else if( firstBody )
105         r = insertBefore( s, firstBody, exceptioncode );
106     else
107         r = appendChild( s, exceptioncode );
108
109     head = s;
110     return r;
111 }
112
113 NodeImpl* HTMLTableElementImpl::setTFoot( HTMLTableSectionElementImpl *s )
114 {
115     int exceptioncode = 0;
116     NodeImpl* r;
117     if(foot) {
118         replaceChild ( s, foot, exceptioncode );
119         r = s;
120     } else if( firstBody )
121         r = insertBefore( s, firstBody, exceptioncode );
122     else
123         r = appendChild( s, exceptioncode );
124     foot = s;
125     return r;
126 }
127
128 NodeImpl* HTMLTableElementImpl::setTBody( HTMLTableSectionElementImpl *s )
129 {
130     int exceptioncode = 0;
131     NodeImpl* r;
132
133     if(firstBody) {
134         replaceChild ( s, firstBody, exceptioncode );
135         r = s;
136     } else
137         r = appendChild( s, exceptioncode );
138     firstBody = s;
139
140     return r;
141 }
142
143 HTMLElementImpl *HTMLTableElementImpl::createTHead(  )
144 {
145     if(!head)
146     {
147         int exceptioncode = 0;
148         head = new HTMLTableSectionElementImpl(docPtr(), ID_THEAD, true /* implicit */);
149         if(foot)
150             insertBefore( head, foot, exceptioncode );
151         else if(firstBody)
152             insertBefore( head, firstBody, exceptioncode);
153         else
154             appendChild(head, exceptioncode);
155     }
156     return head;
157 }
158
159 void HTMLTableElementImpl::deleteTHead(  )
160 {
161     if(head) {
162         int exceptioncode = 0;
163         HTMLElementImpl::removeChild(head, exceptioncode);
164     }
165     head = 0;
166 }
167
168 HTMLElementImpl *HTMLTableElementImpl::createTFoot(  )
169 {
170     if(!foot)
171     {
172         int exceptioncode = 0;
173         foot = new HTMLTableSectionElementImpl(docPtr(), ID_TFOOT, true /*implicit */);
174         if(firstBody)
175             insertBefore( foot, firstBody, exceptioncode );
176         else
177             appendChild(foot, exceptioncode);
178     }
179     return foot;
180 }
181
182 void HTMLTableElementImpl::deleteTFoot(  )
183 {
184     if(foot) {
185         int exceptioncode = 0;
186         HTMLElementImpl::removeChild(foot, exceptioncode);
187     }
188     foot = 0;
189 }
190
191 HTMLElementImpl *HTMLTableElementImpl::createCaption(  )
192 {
193     if(!tCaption)
194     {
195         int exceptioncode = 0;
196         tCaption = new HTMLTableCaptionElementImpl(docPtr());
197         insertBefore( tCaption, firstChild(), exceptioncode );
198     }
199     return tCaption;
200 }
201
202 void HTMLTableElementImpl::deleteCaption(  )
203 {
204     if(tCaption) {
205         int exceptioncode = 0;
206         HTMLElementImpl::removeChild(tCaption, exceptioncode);
207     }
208     tCaption = 0;
209 }
210
211 HTMLElementImpl *HTMLTableElementImpl::insertRow( long index, int &exceptioncode )
212 {
213     // The DOM requires that we create a tbody if the table is empty
214     // (cf DOM2TS HTMLTableElement31 test)
215     // (note: this is different from "if the table has no sections", since we can have
216     // <TABLE><TR>)
217     if(!firstBody && !head && !foot && !hasChildNodes())
218         setTBody( new HTMLTableSectionElementImpl(docPtr(), ID_TBODY, true /* implicit */) );
219
220     //kdDebug(6030) << k_funcinfo << index << endl;
221     // IE treats index=-1 as default value meaning 'append after last'
222     // This isn't in the DOM. So, not implemented yet.
223     HTMLTableSectionElementImpl* section = 0L;
224     HTMLTableSectionElementImpl* lastSection = 0L;
225     NodeImpl *node = firstChild();
226     bool append = (index == -1);
227     bool found = false;
228     for ( ; node && (index>=0 || append) ; node = node->nextSibling() )
229     {
230         // there could be 2 tfoot elements in the table. Only the first one is the "foot", that's why we have the more
231         // complicated if statement below.
232         if ( node != foot && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) )
233         {
234             section = static_cast<HTMLTableSectionElementImpl *>(node);
235             lastSection = section;
236             //kdDebug(6030) << k_funcinfo << "section id=" << node->id() << " rows:" << section->numRows() << endl;
237             if ( !append )
238             {
239                 int rows = section->numRows();
240                 if ( rows > index ) {
241                     found = true;
242                     break;
243                 } else
244                     index -= rows;
245                 //kdDebug(6030) << "       index is now " << index << endl;
246             }
247         }
248     }
249     if ( !found && foot )
250         section = static_cast<HTMLTableSectionElementImpl *>(foot);
251
252     // Index == 0 means "insert before first row in current section"
253     // or "append after last row" (if there's no current section anymore)
254     if ( !section && ( index == 0 || append ) )
255     {
256         section = lastSection;
257         index = section ? section->numRows() : 0;
258     }
259     if ( section && (index >= 0 || append) ) {
260         //kdDebug(6030) << "Inserting row into section " << section << " at index " << index << endl;
261         return section->insertRow( index, exceptioncode );
262     } else {
263         // No more sections => index is too big
264         exceptioncode = DOMException::INDEX_SIZE_ERR;
265         return 0L;
266     }
267 }
268
269 void HTMLTableElementImpl::deleteRow( long index, int &exceptioncode )
270 {
271     HTMLTableSectionElementImpl* section = 0L;
272     NodeImpl *node = firstChild();
273     bool lastRow = index == -1;
274     HTMLTableSectionElementImpl* lastSection = 0L;
275     bool found = false;
276     for ( ; node ; node = node->nextSibling() )
277     {
278         if ( node != foot && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) )
279         {
280             section = static_cast<HTMLTableSectionElementImpl *>(node);
281             lastSection = section;
282             int rows = section->numRows();
283             if ( !lastRow )
284             {
285                 if ( rows > index ) {
286                     found = true;
287                     break;
288                 } else
289                     index -= rows;
290             }
291         }
292         section = 0L;
293     }
294     if ( !found && foot )
295         section = static_cast<HTMLTableSectionElementImpl *>(foot);
296
297     if ( lastRow )
298         lastSection->deleteRow( -1, exceptioncode );
299     else if ( section && index >= 0 && index < section->numRows() )
300         section->deleteRow( index, exceptioncode );
301     else
302         exceptioncode = DOMException::INDEX_SIZE_ERR;
303 }
304
305 NodeImpl *HTMLTableElementImpl::addChild(NodeImpl *child)
306 {
307 #ifdef DEBUG_LAYOUT
308     kdDebug( 6030 ) << nodeName().string() << "(Table)::addChild( " << child->nodeName().string() << " )" << endl;
309 #endif
310
311     if (child->id() == ID_FORM) {
312         // First add the child.
313         HTMLElementImpl::addChild(child);
314         // Now simply return ourselves as the newnode.  This has the effect of
315         // demoting the form to a leaf and moving it safely out of the way.
316         return this;
317     }
318     
319     // We do not want this check-node-allowed test here, however the code to
320     // build up tables relies on childAllowed failing to make sure that the
321     // table is well-formed, the primary work being to add a tbody element.
322     // As 99.9999% of tables on the weeb do not have tbody elements, it seems
323     // odd to traverse an "error" case for the most common table markup.
324     // See <rdar://problem/3719373> Table parsing and construction relies on 
325     // childAllowed failures to build up proper DOM
326     if (child->nodeType() == Node::DOCUMENT_FRAGMENT_NODE) {
327         // child is a DocumentFragment... check all its children instead of child itself
328         for (NodeImpl *c = child->firstChild(); c; c = c->nextSibling())
329             if (!childAllowed(c))
330                 return 0;
331     }
332     else if (!childAllowed(child)) {
333         // child is not a DocumentFragment... check if it's allowed directly
334         return 0;
335     }
336
337     int exceptioncode = 0;
338     NodeImpl *retval = appendChild( child, exceptioncode );
339     if ( retval ) {
340         switch(child->id()) {
341         case ID_CAPTION:
342             if ( !tCaption )
343                 tCaption = static_cast<HTMLTableCaptionElementImpl *>(child);
344             break;
345         case ID_COL:
346         case ID_COLGROUP:
347             break;
348         case ID_THEAD:
349             if ( !head )
350                 head = static_cast<HTMLTableSectionElementImpl *>(child);
351             break;
352         case ID_TFOOT:
353             if ( !foot )
354                 foot = static_cast<HTMLTableSectionElementImpl *>(child);
355             break;
356         case ID_TBODY:
357             if ( !firstBody )
358                 firstBody = static_cast<HTMLTableSectionElementImpl *>(child);
359             break;
360         }
361     }
362     return retval;
363 }
364
365 bool HTMLTableElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
366 {
367     switch(attr) {
368         case ATTR_WIDTH:
369         case ATTR_HEIGHT:
370         case ATTR_BGCOLOR:
371         case ATTR_BACKGROUND:
372         case ATTR_CELLSPACING:
373         case ATTR_VSPACE:
374         case ATTR_HSPACE:
375         case ATTR_VALIGN:
376             result = eUniversal;
377             return false;
378         case ATTR_BORDERCOLOR:
379             result = eUniversal;
380             return true;
381         case ATTR_BORDER:
382             result = eTable;
383             return true;
384         case ATTR_ALIGN:
385             result = eTable;
386             return false;
387         default:
388             break;
389     }
390     return HTMLElementImpl::mapToEntry(attr, result);
391 }
392
393 void HTMLTableElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
394 {
395     switch(attr->id())
396     {
397     case ATTR_WIDTH:
398         addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
399         break;
400     case ATTR_HEIGHT:
401         addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
402         break;
403     case ATTR_BORDER:
404     {
405         m_noBorder = true;
406         if (attr->isNull()) break;
407         if (attr->decl()) {
408             CSSValueImpl* val = attr->decl()->getPropertyCSSValue(CSS_PROP_BORDER_LEFT_WIDTH);
409             if (val) {
410                 val->ref();
411                 if (val->isPrimitiveValue()) {
412                     CSSPrimitiveValueImpl* primVal = static_cast<CSSPrimitiveValueImpl*>(val);
413                     m_noBorder = !primVal->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
414                 }
415                 val->deref();
416             }
417         }
418         else {
419             // ### this needs more work, as the border value is not only
420             //     the border of the box, but also between the cells
421             int border = 0;
422             if (attr->isEmpty())
423                 border = 1;
424             else
425                 border = attr->value().toInt();
426 #ifdef DEBUG_DRAW_BORDER
427             border=1;
428 #endif
429             m_noBorder = !border;
430             DOMString v = QString::number( border );
431             addCSSLength(attr, CSS_PROP_BORDER_WIDTH, v );
432         }
433 #if 0
434         // wanted by HTML4 specs
435         if (m_noBorder)
436             frame = Void, rules = None;
437         else
438             frame = Box, rules = All;
439 #endif
440         break;
441     }
442     case ATTR_BGCOLOR:
443         addHTMLColor(attr, CSS_PROP_BACKGROUND_COLOR, attr->value());
444         break;
445     case ATTR_BORDERCOLOR:
446         m_solid = attr->decl();
447         if (!attr->decl() && !attr->isEmpty()) {
448             addHTMLColor(attr, CSS_PROP_BORDER_COLOR, attr->value());
449             addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
450             addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
451             addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
452             addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
453             m_solid = true;
454         }
455         break;
456     case ATTR_BACKGROUND:
457     {
458         QString url = khtml::parseURL( attr->value() ).string();
459         if (!url.isEmpty())
460             addCSSImageProperty(attr, CSS_PROP_BACKGROUND_IMAGE, getDocument()->completeURL(url));
461         break;
462     }
463     case ATTR_FRAME:
464 #if 0
465         if ( strcasecmp( attr->value(), "void" ) == 0 )
466             frame = Void;
467         else if ( strcasecmp( attr->value(), "border" ) == 0 )
468             frame = Box;
469         else if ( strcasecmp( attr->value(), "box" ) == 0 )
470             frame = Box;
471         else if ( strcasecmp( attr->value(), "hsides" ) == 0 )
472             frame = Hsides;
473         else if ( strcasecmp( attr->value(), "vsides" ) == 0 )
474             frame = Vsides;
475         else if ( strcasecmp( attr->value(), "above" ) == 0 )
476             frame = Above;
477         else if ( strcasecmp( attr->value(), "below" ) == 0 )
478             frame = Below;
479         else if ( strcasecmp( attr->value(), "lhs" ) == 0 )
480             frame = Lhs;
481         else if ( strcasecmp( attr->value(), "rhs" ) == 0 )
482             frame = Rhs;
483 #endif
484         break;
485     case ATTR_RULES:
486 #if 0
487         if ( strcasecmp( attr->value(), "none" ) == 0 )
488             rules = None;
489         else if ( strcasecmp( attr->value(), "groups" ) == 0 )
490             rules = Groups;
491         else if ( strcasecmp( attr->value(), "rows" ) == 0 )
492             rules = Rows;
493         else if ( strcasecmp( attr->value(), "cols" ) == 0 )
494             rules = Cols;
495         else if ( strcasecmp( attr->value(), "all" ) == 0 )
496             rules = All;
497 #endif
498         break;
499    case ATTR_CELLSPACING:
500         if (!attr->value().isEmpty())
501             addCSSLength(attr, CSS_PROP_BORDER_SPACING, attr->value());
502         break;
503     case ATTR_CELLPADDING:
504         if (!attr->value().isEmpty())
505             padding = kMax( 0, attr->value().toInt() );
506         else
507             padding = 1;
508         if (m_render && m_render->isTable()) {
509             static_cast<RenderTable *>(m_render)->setCellPadding(padding);
510             if (!m_render->needsLayout())
511                 m_render->setNeedsLayout(true);
512         }
513         break;
514     case ATTR_COLS:
515     {
516         // ###
517 #if 0
518         int c;
519         c = attr->val()->toInt();
520         addColumns(c-totalCols);
521         break;
522 #endif
523     }
524     case ATTR_VSPACE:
525         addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
526         addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
527         break;
528     case ATTR_HSPACE:
529         addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
530         addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
531         break;
532     case ATTR_ALIGN:
533         if (!attr->value().isEmpty())
534             addCSSProperty(attr, CSS_PROP_FLOAT, attr->value());
535         break;
536     case ATTR_VALIGN:
537         if (!attr->value().isEmpty())
538             addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
539         break;
540     case ATTR_NOSAVE:
541         break;
542     default:
543         HTMLElementImpl::parseHTMLAttribute(attr);
544     }
545 }
546
547 CSSMutableStyleDeclarationImpl* HTMLTableElementImpl::additionalAttributeStyleDecl()
548 {
549     if (m_noBorder)
550         return 0;
551     HTMLAttributeImpl attr(ATTR_TABLEBORDER, m_solid ? "solid" : "outset");
552     CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(ePersistent, &attr);
553     if (!decl) {
554         decl = new CSSMappedAttributeDeclarationImpl(0);
555         decl->setParent(getDocument()->elementSheet());
556         decl->setNode(this);
557         decl->setStrictParsing(false); // Mapped attributes are just always quirky.
558         
559         decl->ref(); // This single ref pins us in the table until the document dies.
560
561         int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_OUTSET;
562         decl->setProperty(CSS_PROP_BORDER_TOP_STYLE, v, false);
563         decl->setProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v, false);
564         decl->setProperty(CSS_PROP_BORDER_LEFT_STYLE, v, false);
565         decl->setProperty(CSS_PROP_BORDER_RIGHT_STYLE, v, false);
566
567         setMappedAttributeDecl(ePersistent, &attr, decl);
568         decl->setParent(0);
569         decl->setNode(0);
570         decl->setMappedState(ePersistent, attr.id(), attr.value());
571     }
572     return decl;
573 }
574
575 CSSMutableStyleDeclarationImpl* HTMLTableElementImpl::getSharedCellDecl()
576 {
577     HTMLAttributeImpl attr(ATTR_CELLBORDER, m_noBorder ? "none" : (m_solid ? "solid" : "inset"));
578     CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(ePersistent, &attr);
579     if (!decl) {
580         decl = new CSSMappedAttributeDeclarationImpl(0);
581         decl->setParent(getDocument()->elementSheet());
582         decl->setNode(this);
583         decl->setStrictParsing(false); // Mapped attributes are just always quirky.
584         
585         decl->ref(); // This single ref pins us in the table until the table dies.
586         
587         if (m_noBorder)
588             decl->setProperty(CSS_PROP_BORDER_WIDTH, "0", false);
589         else {
590             decl->setProperty(CSS_PROP_BORDER_WIDTH, "1px", false);
591             int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_INSET;
592             decl->setProperty(CSS_PROP_BORDER_TOP_STYLE, v, false);
593             decl->setProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v, false);
594             decl->setProperty(CSS_PROP_BORDER_LEFT_STYLE, v, false);
595             decl->setProperty(CSS_PROP_BORDER_RIGHT_STYLE, v, false);
596             decl->setProperty(CSS_PROP_BORDER_COLOR, "inherit", false);
597         }
598
599         setMappedAttributeDecl(ePersistent, &attr, decl);
600         decl->setParent(0);
601         decl->setNode(0);
602         decl->setMappedState(ePersistent, attr.id(), attr.value());
603     }
604     return decl;
605 }
606
607 void HTMLTableElementImpl::attach()
608 {
609     assert(!m_attached);
610     HTMLElementImpl::attach();
611     if ( m_render && m_render->isTable() )
612         static_cast<RenderTable *>(m_render)->setCellPadding( padding );
613 }
614
615 bool HTMLTableElementImpl::isURLAttribute(AttributeImpl *attr) const
616 {
617     return attr->id() == ATTR_BACKGROUND;
618 }
619
620 // --------------------------------------------------------------------------
621
622 bool HTMLTablePartElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
623 {
624     switch(attr) {
625         case ATTR_BGCOLOR:
626         case ATTR_BACKGROUND:
627         case ATTR_BORDERCOLOR:
628         case ATTR_VALIGN:
629         case ATTR_HEIGHT:
630             result = eUniversal;
631             return false;
632         case ATTR_ALIGN:
633             result = eCell; // All table parts will just share in the TD space.
634             return false;
635         default:
636             break;
637     }
638     return HTMLElementImpl::mapToEntry(attr, result);
639 }
640
641 void HTMLTablePartElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
642 {
643     switch(attr->id())
644     {
645     case ATTR_BGCOLOR:
646         addHTMLColor(attr, CSS_PROP_BACKGROUND_COLOR, attr->value() );
647         break;
648     case ATTR_BACKGROUND:
649     {
650         QString url = khtml::parseURL( attr->value() ).string();
651         if (!url.isEmpty())
652             addCSSImageProperty(attr, CSS_PROP_BACKGROUND_IMAGE, getDocument()->completeURL(url));
653         break;
654     }
655     case ATTR_BORDERCOLOR:
656     {
657         if (!attr->value().isEmpty()) {
658             addHTMLColor(attr, CSS_PROP_BORDER_COLOR, attr->value());
659             addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
660             addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
661             addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
662             addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
663         }
664         break;
665     }
666     case ATTR_VALIGN:
667     {
668         if (!attr->value().isEmpty())
669             addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
670         break;
671     }
672     case ATTR_ALIGN:
673     {
674         DOMString v = attr->value();
675         if ( strcasecmp( attr->value(), "middle" ) == 0 || strcasecmp( attr->value(), "center" ) == 0 )
676             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_CENTER);
677         else if (strcasecmp(attr->value(), "absmiddle") == 0)
678             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER);
679         else if (strcasecmp(attr->value(), "left") == 0)
680             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_LEFT);
681         else if (strcasecmp(attr->value(), "right") == 0)
682             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_RIGHT);
683         else
684             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, v);
685         break;
686     }
687     case ATTR_HEIGHT:
688         if (!attr->value().isEmpty())
689             addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
690         break;
691     case ATTR_NOSAVE:
692         break;
693     default:
694         HTMLElementImpl::parseHTMLAttribute(attr);
695     }
696 }
697
698 // -------------------------------------------------------------------------
699
700 HTMLTableSectionElementImpl::HTMLTableSectionElementImpl(DocumentPtr *doc,
701                                                          ushort tagid, bool implicit)
702     : HTMLTablePartElementImpl(doc)
703 {
704     _id = tagid;
705     m_implicit = implicit;
706 }
707
708 HTMLTableSectionElementImpl::~HTMLTableSectionElementImpl()
709 {
710 }
711
712 NodeImpl::Id HTMLTableSectionElementImpl::id() const
713 {
714     return _id;
715 }
716
717 NodeImpl *HTMLTableSectionElementImpl::addChild(NodeImpl *child)
718 {
719 #ifdef DEBUG_LAYOUT
720     kdDebug( 6030 ) << nodeName().string() << "(Tbody)::addChild( " << child->nodeName().string() << " )" << endl;
721 #endif
722
723     if (child->id() == ID_FORM) {
724         // First add the child.
725         HTMLElementImpl::addChild(child);
726         // Now simply return ourselves as the newnode.  This has the effect of
727         // demoting the form to a leaf and moving it safely out of the way.
728         return this;
729     }
730     return HTMLTablePartElementImpl::addChild(child);
731 }
732
733 // these functions are rather slow, since we need to get the row at
734 // the index... but they aren't used during usual HTML parsing anyway
735 HTMLElementImpl *HTMLTableSectionElementImpl::insertRow( long index, int& exceptioncode )
736 {
737     HTMLTableRowElementImpl *r = 0L;
738     NodeListImpl *children = childNodes();
739     int numRows = children ? (int)children->length() : 0;
740     //kdDebug(6030) << k_funcinfo << "index=" << index << " numRows=" << numRows << endl;
741     if ( index < -1 || index > numRows ) {
742         exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM
743     }
744     else
745     {
746         r = new HTMLTableRowElementImpl(docPtr());
747         if ( numRows == index || index == -1 )
748             appendChild(r, exceptioncode);
749         else {
750             NodeImpl *n;
751             if(index < 1)
752                 n = firstChild();
753             else
754                 n = children->item(index);
755             insertBefore(r, n, exceptioncode );
756         }
757     }
758     delete children;
759     return r;
760 }
761
762 void HTMLTableSectionElementImpl::deleteRow( long index, int &exceptioncode )
763 {
764     NodeListImpl *children = childNodes();
765     int numRows = children ? (int)children->length() : 0;
766     if ( index == -1 ) index = numRows - 1;
767     if( index >= 0 && index < numRows )
768         HTMLElementImpl::removeChild(children->item(index), exceptioncode);
769     else
770         exceptioncode = DOMException::INDEX_SIZE_ERR;
771     delete children;
772 }
773
774
775 int HTMLTableSectionElementImpl::numRows() const
776 {
777     int rows = 0;
778     const NodeImpl *n = firstChild();
779     while (n) {
780         if (n->id() == ID_TR)
781             rows++;
782         n = n->nextSibling();
783     }
784
785     return rows;
786 }
787
788 // -------------------------------------------------------------------------
789
790 NodeImpl::Id HTMLTableRowElementImpl::id() const
791 {
792     return ID_TR;
793 }
794
795 NodeImpl *HTMLTableRowElementImpl::addChild(NodeImpl *child)
796 {
797 #ifdef DEBUG_LAYOUT
798     kdDebug( 6030 ) << nodeName().string() << "(Trow)::addChild( " << child->nodeName().string() << " )" << endl;
799 #endif
800
801     if (child->id() == ID_FORM) {
802         // First add the child.
803         HTMLElementImpl::addChild(child);
804         // Now simply return ourselves as the newnode.  This has the effect of
805         // demoting the form to a leaf and moving it safely out of the way.
806         return this;
807     }
808     return HTMLTablePartElementImpl::addChild(child);
809 }
810
811 long HTMLTableRowElementImpl::rowIndex() const
812 {
813     int rIndex = 0;
814
815     NodeImpl *table = parentNode();
816     if ( !table )
817         return -1;
818     table = table->parentNode();
819     if ( !table || table->id() != ID_TABLE )
820         return -1;
821
822     HTMLTableSectionElementImpl *foot = static_cast<HTMLTableElementImpl *>(table)->tFoot();
823     NodeImpl *node = table->firstChild();
824     while ( node ) {
825         if ( node != foot && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) ) {
826             HTMLTableSectionElementImpl* section = static_cast<HTMLTableSectionElementImpl *>(node);
827             const NodeImpl *row = section->firstChild();
828             while ( row ) {
829                 if ( row == this )
830                     return rIndex;
831                 rIndex++;
832                 row = row->nextSibling();
833             }
834         }
835         node = node->nextSibling();
836     }
837     const NodeImpl *row = foot->firstChild();
838     while ( row ) {
839         if ( row == this )
840             return rIndex;
841         rIndex++;
842         row = row->nextSibling();
843     }
844     // should never happen
845     return -1;
846 }
847
848 long HTMLTableRowElementImpl::sectionRowIndex() const
849 {
850     int rIndex = 0;
851     const NodeImpl *n = this;
852     do {
853         n = n->previousSibling();
854         if (n && n->isElementNode() && n->id() == ID_TR)
855             rIndex++;
856     }
857     while (n);
858
859     return rIndex;
860 }
861
862 HTMLElementImpl *HTMLTableRowElementImpl::insertCell( long index, int &exceptioncode )
863 {
864     HTMLTableCellElementImpl *c = 0L;
865     NodeListImpl *children = childNodes();
866     int numCells = children ? children->length() : 0;
867     if ( index < -1 || index > numCells )
868         exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM
869     else
870     {
871         c = new HTMLTableCellElementImpl(docPtr(), ID_TD);
872         if(numCells == index || index == -1)
873             appendChild(c, exceptioncode);
874         else {
875             NodeImpl *n;
876             if(index < 1)
877                 n = firstChild();
878             else
879                 n = children->item(index);
880             insertBefore(c, n, exceptioncode);
881         }
882     }
883     delete children;
884     return c;
885 }
886
887 void HTMLTableRowElementImpl::deleteCell( long index, int &exceptioncode )
888 {
889     NodeListImpl *children = childNodes();
890     int numCells = children ? children->length() : 0;
891     if ( index == -1 ) index = numCells-1;
892     if( index >= 0 && index < numCells )
893         HTMLElementImpl::removeChild(children->item(index), exceptioncode);
894     else
895         exceptioncode = DOMException::INDEX_SIZE_ERR;
896     delete children;
897 }
898
899 // -------------------------------------------------------------------------
900
901 HTMLTableCellElementImpl::HTMLTableCellElementImpl(DocumentPtr *doc, int tag)
902   : HTMLTablePartElementImpl(doc)
903 {
904   _col = -1;
905   _row = -1;
906   cSpan = rSpan = 1;
907   _id = tag;
908   rowHeight = 0;
909   m_solid = false;
910 }
911
912 HTMLTableCellElementImpl::~HTMLTableCellElementImpl()
913 {
914 }
915
916
917 bool HTMLTableCellElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
918 {
919     switch(attr) {
920         case ATTR_NOWRAP:
921             result = eUniversal;
922             return false;
923         case ATTR_WIDTH:
924         case ATTR_HEIGHT:
925             result = eCell; // Because of the quirky behavior of ignoring 0 values, cells are special.
926             return false;
927         default:
928             break;
929     }
930     return HTMLTablePartElementImpl::mapToEntry(attr, result);
931 }
932
933 void HTMLTableCellElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
934 {
935     switch(attr->id())
936     {
937     case ATTR_ROWSPAN:
938         // ###
939         rSpan = !attr->isNull() ? attr->value().toInt() : 1;
940         // limit this to something not causing an overflow with short int
941         if (rSpan < 1 || rSpan > 1024) rSpan = 1;
942         if (renderer() && renderer()->isTableCell())
943             static_cast<RenderTableCell*>(renderer())->updateFromElement();
944         break;
945     case ATTR_COLSPAN:
946         // ###
947         cSpan = !attr->isNull() ? attr->value().toInt() : 1;
948         // limit this to something not causing an overflow with short int
949         if (cSpan < 1 || cSpan > 1024) cSpan = 1;
950         if (renderer() && renderer()->isTableCell())
951             static_cast<RenderTableCell*>(renderer())->updateFromElement();
952         break;
953     case ATTR_NOWRAP:
954         if (!attr->isNull())
955             addCSSProperty(attr, CSS_PROP_WHITE_SPACE, CSS_VAL__KHTML_NOWRAP);
956         break;
957     case ATTR_WIDTH:
958         if (!attr->value().isEmpty()) {
959             int widthInt = attr->value().toInt();
960             if (widthInt > 0) // width="0" is ignored for compatibility with WinIE.
961                 addCSSLength( attr, CSS_PROP_WIDTH, attr->value() );
962         }
963         break;
964     case ATTR_HEIGHT:
965         if (!attr->value().isEmpty()) {
966             int heightInt = attr->value().toInt();
967             if (heightInt > 0) // height="0" is ignored for compatibility with WinIE.
968                 addCSSLength( attr, CSS_PROP_HEIGHT, attr->value() );
969         }
970         break;
971     case ATTR_NOSAVE:
972         break;
973     default:
974         HTMLTablePartElementImpl::parseHTMLAttribute(attr);
975     }
976 }
977
978 // used by table cells to share style decls created by the enclosing table.
979 CSSMutableStyleDeclarationImpl* HTMLTableCellElementImpl::additionalAttributeStyleDecl()
980 {
981     HTMLElementImpl* p = static_cast<HTMLElementImpl*>(parentNode());
982     while(p && p->id() != ID_TABLE)
983         p = static_cast<HTMLElementImpl*>(p->parentNode());
984
985     if (p) {
986         HTMLTableElementImpl* table = static_cast<HTMLTableElementImpl*>(p);
987         return table->getSharedCellDecl();
988     }
989
990     return 0;
991 }
992
993 void HTMLTableCellElementImpl::attach()
994 {
995     HTMLElementImpl* p = static_cast<HTMLElementImpl*>(parentNode());
996     while(p && p->id() != ID_TABLE)
997         p = static_cast<HTMLElementImpl*>(p->parentNode());
998
999     HTMLTablePartElementImpl::attach();
1000 }
1001
1002 bool HTMLTableCellElementImpl::isURLAttribute(AttributeImpl *attr) const
1003 {
1004     return attr->id() == ATTR_BACKGROUND;
1005 }
1006
1007 // -------------------------------------------------------------------------
1008
1009 HTMLTableColElementImpl::HTMLTableColElementImpl(DocumentPtr *doc, ushort i)
1010     : HTMLTablePartElementImpl(doc)
1011 {
1012     _id = i;
1013     _span = (_id == ID_COLGROUP ? 0 : 1);
1014 }
1015
1016 NodeImpl::Id HTMLTableColElementImpl::id() const
1017 {
1018     return _id;
1019 }
1020
1021 bool HTMLTableColElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
1022 {
1023     switch(attr) {
1024         case ATTR_WIDTH:
1025             result = eUniversal;
1026             return false;
1027         default:
1028             break;
1029     }
1030     return HTMLTablePartElementImpl::mapToEntry(attr, result);
1031 }
1032
1033 void HTMLTableColElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
1034 {
1035     switch(attr->id())
1036     {
1037     case ATTR_SPAN:
1038         _span = !attr->isNull() ? attr->value().toInt() : 1;
1039         break;
1040     case ATTR_WIDTH:
1041         if (!attr->value().isEmpty())
1042             addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
1043         break;
1044     default:
1045         HTMLTablePartElementImpl::parseHTMLAttribute(attr);
1046     }
1047
1048 }
1049
1050 // -------------------------------------------------------------------------
1051
1052 NodeImpl::Id HTMLTableCaptionElementImpl::id() const
1053 {
1054     return ID_CAPTION;
1055 }
1056
1057 bool HTMLTableCaptionElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
1058 {
1059     if (attr == ATTR_ALIGN) {
1060         result = eCaption;
1061         return false;
1062     }
1063     return HTMLElementImpl::mapToEntry(attr, result);
1064 }
1065
1066 void HTMLTableCaptionElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
1067 {
1068     switch(attr->id())
1069     {
1070     case ATTR_ALIGN:
1071         if (!attr->value().isEmpty())
1072             addCSSProperty(attr, CSS_PROP_CAPTION_SIDE, attr->value());
1073         break;
1074     default:
1075         HTMLElementImpl::parseHTMLAttribute(attr);
1076     }
1077
1078 }