File indexing completed on 2024-05-05 16:10:15

0001 /**
0002  * This file is part of the DOM implementation for KDE.
0003  *
0004  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
0005  *           (C) 1997 Torben Weis (weis@kde.org)
0006  *           (C) 1998 Waldo Bastian (bastian@kde.org)
0007  *           (C) 1999-2003 Lars Knoll (knoll@kde.org)
0008  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
0009  *           (C) 2003 Apple Computer, Inc.
0010  *           (C) 2006 Maksim Orlovich (maksim@kde.org)
0011  *
0012  * This library is free software; you can redistribute it and/or
0013  * modify it under the terms of the GNU Library General Public
0014  * License as published by the Free Software Foundation; either
0015  * version 2 of the License, or (at your option) any later version.
0016  *
0017  * This library is distributed in the hope that it will be useful,
0018  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0019  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0020  * Library General Public License for more details.
0021  *
0022  * You should have received a copy of the GNU Library General Public License
0023  * along with this library; see the file COPYING.LIB.  If not, write to
0024  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0025  * Boston, MA 02110-1301, USA.
0026  */
0027 
0028 #include "html_tableimpl.h"
0029 
0030 #include "html_documentimpl.h"
0031 
0032 #include <dom/dom_exception.h>
0033 #include <dom/dom_node.h>
0034 
0035 #include <khtmlview.h>
0036 #include <khtml_part.h>
0037 
0038 #include <css/cssstyleselector.h>
0039 #include <css/cssproperties.h>
0040 #include <css/cssvalues.h>
0041 
0042 #include <rendering/render_table.h>
0043 
0044 #include "khtml_debug.h"
0045 
0046 using namespace khtml;
0047 using namespace DOM;
0048 
0049 HTMLTableElementImpl::HTMLTableElementImpl(DocumentImpl *doc)
0050     : HTMLElementImpl(doc)
0051 {
0052     rules = None;
0053     frame = Void;
0054 
0055     padding = 1;
0056 
0057     m_solid = false;
0058 }
0059 
0060 HTMLTableElementImpl::~HTMLTableElementImpl()
0061 {
0062 }
0063 
0064 NodeImpl::Id HTMLTableElementImpl::id() const
0065 {
0066     return ID_TABLE;
0067 }
0068 
0069 NodeImpl *HTMLTableElementImpl::setCaption(HTMLTableCaptionElementImpl *c)
0070 {
0071     int exceptioncode = 0;
0072     NodeImpl *r;
0073     if (ElementImpl *cap = caption()) {
0074         replaceChild(c, cap, exceptioncode);
0075         r = c;
0076     } else {
0077         r = insertBefore(c, firstChild(), exceptioncode);
0078     }
0079     tCaption = c;
0080     return r;
0081 }
0082 
0083 NodeImpl *HTMLTableElementImpl::setTHead(HTMLTableSectionElementImpl *s)
0084 {
0085     int exceptioncode = 0;
0086     NodeImpl *r;
0087     if (ElementImpl *head = tHead()) {
0088         replaceChild(s, head, exceptioncode);
0089         r = s;
0090     } else if (ElementImpl *foot = tFoot()) {
0091         r = insertBefore(s, foot, exceptioncode);
0092     } else if (ElementImpl *firstBody = tFirstBody()) {
0093         r = insertBefore(s, firstBody, exceptioncode);
0094     } else {
0095         r = appendChild(s, exceptioncode);
0096     }
0097 
0098     head = s;
0099     return r;
0100 }
0101 
0102 NodeImpl *HTMLTableElementImpl::setTFoot(HTMLTableSectionElementImpl *s)
0103 {
0104     int exceptioncode = 0;
0105     NodeImpl *r;
0106     if (ElementImpl *foot = tFoot()) {
0107         replaceChild(s, foot, exceptioncode);
0108         r = s;
0109     } else if (ElementImpl *firstBody = tFirstBody()) {
0110         r = insertBefore(s, firstBody, exceptioncode);
0111     } else {
0112         r = appendChild(s, exceptioncode);
0113     }
0114     foot = s;
0115     return r;
0116 }
0117 
0118 NodeImpl *HTMLTableElementImpl::setTBody(HTMLTableSectionElementImpl *s)
0119 {
0120     int exceptioncode = 0;
0121     NodeImpl *r;
0122 
0123     if (ElementImpl *firstBody = tFirstBody()) {
0124         replaceChild(s, firstBody, exceptioncode);
0125         r = s;
0126     } else {
0127         r = appendChild(s, exceptioncode);
0128     }
0129     firstBody = s;
0130 
0131     return r;
0132 }
0133 
0134 HTMLElementImpl *HTMLTableElementImpl::createTHead()
0135 {
0136     if (!tHead()) {
0137         int exceptioncode = 0;
0138         ElementImpl *head = new HTMLTableSectionElementImpl(docPtr(), ID_THEAD, true /* implicit */);
0139         if (ElementImpl *foot = tFoot()) {
0140             insertBefore(head, foot, exceptioncode);
0141         } else if (ElementImpl *firstBody = tFirstBody()) {
0142             insertBefore(head, firstBody, exceptioncode);
0143         } else {
0144             appendChild(head, exceptioncode);
0145         }
0146     }
0147     return tHead();
0148 }
0149 
0150 void HTMLTableElementImpl::deleteTHead()
0151 {
0152     if (ElementImpl *head = tHead()) {
0153         int exceptioncode = 0;
0154         removeChild(head, exceptioncode);
0155     }
0156 }
0157 
0158 HTMLElementImpl *HTMLTableElementImpl::createTFoot()
0159 {
0160     if (!tFoot()) {
0161         int exceptioncode = 0;
0162         ElementImpl *foot = new HTMLTableSectionElementImpl(docPtr(), ID_TFOOT, true /*implicit */);
0163         if (ElementImpl *firstBody = tFirstBody()) {
0164             insertBefore(foot, firstBody, exceptioncode);
0165         } else {
0166             appendChild(foot, exceptioncode);
0167         }
0168     }
0169     return tFoot();
0170 }
0171 
0172 void HTMLTableElementImpl::deleteTFoot()
0173 {
0174     if (ElementImpl *foot = tFoot()) {
0175         int exceptioncode = 0;
0176         removeChild(foot, exceptioncode);
0177     }
0178 }
0179 
0180 HTMLElementImpl *HTMLTableElementImpl::createCaption()
0181 {
0182     if (!caption()) {
0183         int exceptioncode = 0;
0184         ElementImpl *tCaption = new HTMLTableCaptionElementImpl(docPtr());
0185         insertBefore(tCaption, firstChild(), exceptioncode);
0186     }
0187     return caption();
0188 }
0189 
0190 void HTMLTableElementImpl::deleteCaption()
0191 {
0192     if (ElementImpl *tCaption = caption()) {
0193         int exceptioncode = 0;
0194         removeChild(tCaption, exceptioncode);
0195     }
0196 }
0197 
0198 /**
0199  Helper. This checks whether the section contains the desired index, and if so,
0200  returns the section. Otherwise, it adjust the index, and returns 0.
0201  indices < 0 are considered to be infinite.
0202 
0203  lastSection is adjusted to reflect the parameter passed in.
0204 */
0205 static inline HTMLTableSectionElementImpl *processSection(HTMLTableSectionElementImpl *section,
0206         HTMLTableSectionElementImpl *&lastSection, long &index)
0207 {
0208     lastSection = section;
0209     if (index < 0) { //append/last mode
0210         return nullptr;
0211     }
0212 
0213     long rows   = section->numRows();
0214     if (index >= rows) {
0215         section =  nullptr;
0216         index   -= rows;
0217     }
0218     return section;
0219 }
0220 
0221 bool HTMLTableElementImpl::findRowSection(long index,
0222         HTMLTableSectionElementImpl *&outSection,
0223         long                         &outIndex) const
0224 {
0225     HTMLTableSectionElementImpl *foot = tFoot();
0226     HTMLTableSectionElementImpl *head = tHead();
0227 
0228     HTMLTableSectionElementImpl *section = nullptr;
0229     HTMLTableSectionElementImpl *lastSection = nullptr;
0230 
0231     if (head) {
0232         section = processSection(head, lastSection, index);
0233     }
0234 
0235     if (!section) {
0236         for (NodeImpl *node = firstChild(); node; node = node->nextSibling()) {
0237             if ((node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY) &&
0238                     node != foot && node != head) {
0239                 section = processSection(static_cast<HTMLTableSectionElementImpl *>(node),
0240                                          lastSection, index);
0241                 if (section) {
0242                     break;
0243                 }
0244             }
0245         }
0246     }
0247 
0248     if (!section && foot) {
0249         section = processSection(foot, lastSection, index);
0250     }
0251 
0252     outIndex = index;
0253     if (section) {
0254         outSection = section;
0255         return true;
0256     } else {
0257         outSection = lastSection;
0258         return false;
0259     }
0260 }
0261 
0262 HTMLElementImpl *HTMLTableElementImpl::insertRow(long index, int &exceptioncode)
0263 {
0264     // The DOM requires that we create a tbody if the table is empty
0265     // (cf DOM2TS HTMLTableElement31 test). This includes even the cases where
0266     // there are <tr>'s immediately under the table, as they're essentially
0267     // ignored by these functions.
0268     HTMLTableSectionElementImpl *foot = tFoot();
0269     HTMLTableSectionElementImpl *head = tHead();
0270     if (!tFirstBody() && !foot && !head) {
0271         setTBody(new HTMLTableSectionElementImpl(docPtr(), ID_TBODY, true /* implicit */));
0272     }
0273 
0274     //qCDebug(KHTML_LOG) << index;
0275 
0276     long sectionIndex;
0277     HTMLTableSectionElementImpl *section;
0278     if (findRowSection(index, section, sectionIndex)) {
0279         return section->insertRow(sectionIndex, exceptioncode);
0280     } else if (index == -1 || sectionIndex == 0) {
0281         return section->insertRow(section->numRows(), exceptioncode);
0282     }
0283 
0284     // The index is too big.
0285     exceptioncode = DOMException::INDEX_SIZE_ERR;
0286     return nullptr;
0287 }
0288 
0289 void HTMLTableElementImpl::deleteRow(long index, int &exceptioncode)
0290 {
0291     long sectionIndex;
0292     HTMLTableSectionElementImpl *section;
0293     if (findRowSection(index, section, sectionIndex)) {
0294         section->deleteRow(sectionIndex, exceptioncode);
0295     } else if (section && index == -1) {
0296         section->deleteRow(-1, exceptioncode);
0297     } else {
0298         exceptioncode = DOMException::INDEX_SIZE_ERR;
0299     }
0300 }
0301 
0302 NodeImpl *HTMLTableElementImpl::appendChild(NodeImpl *child, int &exceptioncode)
0303 {
0304     NodeImpl *retval = HTMLElementImpl::appendChild(child, exceptioncode);
0305     if (retval) {
0306         handleChildAppend(child);
0307     }
0308     return retval;
0309 }
0310 
0311 void HTMLTableElementImpl::handleChildAdd(NodeImpl *child)
0312 {
0313     if (!child) {
0314         return;
0315     }
0316     switch (child->id()) {
0317     case ID_CAPTION:
0318         tCaption.childAdded(this, child);
0319         break;
0320     case ID_THEAD:
0321         head.childAdded(this, child);
0322         break;
0323     case ID_TFOOT:
0324         foot.childAdded(this, child);
0325         break;
0326     case ID_TBODY:
0327         firstBody.childAdded(this, child);
0328         break;
0329     }
0330 }
0331 
0332 void HTMLTableElementImpl::handleChildAppend(NodeImpl *child)
0333 {
0334     if (!child) {
0335         return;
0336     }
0337     switch (child->id()) {
0338     case ID_CAPTION:
0339         tCaption.childAppended(child);
0340         break;
0341     case ID_THEAD:
0342         head.childAppended(child);
0343         break;
0344     case ID_TFOOT:
0345         foot.childAppended(child);
0346         break;
0347     case ID_TBODY:
0348         firstBody.childAppended(child);
0349         break;
0350     }
0351 }
0352 
0353 void HTMLTableElementImpl::handleChildRemove(NodeImpl *child)
0354 {
0355     if (!child) {
0356         return;
0357     }
0358     switch (child->id()) {
0359     case ID_CAPTION:
0360         tCaption.childRemoved(this, child);
0361         break;
0362     case ID_THEAD:
0363         head.childRemoved(this, child);
0364         break;
0365     case ID_TFOOT:
0366         foot.childRemoved(this, child);
0367         break;
0368     case ID_TBODY:
0369         firstBody.childRemoved(this, child);
0370         break;
0371     }
0372 }
0373 
0374 NodeImpl *HTMLTableElementImpl::addChild(NodeImpl *child)
0375 {
0376 #ifdef DEBUG_LAYOUT
0377     // qCDebug(KHTML_LOG) << nodeName().string() << "(Table)::addChild( " << child->nodeName().string() << " )";
0378 #endif
0379 
0380     NodeImpl *retval = HTMLElementImpl::addChild(child);
0381     if (retval) {
0382         handleChildAppend(child);
0383     }
0384 
0385     return retval;
0386 }
0387 
0388 NodeImpl *HTMLTableElementImpl::insertBefore(NodeImpl *newChild, NodeImpl *refChild, int &exceptioncode)
0389 {
0390     NodeImpl *retval = HTMLElementImpl::insertBefore(newChild, refChild, exceptioncode);
0391     if (retval) {
0392         handleChildAdd(newChild);
0393     }
0394 
0395     return retval;
0396 }
0397 
0398 void HTMLTableElementImpl::replaceChild(NodeImpl *newChild, NodeImpl *oldChild, int &exceptioncode)
0399 {
0400     handleChildRemove(oldChild);   //Always safe.
0401     HTMLElementImpl::replaceChild(newChild, oldChild, exceptioncode);
0402     if (!exceptioncode) {
0403         handleChildAdd(newChild);
0404     }
0405 }
0406 
0407 void HTMLTableElementImpl::removeChild(NodeImpl *oldChild, int &exceptioncode)
0408 {
0409     handleChildRemove(oldChild);
0410     HTMLElementImpl::removeChild(oldChild, exceptioncode);
0411 }
0412 
0413 void HTMLTableElementImpl::removeChildren()
0414 {
0415     HTMLElementImpl::removeChildren();
0416     tCaption.clear();
0417     head.clear();
0418     foot.clear();
0419     firstBody.clear();
0420 }
0421 
0422 static inline bool isTableCellAncestor(NodeImpl *n)
0423 {
0424     return n->id() == ID_THEAD || n->id() == ID_TBODY ||
0425            n->id() == ID_TFOOT || n->id() == ID_TR;
0426 }
0427 
0428 static bool setTableCellsChanged(NodeImpl *n)
0429 {
0430     assert(n);
0431     bool cellChanged = false;
0432 
0433     if (n->id() == ID_TD || n->id() == ID_TH) {
0434         cellChanged = true;
0435     } else if (isTableCellAncestor(n)) {
0436         for (NodeImpl *child = n->firstChild(); child; child = child->nextSibling()) {
0437             cellChanged |= setTableCellsChanged(child);
0438         }
0439     }
0440 
0441     if (cellChanged) {
0442         n->setChanged();
0443     }
0444 
0445     return cellChanged;
0446 }
0447 
0448 void HTMLTableElementImpl::parseAttribute(AttributeImpl *attr)
0449 {
0450     // ### to CSS!!
0451     switch (attr->id()) {
0452     case ATTR_WIDTH:
0453         if (!attr->value().isEmpty()) {
0454             addCSSLength(CSS_PROP_WIDTH, attr->value());
0455         } else {
0456             removeCSSProperty(CSS_PROP_WIDTH);
0457         }
0458         break;
0459     case ATTR_HEIGHT:
0460         if (!attr->value().isEmpty()) {
0461             addCSSLength(CSS_PROP_HEIGHT, attr->value());
0462         } else {
0463             removeCSSProperty(CSS_PROP_HEIGHT);
0464         }
0465         break;
0466     case ATTR_BORDER: {
0467         int border;
0468         bool ok = true;
0469         // ### this needs more work, as the border value is not only
0470         //     the border of the box, but also between the cells
0471         if (!attr->val()) {
0472             border = 0;
0473         } else if (attr->val()->l == 0) {
0474             border = 1;
0475         } else {
0476             border = attr->val()->toInt(&ok);
0477         }
0478         if (!ok) {
0479             border = 1;
0480         }
0481 #ifdef DEBUG_DRAW_BORDER
0482         border = 1;
0483 #endif
0484         DOMString v = QString::number(border);
0485         addCSSLength(CSS_PROP_BORDER_WIDTH, v);
0486 
0487         attr->rewriteValue(v);
0488 
0489         // wanted by HTML4 specs
0490         if (!border) {
0491             frame = Void, rules = None;
0492         } else {
0493             frame = Box, rules = All;
0494         }
0495 
0496         if (attached()) {
0497             updateFrame();
0498             if (tFirstBody()) {
0499                 setTableCellsChanged(tFirstBody());
0500             }
0501         }
0502         break;
0503     }
0504     case ATTR_BGCOLOR:
0505         if (!attr->value().isEmpty()) {
0506             addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value());
0507         } else {
0508             removeCSSProperty(CSS_PROP_BACKGROUND_COLOR);
0509         }
0510         break;
0511     case ATTR_BORDERCOLOR:
0512         if (!attr->value().isEmpty()) {
0513             addHTMLColor(CSS_PROP_BORDER_COLOR, attr->value());
0514             m_solid = true;
0515         }
0516 
0517         if (attached()) {
0518             updateFrame();
0519         }
0520         break;
0521     case ATTR_BACKGROUND: {
0522         QString url = attr->value().trimSpaces().string();
0523         if (!url.isEmpty()) {
0524             url = document()->completeURL(url);
0525             addCSSProperty(CSS_PROP_BACKGROUND_IMAGE, DOMString("url('" + url + "')"));
0526         } else {
0527             removeCSSProperty(CSS_PROP_BACKGROUND_IMAGE);
0528         }
0529         break;
0530     }
0531     case ATTR_FRAME:
0532 
0533         if (strcasecmp(attr->value(), "void") == 0) {
0534             frame = Void;
0535         } else if (strcasecmp(attr->value(), "border") == 0) {
0536             frame = Box;
0537         } else if (strcasecmp(attr->value(), "box") == 0) {
0538             frame = Box;
0539         } else if (strcasecmp(attr->value(), "hsides") == 0) {
0540             frame = Hsides;
0541         } else if (strcasecmp(attr->value(), "vsides") == 0) {
0542             frame = Vsides;
0543         } else if (strcasecmp(attr->value(), "above") == 0) {
0544             frame = Above;
0545         } else if (strcasecmp(attr->value(), "below") == 0) {
0546             frame = Below;
0547         } else if (strcasecmp(attr->value(), "lhs") == 0) {
0548             frame = Lhs;
0549         } else if (strcasecmp(attr->value(), "rhs") == 0) {
0550             frame = Rhs;
0551         }
0552 
0553         if (attached()) {
0554             updateFrame();
0555         }
0556         break;
0557     case ATTR_RULES:
0558         if (strcasecmp(attr->value(), "none") == 0) {
0559             rules = None;
0560         } else if (strcasecmp(attr->value(), "groups") == 0) {
0561             rules = Groups;
0562         } else if (strcasecmp(attr->value(), "rows") == 0) {
0563             rules = Rows;
0564         } else if (strcasecmp(attr->value(), "cols") == 0) {
0565             rules = Cols;
0566         } else if (strcasecmp(attr->value(), "all") == 0) {
0567             rules = All;
0568         }
0569 
0570         if (attached() && tFirstBody())
0571             if (setTableCellsChanged(tFirstBody())) {
0572                 setChanged();
0573             }
0574         break;
0575     case ATTR_CELLSPACING:
0576         if (!attr->value().isEmpty()) {
0577             addCSSLength(CSS_PROP_BORDER_SPACING, attr->value(), true);
0578         } else {
0579             removeCSSProperty(CSS_PROP_BORDER_SPACING);
0580         }
0581         break;
0582     case ATTR_CELLPADDING:
0583         if (!attr->value().isEmpty()) {
0584             padding = qMax(0, attr->value().toInt());
0585         } else {
0586             padding = 1;
0587         }
0588         if (m_render && m_render->isTable()) {
0589             static_cast<RenderTable *>(m_render)->setCellPadding(padding);
0590             if (!m_render->needsLayout()) {
0591                 m_render->setNeedsLayout(true);
0592             }
0593         }
0594         break;
0595     case ATTR_COLS: {
0596         // ###
0597 #if 0
0598         int c;
0599         c = attr->val()->toInt();
0600         addColumns(c - totalCols);
0601 #endif
0602         break;
0603 
0604     }
0605     case ATTR_ALIGN:
0606         setChanged();
0607         break;
0608     case ATTR_VALIGN:
0609         if (!attr->value().isEmpty()) {
0610             addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower());
0611         } else {
0612             removeCSSProperty(CSS_PROP_VERTICAL_ALIGN);
0613         }
0614         break;
0615     case ATTR_NOSAVE:
0616         break;
0617     default:
0618         HTMLElementImpl::parseAttribute(attr);
0619     }
0620 }
0621 
0622 void HTMLTableElementImpl::attach()
0623 {
0624     updateFrame();
0625     HTMLElementImpl::attach();
0626     if (m_render && m_render->isTable()) {
0627         static_cast<RenderTable *>(m_render)->setCellPadding(padding);
0628     }
0629 }
0630 
0631 void HTMLTableElementImpl::close()
0632 {
0633     ElementImpl *firstBody = tFirstBody();
0634     if (firstBody && !firstBody->closed()) {
0635         firstBody->close();
0636     }
0637     HTMLElementImpl::close();
0638 }
0639 
0640 void HTMLTableElementImpl::updateFrame()
0641 {
0642     int v = m_solid ? CSS_VAL_SOLID : CSS_VAL_OUTSET;
0643     if (frame & Above) {
0644         addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, v);
0645     } else {
0646         addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_NONE);
0647     }
0648     if (frame & Below) {
0649         addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, v);
0650     } else {
0651         addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_NONE);
0652     }
0653     if (frame & Lhs) {
0654         addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, v);
0655     } else {
0656         addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_NONE);
0657     }
0658     if (frame & Rhs) {
0659         addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, v);
0660     } else {
0661         addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_NONE);
0662     }
0663 }
0664 
0665 // --------------------------------------------------------------------------
0666 
0667 void HTMLTablePartElementImpl::parseAttribute(AttributeImpl *attr)
0668 {
0669     switch (attr->id()) {
0670     case ATTR_BGCOLOR:
0671         if (attr->val()) {
0672             addHTMLColor(CSS_PROP_BACKGROUND_COLOR, attr->value());
0673         } else {
0674             removeCSSProperty(CSS_PROP_BACKGROUND_COLOR);
0675         }
0676         break;
0677     case ATTR_BACKGROUND: {
0678         QString url = attr->value().trimSpaces().string();
0679         if (!url.isEmpty()) {
0680             url = document()->completeURL(url);
0681             addCSSProperty(CSS_PROP_BACKGROUND_IMAGE,  DOMString("url('" + url + "')"));
0682         } else {
0683             removeCSSProperty(CSS_PROP_BACKGROUND_IMAGE);
0684         }
0685         break;
0686     }
0687     case ATTR_BORDERCOLOR: {
0688         if (!attr->value().isEmpty()) {
0689             addHTMLColor(CSS_PROP_BORDER_COLOR, attr->value());
0690             addCSSProperty(CSS_PROP_BORDER_TOP_STYLE, CSS_VAL_SOLID);
0691             addCSSProperty(CSS_PROP_BORDER_BOTTOM_STYLE, CSS_VAL_SOLID);
0692             addCSSProperty(CSS_PROP_BORDER_LEFT_STYLE, CSS_VAL_SOLID);
0693             addCSSProperty(CSS_PROP_BORDER_RIGHT_STYLE, CSS_VAL_SOLID);
0694         }
0695         break;
0696     }
0697     case ATTR_ALIGN: {
0698         DOMString v = attr->value();
0699         if (strcasecmp(attr->value(), "middle") == 0 || strcasecmp(attr->value(), "center") == 0) {
0700             addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_CENTER);
0701         } else if (strcasecmp(attr->value(), "absmiddle") == 0) {
0702             addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL_CENTER);
0703         } else if (strcasecmp(attr->value(), "left") == 0) {
0704             addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_LEFT);
0705         } else if (strcasecmp(attr->value(), "right") == 0) {
0706             addCSSProperty(CSS_PROP_TEXT_ALIGN, CSS_VAL__KHTML_RIGHT);
0707         } else {
0708             addCSSProperty(CSS_PROP_TEXT_ALIGN, v);
0709         }
0710         break;
0711     }
0712     case ATTR_VALIGN: {
0713         if (!attr->value().isEmpty()) {
0714             addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower());
0715         } else {
0716             removeCSSProperty(CSS_PROP_VERTICAL_ALIGN);
0717         }
0718         break;
0719     }
0720     case ATTR_HEIGHT:
0721         if (!attr->value().isEmpty()) {
0722             addCSSLength(CSS_PROP_HEIGHT, attr->value());
0723         } else {
0724             removeCSSProperty(CSS_PROP_HEIGHT);
0725         }
0726         break;
0727     case ATTR_NOSAVE:
0728         break;
0729     default:
0730         HTMLElementImpl::parseAttribute(attr);
0731     }
0732 }
0733 
0734 // -------------------------------------------------------------------------
0735 
0736 HTMLTableSectionElementImpl::HTMLTableSectionElementImpl(DocumentImpl *doc,
0737         ushort tagid, bool implicit)
0738     : HTMLTablePartElementImpl(doc)
0739 {
0740     _id = tagid;
0741     m_implicit = implicit;
0742 }
0743 
0744 HTMLTableSectionElementImpl::~HTMLTableSectionElementImpl()
0745 {
0746 }
0747 
0748 NodeImpl::Id HTMLTableSectionElementImpl::id() const
0749 {
0750     return _id;
0751 }
0752 
0753 // these functions are rather slow, since we need to get the row at
0754 // the index... but they aren't used during usual HTML parsing anyway
0755 HTMLElementImpl *HTMLTableSectionElementImpl::insertRow(long index, int &exceptioncode)
0756 {
0757     HTMLTableRowElementImpl *r = nullptr;
0758     HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl *>(this), HTMLCollectionImpl::TSECTION_ROWS);
0759     int numRows = rows.length();
0760     //qCDebug(KHTML_LOG) << "index=" << index << " numRows=" << numRows;
0761     if (index < -1 || index > numRows) {
0762         exceptioncode = DOMException::INDEX_SIZE_ERR; // per the DOM
0763     } else {
0764         r = new HTMLTableRowElementImpl(docPtr());
0765         if (numRows == index || index == -1) {
0766             appendChild(r, exceptioncode);
0767         } else {
0768             NodeImpl *n;
0769             if (index < 1) {
0770                 n = firstChild();
0771             } else {
0772                 n = rows.item(index);
0773             }
0774             insertBefore(r, n, exceptioncode);
0775         }
0776     }
0777     return r;
0778 }
0779 
0780 void HTMLTableSectionElementImpl::deleteRow(long index, int &exceptioncode)
0781 {
0782     HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl *>(this), HTMLCollectionImpl::TSECTION_ROWS);
0783     int numRows = rows.length();
0784     if (index == -1) {
0785         index = numRows - 1;
0786     }
0787     if (index >= 0 && index < numRows) {
0788         HTMLElementImpl::removeChild(rows.item(index), exceptioncode);
0789     } else {
0790         exceptioncode = DOMException::INDEX_SIZE_ERR;
0791     }
0792 }
0793 
0794 int HTMLTableSectionElementImpl::numRows() const
0795 {
0796     HTMLCollectionImpl rows(const_cast<HTMLTableSectionElementImpl *>(this), HTMLCollectionImpl::TSECTION_ROWS);
0797     return rows.length();
0798 }
0799 
0800 // -------------------------------------------------------------------------
0801 
0802 NodeImpl::Id HTMLTableRowElementImpl::id() const
0803 {
0804     return ID_TR;
0805 }
0806 
0807 long HTMLTableRowElementImpl::rowIndex() const
0808 {
0809     int rIndex = 0;
0810 
0811     NodeImpl *table = parentNode();
0812     if (!table) {
0813         return -1;
0814     }
0815     table = table->parentNode();
0816     if (!table || table->id() != ID_TABLE) {
0817         return -1;
0818     }
0819 
0820     HTMLTableSectionElementImpl *head = static_cast<HTMLTableElementImpl *>(table)->tHead();
0821     HTMLTableSectionElementImpl *foot = static_cast<HTMLTableElementImpl *>(table)->tFoot();
0822 
0823     if (head) {
0824         const NodeImpl *row = head->firstChild();
0825         while (row) {
0826             if (row == this) {
0827                 return rIndex;
0828             }
0829             if (row->id() == ID_TR) {
0830                 rIndex++;
0831             }
0832             row = row->nextSibling();
0833         }
0834     }
0835 
0836     NodeImpl *node = table->firstChild();
0837     while (node) {
0838         if (node != foot && node != head && (node->id() == ID_THEAD || node->id() == ID_TFOOT || node->id() == ID_TBODY)) {
0839             HTMLTableSectionElementImpl *section = static_cast<HTMLTableSectionElementImpl *>(node);
0840             const NodeImpl *row = section->firstChild();
0841             while (row) {
0842                 if (row == this) {
0843                     return rIndex;
0844                 }
0845                 if (row->id() == ID_TR) {
0846                     rIndex++;
0847                 }
0848                 row = row->nextSibling();
0849             }
0850         }
0851         node = node->nextSibling();
0852     }
0853     const NodeImpl *row = foot->firstChild();
0854     while (row) {
0855         if (row == this) {
0856             return rIndex;
0857         }
0858         if (row->id() == ID_TR) {
0859             rIndex++;
0860         }
0861         row = row->nextSibling();
0862     }
0863     // should never happen
0864     return -1;
0865 }
0866 
0867 long HTMLTableRowElementImpl::sectionRowIndex() const
0868 {
0869     int rIndex = 0;
0870     const NodeImpl *n = this;
0871     do {
0872         n = n->previousSibling();
0873         if (n && n->id() == ID_TR) {
0874             rIndex++;
0875         }
0876     } while (n);
0877 
0878     return rIndex;
0879 }
0880 
0881 HTMLElementImpl *HTMLTableRowElementImpl::insertCell(long index, int &exceptioncode)
0882 {
0883     HTMLTableCellElementImpl *c = nullptr;
0884     HTMLCollectionImpl children(const_cast<HTMLTableRowElementImpl *>(this), HTMLCollectionImpl::TR_CELLS);
0885     int numCells = children.length();
0886     if (index < -1 || index > numCells) {
0887         exceptioncode = DOMException::INDEX_SIZE_ERR;    // per the DOM
0888     } else {
0889         c = new HTMLTableCellElementImpl(docPtr(), ID_TD);
0890         if (numCells == index || index == -1) {
0891             appendChild(c, exceptioncode);
0892         } else {
0893             NodeImpl *n;
0894             if (index < 1) {
0895                 n = firstChild();
0896             } else {
0897                 n = children.item(index);
0898             }
0899             insertBefore(c, n, exceptioncode);
0900         }
0901     }
0902     return c;
0903 }
0904 
0905 void HTMLTableRowElementImpl::deleteCell(long index, int &exceptioncode)
0906 {
0907     HTMLCollectionImpl children(const_cast<HTMLTableRowElementImpl *>(this), HTMLCollectionImpl::TR_CELLS);
0908     int numCells = children.length();
0909     if (index == -1) {
0910         index = numCells - 1;
0911     }
0912     if (index >= 0 && index < numCells) {
0913         HTMLElementImpl::removeChild(children.item(index), exceptioncode);
0914     } else {
0915         exceptioncode = DOMException::INDEX_SIZE_ERR;
0916     }
0917 }
0918 
0919 // -------------------------------------------------------------------------
0920 
0921 HTMLTableCellElementImpl::HTMLTableCellElementImpl(DocumentImpl *doc, int tag)
0922     : HTMLTablePartElementImpl(doc)
0923 {
0924     _col = -1;
0925     _row = -1;
0926     cSpan = rSpan = 1;
0927     _id = tag;
0928     rowHeight = 0;
0929     m_solid = false;
0930 }
0931 
0932 HTMLTableCellElementImpl::~HTMLTableCellElementImpl()
0933 {
0934 }
0935 
0936 long HTMLTableCellElementImpl::cellIndex() const
0937 {
0938     int index = 0;
0939     for (const NodeImpl *node = previousSibling(); node; node = node->previousSibling()) {
0940         if (node->id() == ID_TD || node->id() == ID_TH) {
0941             index++;
0942         }
0943     }
0944 
0945     return index;
0946 }
0947 
0948 void HTMLTableCellElementImpl::parseAttribute(AttributeImpl *attr)
0949 {
0950     switch (attr->id()) {
0951     case ATTR_BORDER:
0952         // euhm? not supported by other browsers as far as I can see (Dirk)
0953         //addCSSLength(CSS_PROP_BORDER_WIDTH, attr->value());
0954         break;
0955     case ATTR_ROWSPAN: {
0956         bool Ok = true;
0957         rSpan = attr->val() ? attr->val()->toInt(&Ok) : 1;
0958         // limit this to something not causing an overflow with short int
0959         if (rSpan < 0 || rSpan > 1024 || !Ok || (!rSpan && document()->inCompatMode())) {
0960             rSpan = 1;
0961         }
0962         if (renderer()) {
0963             renderer()->updateFromElement();
0964         }
0965         break;
0966     }
0967     case ATTR_COLSPAN: {
0968         bool Ok = true;
0969         cSpan = attr->val() ? attr->val()->toInt(&Ok) : 1;
0970         // limit this to something not causing an overflow with short int
0971         if (cSpan < 0 || cSpan > 1024 || !Ok || (!cSpan && document()->inCompatMode())) {
0972             cSpan = 1;
0973         }
0974         if (renderer()) {
0975             renderer()->updateFromElement();
0976         }
0977         break;
0978     }
0979     case ATTR_NOWRAP:
0980         if (attr->val() != nullptr) {
0981             addCSSProperty(CSS_PROP_WHITE_SPACE, CSS_VAL__KHTML_NOWRAP);
0982         } else {
0983             removeCSSProperty(CSS_PROP_WHITE_SPACE);
0984         }
0985         break;
0986     case ATTR_WIDTH:
0987         if (!attr->value().isEmpty()) {
0988             addCSSLength(CSS_PROP_WIDTH, attr->value());
0989         } else {
0990             removeCSSProperty(CSS_PROP_WIDTH);
0991         }
0992         break;
0993     case ATTR_NOSAVE:
0994         break;
0995     default:
0996         HTMLTablePartElementImpl::parseAttribute(attr);
0997     }
0998 }
0999 
1000 void HTMLTableCellElementImpl::attach()
1001 {
1002     HTMLTablePartElementImpl::attach();
1003 }
1004 
1005 // -------------------------------------------------------------------------
1006 
1007 HTMLTableColElementImpl::HTMLTableColElementImpl(DocumentImpl *doc, ushort i)
1008     : HTMLTablePartElementImpl(doc)
1009 {
1010     _id = i;
1011     _span = 1;
1012 }
1013 
1014 NodeImpl::Id HTMLTableColElementImpl::id() const
1015 {
1016     return _id;
1017 }
1018 
1019 void HTMLTableColElementImpl::parseAttribute(AttributeImpl *attr)
1020 {
1021     switch (attr->id()) {
1022     case ATTR_SPAN:
1023         _span = attr->val() ? attr->val()->toInt() : 1;
1024         if (_span < 1) {
1025             _span = 1;
1026         }
1027         break;
1028     case ATTR_WIDTH:
1029         if (!attr->value().isEmpty()) {
1030             addCSSLength(CSS_PROP_WIDTH, attr->value(), false, true);
1031         } else {
1032             removeCSSProperty(CSS_PROP_WIDTH);
1033         }
1034         break;
1035     case ATTR_VALIGN:
1036         if (!attr->value().isEmpty()) {
1037             addCSSProperty(CSS_PROP_VERTICAL_ALIGN, attr->value().lower());
1038         } else {
1039             removeCSSProperty(CSS_PROP_VERTICAL_ALIGN);
1040         }
1041         break;
1042     default:
1043         HTMLTablePartElementImpl::parseAttribute(attr);
1044     }
1045 
1046 }
1047 
1048 // -------------------------------------------------------------------------
1049 
1050 NodeImpl::Id HTMLTableCaptionElementImpl::id() const
1051 {
1052     return ID_CAPTION;
1053 }
1054 
1055 void HTMLTableCaptionElementImpl::parseAttribute(AttributeImpl *attr)
1056 {
1057     switch (attr->id()) {
1058     case ATTR_ALIGN:
1059         if (!attr->value().isEmpty()) {
1060             addCSSProperty(CSS_PROP_CAPTION_SIDE, attr->value().lower());
1061         } else {
1062             removeCSSProperty(CSS_PROP_CAPTION_SIDE);
1063         }
1064         break;
1065     default:
1066         HTMLElementImpl::parseAttribute(attr);
1067     }
1068 
1069 }