e9d4a75a97658a402aa0fb4fbe651d015f6e4027
[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_BACKGROUND:
369             result = (MappedAttributeEntry)(eLastEntry + getDocument()->docID());
370             return false;
371         case ATTR_WIDTH:
372         case ATTR_HEIGHT:
373         case ATTR_BGCOLOR:
374         case ATTR_CELLSPACING:
375         case ATTR_VSPACE:
376         case ATTR_HSPACE:
377         case ATTR_VALIGN:
378             result = eUniversal;
379             return false;
380         case ATTR_BORDERCOLOR:
381             result = eUniversal;
382             return true;
383         case ATTR_BORDER:
384             result = eTable;
385             return true;
386         case ATTR_ALIGN:
387             result = eTable;
388             return false;
389         default:
390             break;
391     }
392     return HTMLElementImpl::mapToEntry(attr, result);
393 }
394
395 void HTMLTableElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
396 {
397     switch(attr->id())
398     {
399     case ATTR_WIDTH:
400         addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
401         break;
402     case ATTR_HEIGHT:
403         addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
404         break;
405     case ATTR_BORDER:
406     {
407         m_noBorder = true;
408         if (attr->isNull()) break;
409         if (attr->decl()) {
410             CSSValueImpl* val = attr->decl()->getPropertyCSSValue(CSS_PROP_BORDER_LEFT_WIDTH);
411             if (val) {
412                 val->ref();
413                 if (val->isPrimitiveValue()) {
414                     CSSPrimitiveValueImpl* primVal = static_cast<CSSPrimitiveValueImpl*>(val);
415                     m_noBorder = !primVal->getFloatValue(CSSPrimitiveValue::CSS_NUMBER);
416                 }
417                 val->deref();
418             }
419         }
420         else {
421             // ### this needs more work, as the border value is not only
422             //     the border of the box, but also between the cells
423             int border = 0;
424             if (attr->isEmpty())
425                 border = 1;
426             else
427                 border = attr->value().toInt();
428 #ifdef DEBUG_DRAW_BORDER
429             border=1;
430 #endif
431             m_noBorder = !border;
432             DOMString v = QString::number( border );
433             addCSSLength(attr, CSS_PROP_BORDER_WIDTH, v );
434         }
435 #if 0
436         // wanted by HTML4 specs
437         if (m_noBorder)
438             frame = Void, rules = None;
439         else
440             frame = Box, rules = All;
441 #endif
442         break;
443     }
444     case ATTR_BGCOLOR:
445         addHTMLColor(attr, CSS_PROP_BACKGROUND_COLOR, attr->value());
446         break;
447     case ATTR_BORDERCOLOR:
448         m_solid = attr->decl();
449         if (!attr->decl() && !attr->isEmpty()) {
450             addHTMLColor(attr, CSS_PROP_BORDER_COLOR, attr->value());
451             addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
452             addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
453             addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
454             addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
455             m_solid = true;
456         }
457         break;
458     case ATTR_BACKGROUND:
459     {
460         QString url = khtml::parseURL( attr->value() ).string();
461         if (!url.isEmpty())
462             addCSSImageProperty(attr, CSS_PROP_BACKGROUND_IMAGE, getDocument()->completeURL(url));
463         break;
464     }
465     case ATTR_FRAME:
466 #if 0
467         if ( strcasecmp( attr->value(), "void" ) == 0 )
468             frame = Void;
469         else if ( strcasecmp( attr->value(), "border" ) == 0 )
470             frame = Box;
471         else if ( strcasecmp( attr->value(), "box" ) == 0 )
472             frame = Box;
473         else if ( strcasecmp( attr->value(), "hsides" ) == 0 )
474             frame = Hsides;
475         else if ( strcasecmp( attr->value(), "vsides" ) == 0 )
476             frame = Vsides;
477         else if ( strcasecmp( attr->value(), "above" ) == 0 )
478             frame = Above;
479         else if ( strcasecmp( attr->value(), "below" ) == 0 )
480             frame = Below;
481         else if ( strcasecmp( attr->value(), "lhs" ) == 0 )
482             frame = Lhs;
483         else if ( strcasecmp( attr->value(), "rhs" ) == 0 )
484             frame = Rhs;
485 #endif
486         break;
487     case ATTR_RULES:
488 #if 0
489         if ( strcasecmp( attr->value(), "none" ) == 0 )
490             rules = None;
491         else if ( strcasecmp( attr->value(), "groups" ) == 0 )
492             rules = Groups;
493         else if ( strcasecmp( attr->value(), "rows" ) == 0 )
494             rules = Rows;
495         else if ( strcasecmp( attr->value(), "cols" ) == 0 )
496             rules = Cols;
497         else if ( strcasecmp( attr->value(), "all" ) == 0 )
498             rules = All;
499 #endif
500         break;
501    case ATTR_CELLSPACING:
502         if (!attr->value().isEmpty())
503             addCSSLength(attr, CSS_PROP_BORDER_SPACING, attr->value());
504         break;
505     case ATTR_CELLPADDING:
506         if (!attr->value().isEmpty())
507             padding = kMax( 0, attr->value().toInt() );
508         else
509             padding = 1;
510         if (m_render && m_render->isTable()) {
511             static_cast<RenderTable *>(m_render)->setCellPadding(padding);
512             if (!m_render->needsLayout())
513                 m_render->setNeedsLayout(true);
514         }
515         break;
516     case ATTR_COLS:
517     {
518         // ###
519 #if 0
520         int c;
521         c = attr->val()->toInt();
522         addColumns(c-totalCols);
523         break;
524 #endif
525     }
526     case ATTR_VSPACE:
527         addCSSLength(attr, CSS_PROP_MARGIN_TOP, attr->value());
528         addCSSLength(attr, CSS_PROP_MARGIN_BOTTOM, attr->value());
529         break;
530     case ATTR_HSPACE:
531         addCSSLength(attr, CSS_PROP_MARGIN_LEFT, attr->value());
532         addCSSLength(attr, CSS_PROP_MARGIN_RIGHT, attr->value());
533         break;
534     case ATTR_ALIGN:
535         if (!attr->value().isEmpty())
536             addCSSProperty(attr, CSS_PROP_FLOAT, attr->value());
537         break;
538     case ATTR_VALIGN:
539         if (!attr->value().isEmpty())
540             addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
541         break;
542     case ATTR_NOSAVE:
543         break;
544     default:
545         HTMLElementImpl::parseHTMLAttribute(attr);
546     }
547 }
548
549 CSSMutableStyleDeclarationImpl* HTMLTableElementImpl::additionalAttributeStyleDecl()
550 {
551     if (m_noBorder)
552         return 0;
553     HTMLAttributeImpl attr(ATTR_TABLEBORDER, m_solid ? "solid" : "outset");
554     CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(ePersistent, &attr);
555     if (!decl) {
556         decl = new CSSMappedAttributeDeclarationImpl(0);
557         decl->setParent(getDocument()->elementSheet());
558         decl->setNode(this);
559         decl->setStrictParsing(false); // Mapped attributes are just always quirky.
560         
561         decl->ref(); // This single ref pins us in the table until the document dies.
562
563         int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_OUTSET;
564         decl->setProperty(CSS_PROP_BORDER_TOP_STYLE, v, false);
565         decl->setProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v, false);
566         decl->setProperty(CSS_PROP_BORDER_LEFT_STYLE, v, false);
567         decl->setProperty(CSS_PROP_BORDER_RIGHT_STYLE, v, false);
568
569         setMappedAttributeDecl(ePersistent, &attr, decl);
570         decl->setParent(0);
571         decl->setNode(0);
572         decl->setMappedState(ePersistent, attr.id(), attr.value());
573     }
574     return decl;
575 }
576
577 CSSMutableStyleDeclarationImpl* HTMLTableElementImpl::getSharedCellDecl()
578 {
579     HTMLAttributeImpl attr(ATTR_CELLBORDER, m_noBorder ? "none" : (m_solid ? "solid" : "inset"));
580     CSSMappedAttributeDeclarationImpl* decl = getMappedAttributeDecl(ePersistent, &attr);
581     if (!decl) {
582         decl = new CSSMappedAttributeDeclarationImpl(0);
583         decl->setParent(getDocument()->elementSheet());
584         decl->setNode(this);
585         decl->setStrictParsing(false); // Mapped attributes are just always quirky.
586         
587         decl->ref(); // This single ref pins us in the table until the table dies.
588         
589         if (m_noBorder)
590             decl->setProperty(CSS_PROP_BORDER_WIDTH, "0", false);
591         else {
592             decl->setProperty(CSS_PROP_BORDER_WIDTH, "1px", false);
593             int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_INSET;
594             decl->setProperty(CSS_PROP_BORDER_TOP_STYLE, v, false);
595             decl->setProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v, false);
596             decl->setProperty(CSS_PROP_BORDER_LEFT_STYLE, v, false);
597             decl->setProperty(CSS_PROP_BORDER_RIGHT_STYLE, v, false);
598             decl->setProperty(CSS_PROP_BORDER_COLOR, "inherit", false);
599         }
600
601         setMappedAttributeDecl(ePersistent, &attr, decl);
602         decl->setParent(0);
603         decl->setNode(0);
604         decl->setMappedState(ePersistent, attr.id(), attr.value());
605     }
606     return decl;
607 }
608
609 void HTMLTableElementImpl::attach()
610 {
611     assert(!m_attached);
612     HTMLElementImpl::attach();
613     if ( m_render && m_render->isTable() )
614         static_cast<RenderTable *>(m_render)->setCellPadding( padding );
615 }
616
617 bool HTMLTableElementImpl::isURLAttribute(AttributeImpl *attr) const
618 {
619     return attr->id() == ATTR_BACKGROUND;
620 }
621
622 // --------------------------------------------------------------------------
623
624 bool HTMLTablePartElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
625 {
626     switch(attr) {
627         case ATTR_BACKGROUND:
628             result = (MappedAttributeEntry)(eLastEntry + getDocument()->docID());
629             return false;
630         case ATTR_BGCOLOR:
631         case ATTR_BORDERCOLOR:
632         case ATTR_VALIGN:
633         case ATTR_HEIGHT:
634             result = eUniversal;
635             return false;
636         case ATTR_ALIGN:
637             result = eCell; // All table parts will just share in the TD space.
638             return false;
639         default:
640             break;
641     }
642     return HTMLElementImpl::mapToEntry(attr, result);
643 }
644
645 void HTMLTablePartElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
646 {
647     switch(attr->id())
648     {
649     case ATTR_BGCOLOR:
650         addHTMLColor(attr, CSS_PROP_BACKGROUND_COLOR, attr->value() );
651         break;
652     case ATTR_BACKGROUND:
653     {
654         QString url = khtml::parseURL( attr->value() ).string();
655         if (!url.isEmpty())
656             addCSSImageProperty(attr, CSS_PROP_BACKGROUND_IMAGE, getDocument()->completeURL(url));
657         break;
658     }
659     case ATTR_BORDERCOLOR:
660     {
661         if (!attr->value().isEmpty()) {
662             addHTMLColor(attr, CSS_PROP_BORDER_COLOR, attr->value());
663             addCSSProperty(attr, CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
664             addCSSProperty(attr, CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
665             addCSSProperty(attr, CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
666             addCSSProperty(attr, CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
667         }
668         break;
669     }
670     case ATTR_VALIGN:
671     {
672         if (!attr->value().isEmpty())
673             addCSSProperty(attr, CSS_PROP_VERTICAL_ALIGN, attr->value());
674         break;
675     }
676     case ATTR_ALIGN:
677     {
678         DOMString v = attr->value();
679         if ( strcasecmp( attr->value(), "middle" ) == 0 || strcasecmp( attr->value(), "center" ) == 0 )
680             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_CENTER);
681         else if (strcasecmp(attr->value(), "absmiddle") == 0)
682             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER);
683         else if (strcasecmp(attr->value(), "left") == 0)
684             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_LEFT);
685         else if (strcasecmp(attr->value(), "right") == 0)
686             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_RIGHT);
687         else
688             addCSSProperty(attr, CSS_PROP_TEXT_ALIGN, v);
689         break;
690     }
691     case ATTR_HEIGHT:
692         if (!attr->value().isEmpty())
693             addCSSLength(attr, CSS_PROP_HEIGHT, attr->value());
694         break;
695     case ATTR_NOSAVE:
696         break;
697     default:
698         HTMLElementImpl::parseHTMLAttribute(attr);
699     }
700 }
701
702 // -------------------------------------------------------------------------
703
704 HTMLTableSectionElementImpl::HTMLTableSectionElementImpl(DocumentPtr *doc,
705                                                          ushort tagid, bool implicit)
706     : HTMLTablePartElementImpl(doc)
707 {
708     _id = tagid;
709     m_implicit = implicit;
710 }
711
712 HTMLTableSectionElementImpl::~HTMLTableSectionElementImpl()
713 {
714 }
715
716 NodeImpl::Id HTMLTableSectionElementImpl::id() const
717 {
718     return _id;
719 }
720
721 NodeImpl *HTMLTableSectionElementImpl::addChild(NodeImpl *child)
722 {
723 #ifdef DEBUG_LAYOUT
724     kdDebug( 6030 ) << nodeName().string() << "(Tbody)::addChild( " << child->nodeName().string() << " )" << endl;
725 #endif
726
727     if (child->id() == ID_FORM) {
728         // First add the child.
729         HTMLElementImpl::addChild(child);
730         // Now simply return ourselves as the newnode.  This has the effect of
731         // demoting the form to a leaf and moving it safely out of the way.
732         return this;
733     }
734     return HTMLTablePartElementImpl::addChild(child);
735 }
736
737 // these functions are rather slow, since we need to get the row at
738 // the index... but they aren't used during usual HTML parsing anyway
739 HTMLElementImpl *HTMLTableSectionElementImpl::insertRow( long index, int& exceptioncode )
740 {
741     HTMLTableRowElementImpl *r = 0L;
742     NodeListImpl *children = childNodes();
743     int numRows = children ? (int)children->length() : 0;
744     //kdDebug(6030) << k_funcinfo << "index=" << index << " numRows=" << numRows << endl;
745     if ( index < -1 || index > numRows ) {
746         exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM
747     }
748     else
749     {
750         r = new HTMLTableRowElementImpl(docPtr());
751         if ( numRows == index || index == -1 )
752             appendChild(r, exceptioncode);
753         else {
754             NodeImpl *n;
755             if(index < 1)
756                 n = firstChild();
757             else
758                 n = children->item(index);
759             insertBefore(r, n, exceptioncode );
760         }
761     }
762     delete children;
763     return r;
764 }
765
766 void HTMLTableSectionElementImpl::deleteRow( long index, int &exceptioncode )
767 {
768     NodeListImpl *children = childNodes();
769     int numRows = children ? (int)children->length() : 0;
770     if ( index == -1 ) index = numRows - 1;
771     if( index >= 0 && index < numRows )
772         HTMLElementImpl::removeChild(children->item(index), exceptioncode);
773     else
774         exceptioncode = DOMException::INDEX_SIZE_ERR;
775     delete children;
776 }
777
778
779 int HTMLTableSectionElementImpl::numRows() const
780 {
781     int rows = 0;
782     const NodeImpl *n = firstChild();
783     while (n) {
784         if (n->id() == ID_TR)
785             rows++;
786         n = n->nextSibling();
787     }
788
789     return rows;
790 }
791
792 // -------------------------------------------------------------------------
793
794 NodeImpl::Id HTMLTableRowElementImpl::id() const
795 {
796     return ID_TR;
797 }
798
799 NodeImpl *HTMLTableRowElementImpl::addChild(NodeImpl *child)
800 {
801 #ifdef DEBUG_LAYOUT
802     kdDebug( 6030 ) << nodeName().string() << "(Trow)::addChild( " << child->nodeName().string() << " )" << endl;
803 #endif
804
805     if (child->id() == ID_FORM) {
806         // First add the child.
807         HTMLElementImpl::addChild(child);
808         // Now simply return ourselves as the newnode.  This has the effect of
809         // demoting the form to a leaf and moving it safely out of the way.
810         return this;
811     }
812     return HTMLTablePartElementImpl::addChild(child);
813 }
814
815 long HTMLTableRowElementImpl::rowIndex() const
816 {
817     int rIndex = 0;
818
819     NodeImpl *table = parentNode();
820     if ( !table )
821         return -1;
822     table = table->parentNode();
823     if ( !table || table->id() != ID_TABLE )
824         return -1;
825
826     HTMLTableSectionElementImpl *foot = static_cast<HTMLTableElementImpl *>(table)->tFoot();
827     NodeImpl *node = table->firstChild();
828     while ( node ) {
829         if ( node != foot && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) ) {
830             HTMLTableSectionElementImpl* section = static_cast<HTMLTableSectionElementImpl *>(node);
831             const NodeImpl *row = section->firstChild();
832             while ( row ) {
833                 if ( row == this )
834                     return rIndex;
835                 rIndex++;
836                 row = row->nextSibling();
837             }
838         }
839         node = node->nextSibling();
840     }
841     const NodeImpl *row = foot->firstChild();
842     while ( row ) {
843         if ( row == this )
844             return rIndex;
845         rIndex++;
846         row = row->nextSibling();
847     }
848     // should never happen
849     return -1;
850 }
851
852 long HTMLTableRowElementImpl::sectionRowIndex() const
853 {
854     int rIndex = 0;
855     const NodeImpl *n = this;
856     do {
857         n = n->previousSibling();
858         if (n && n->isElementNode() && n->id() == ID_TR)
859             rIndex++;
860     }
861     while (n);
862
863     return rIndex;
864 }
865
866 HTMLElementImpl *HTMLTableRowElementImpl::insertCell( long index, int &exceptioncode )
867 {
868     HTMLTableCellElementImpl *c = 0L;
869     NodeListImpl *children = childNodes();
870     int numCells = children ? children->length() : 0;
871     if ( index < -1 || index > numCells )
872         exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM
873     else
874     {
875         c = new HTMLTableCellElementImpl(docPtr(), ID_TD);
876         if(numCells == index || index == -1)
877             appendChild(c, exceptioncode);
878         else {
879             NodeImpl *n;
880             if(index < 1)
881                 n = firstChild();
882             else
883                 n = children->item(index);
884             insertBefore(c, n, exceptioncode);
885         }
886     }
887     delete children;
888     return c;
889 }
890
891 void HTMLTableRowElementImpl::deleteCell( long index, int &exceptioncode )
892 {
893     NodeListImpl *children = childNodes();
894     int numCells = children ? children->length() : 0;
895     if ( index == -1 ) index = numCells-1;
896     if( index >= 0 && index < numCells )
897         HTMLElementImpl::removeChild(children->item(index), exceptioncode);
898     else
899         exceptioncode = DOMException::INDEX_SIZE_ERR;
900     delete children;
901 }
902
903 // -------------------------------------------------------------------------
904
905 HTMLTableCellElementImpl::HTMLTableCellElementImpl(DocumentPtr *doc, int tag)
906   : HTMLTablePartElementImpl(doc)
907 {
908   _col = -1;
909   _row = -1;
910   cSpan = rSpan = 1;
911   _id = tag;
912   rowHeight = 0;
913   m_solid = false;
914 }
915
916 HTMLTableCellElementImpl::~HTMLTableCellElementImpl()
917 {
918 }
919
920
921 bool HTMLTableCellElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
922 {
923     switch(attr) {
924         case ATTR_NOWRAP:
925             result = eUniversal;
926             return false;
927         case ATTR_WIDTH:
928         case ATTR_HEIGHT:
929             result = eCell; // Because of the quirky behavior of ignoring 0 values, cells are special.
930             return false;
931         default:
932             break;
933     }
934     return HTMLTablePartElementImpl::mapToEntry(attr, result);
935 }
936
937 void HTMLTableCellElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
938 {
939     switch(attr->id())
940     {
941     case ATTR_ROWSPAN:
942         rSpan = !attr->isNull() ? attr->value().toInt() : 1;
943         if (rSpan < 1) rSpan = 1;
944         if (renderer() && renderer()->isTableCell())
945             static_cast<RenderTableCell*>(renderer())->updateFromElement();
946         break;
947     case ATTR_COLSPAN:
948         cSpan = !attr->isNull() ? attr->value().toInt() : 1;
949         if (cSpan < 1) 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         if (renderer() && renderer()->isTableCol())
1040             static_cast<RenderTableCol*>(renderer())->updateFromElement();
1041         break;
1042     case ATTR_WIDTH:
1043         if (!attr->value().isEmpty())
1044             addCSSLength(attr, CSS_PROP_WIDTH, attr->value());
1045         break;
1046     default:
1047         HTMLTablePartElementImpl::parseHTMLAttribute(attr);
1048     }
1049
1050 }
1051
1052 // -------------------------------------------------------------------------
1053
1054 NodeImpl::Id HTMLTableCaptionElementImpl::id() const
1055 {
1056     return ID_CAPTION;
1057 }
1058
1059 bool HTMLTableCaptionElementImpl::mapToEntry(NodeImpl::Id attr, MappedAttributeEntry& result) const
1060 {
1061     if (attr == ATTR_ALIGN) {
1062         result = eCaption;
1063         return false;
1064     }
1065     return HTMLElementImpl::mapToEntry(attr, result);
1066 }
1067
1068 void HTMLTableCaptionElementImpl::parseHTMLAttribute(HTMLAttributeImpl *attr)
1069 {
1070     switch(attr->id())
1071     {
1072     case ATTR_ALIGN:
1073         if (!attr->value().isEmpty())
1074             addCSSProperty(attr, CSS_PROP_CAPTION_SIDE, attr->value());
1075         break;
1076     default:
1077         HTMLElementImpl::parseHTMLAttribute(attr);
1078     }
1079
1080 }