File indexing completed on 2024-04-28 11:38:57

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) 2005,2011 Allan Sandfeld Jensen (kde@carewolf.com)
0011  *           (C) 2008 Germain Garand (germain@ebooksfrance.org)
0012  *           (C) 2009 Carlos Licea (carlos.licea@kdemail.net)
0013  *
0014  * This library is free software; you can redistribute it and/or
0015  * modify it under the terms of the GNU Library General Public
0016  * License as published by the Free Software Foundation; either
0017  * version 2 of the License, or (at your option) any later version.
0018  *
0019  * This library is distributed in the hope that it will be useful,
0020  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0021  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0022  * Library General Public License for more details.
0023  *
0024  * You should have received a copy of the GNU Library General Public License
0025  * along with this library; see the file COPYING.LIB.  If not, write to
0026  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0027  * Boston, MA 02110-1301, USA.
0028  */
0029 
0030 //#define TABLE_DEBUG
0031 //#define TABLE_PRINT
0032 //#define DEBUG_LAYOUT
0033 //#define BOX_DEBUG
0034 #include "rendering/render_table.h"
0035 #include "rendering/render_replaced.h"
0036 #include "rendering/render_canvas.h"
0037 #include "rendering/table_layout.h"
0038 #include "html/html_tableimpl.h"
0039 #include "html/html_formimpl.h"
0040 #include "rendering/render_line.h"
0041 #include "xml/dom_docimpl.h"
0042 
0043 #include <QApplication>
0044 #include <QStyle>
0045 
0046 #include "khtml_debug.h"
0047 #include <assert.h>
0048 
0049 using namespace khtml;
0050 using namespace DOM;
0051 
0052 RenderTable::RenderTable(DOM::NodeImpl *node)
0053     : RenderBlock(node)
0054 {
0055 
0056     tCaption = nullptr;
0057     head = foot = firstBody = nullptr;
0058     tableLayout = nullptr;
0059     m_currentBorder = nullptr;
0060 
0061     has_col_elems = false;
0062     hspacing = vspacing = 0;
0063     padding = 0;
0064     needSectionRecalc = false;
0065     padding = 0;
0066 
0067     columnPos.resize(2);
0068     columnPos.fill(0);
0069     columns.resize(1);
0070     columns.fill(ColumnStruct());
0071 
0072     columnPos[0] = 0;
0073 }
0074 
0075 RenderTable::~RenderTable()
0076 {
0077     delete tableLayout;
0078 }
0079 
0080 void RenderTable::setStyle(RenderStyle *_style)
0081 {
0082     ETableLayout oldTableLayout = style() ? style()->tableLayout() : TAUTO;
0083     if (_style->display() == INLINE) {
0084         _style->setDisplay(INLINE_TABLE);
0085     }
0086     if (_style->display() != INLINE_TABLE) {
0087         _style->setDisplay(TABLE);
0088     }
0089     if (!_style->flowAroundFloats()) {
0090         _style->setFlowAroundFloats(true);
0091     }
0092     RenderBlock::setStyle(_style);
0093 
0094     // init RenderObject attributes
0095     setInline(style()->display() == INLINE_TABLE && !isPositioned());
0096     setReplaced(style()->display() == INLINE_TABLE);
0097 
0098     // In the collapsed border model, there is no cell spacing.
0099     hspacing = collapseBorders() ? 0 : style()->borderHorizontalSpacing();
0100     vspacing = collapseBorders() ? 0 : style()->borderVerticalSpacing();
0101     columnPos[0] = hspacing;
0102 
0103     if (!tableLayout || style()->tableLayout() != oldTableLayout) {
0104         delete tableLayout;
0105 
0106         // According to the CSS2 spec, you only use fixed table layout if an
0107         // explicit width is specified on the table.  Auto width implies auto table layout.
0108         if (style()->tableLayout() == TFIXED && !style()->width().isAuto()) {
0109             tableLayout = new FixedTableLayout(this);
0110 #ifdef DEBUG_LAYOUT
0111             qCDebug(KHTML_LOG) << "using fixed table layout";
0112 #endif
0113         } else {
0114             tableLayout = new AutoTableLayout(this);
0115         }
0116     }
0117 }
0118 
0119 short RenderTable::lineHeight(bool b) const
0120 {
0121     // Inline tables are replaced elements. Otherwise, just pass off to
0122     // the base class.
0123     if (isReplaced()) {
0124         return height() + marginTop() + marginBottom();
0125     }
0126     return RenderBlock::lineHeight(b);
0127 }
0128 
0129 short RenderTable::baselinePosition(bool b) const
0130 {
0131     // CSS2.1 - 10.8.1 The baseline of an 'inline-table' is the baseline of the first row of the table.
0132     if (isReplaced() && !needsLayout()) {
0133         // compatibility with Gecko: only apply to generic containers, not to HTML Table.
0134         if (element() && element()->id() == ID_TABLE) {
0135             return height() + marginTop() + marginBottom();
0136         }
0137 
0138         if (firstBodySection() && firstBodySection()->grid.size()) {
0139             return firstBodySection()->grid[0].baseLine + firstBodySection()->yPos() + marginTop();
0140         }
0141         return 0;
0142     }
0143     return RenderBox::baselinePosition(b);
0144 }
0145 
0146 static inline void resetSectionPointerIfNotBefore(RenderTableSection *&ptr, RenderObject *before)
0147 {
0148     if (!before || !ptr) {
0149         return;
0150     }
0151     RenderObject *o = before->previousSibling();
0152     while (o && o != ptr) {
0153         o = o->previousSibling();
0154     }
0155     if (!o) {
0156         ptr = nullptr;
0157     }
0158 }
0159 
0160 void RenderTable::addChild(RenderObject *child, RenderObject *beforeChild)
0161 {
0162 #ifdef DEBUG_LAYOUT
0163     qCDebug(KHTML_LOG) << renderName() << "(Table)::addChild( " << child->renderName() << ", " <<
0164              (beforeChild ? beforeChild->renderName() : "0") << " )";
0165 #endif
0166     bool wrapInAnonymousSection = false;
0167 
0168     switch (child->style()->display()) {
0169     case TABLE_CAPTION:
0170         if (child->isRenderBlock()) {
0171             // First caption wins.
0172             if (beforeChild && tCaption) {
0173                 RenderObject *o = beforeChild->previousSibling();
0174                 while (o && o != tCaption) {
0175                     o = o->previousSibling();
0176                 }
0177                 if (!o) {
0178                     tCaption = nullptr;
0179                 }
0180             }
0181             if (!tCaption) {
0182                 tCaption = static_cast<RenderBlock *>(child);
0183             }
0184         }
0185         break;
0186     case TABLE_COLUMN:
0187     case TABLE_COLUMN_GROUP:
0188         has_col_elems = true;
0189         break;
0190     case TABLE_HEADER_GROUP:
0191         if (child->isTableSection()) {
0192             resetSectionPointerIfNotBefore(head, beforeChild);
0193             if (!head) {
0194                 head = static_cast<RenderTableSection *>(child);
0195             } else {
0196                 resetSectionPointerIfNotBefore(firstBody, beforeChild);
0197                 if (!firstBody) {
0198                     firstBody = static_cast<RenderTableSection *>(child);
0199                 }
0200             }
0201         }
0202         break;
0203     case TABLE_FOOTER_GROUP:
0204         if (child->isTableSection()) {
0205             resetSectionPointerIfNotBefore(foot, beforeChild);
0206             if (!foot) {
0207                 foot = static_cast<RenderTableSection *>(child);
0208                 break;
0209             }
0210         }
0211     // fall through
0212     case TABLE_ROW_GROUP:
0213         if (child->isTableSection()) {
0214             resetSectionPointerIfNotBefore(firstBody, beforeChild);
0215             if (!firstBody) {
0216                 firstBody = static_cast<RenderTableSection *>(child);
0217             }
0218         }
0219         break;
0220     case TABLE_CELL:
0221     case TABLE_ROW:
0222         wrapInAnonymousSection = true;
0223         break;
0224     case BLOCK:
0225 //         case BOX:
0226     case COMPACT:
0227     case INLINE:
0228     case INLINE_BLOCK:
0229 //         case INLINE_BOX:
0230     case INLINE_TABLE:
0231     case LIST_ITEM:
0232     case NONE:
0233     case RUN_IN:
0234     case TABLE:
0235         // The special TABLE > FORM quirk allows the form to sit directly under the table
0236         if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM) {
0237             wrapInAnonymousSection = !static_cast<HTMLFormElementImpl *>(child->element())->isMalformed();
0238         } else {
0239             wrapInAnonymousSection = true;
0240         }
0241         break;
0242     }
0243 
0244     if (!wrapInAnonymousSection) {
0245         RenderContainer::addChild(child, beforeChild);
0246         return;
0247     }
0248 
0249     if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
0250         lastChild()->addChild(child);
0251         return;
0252     }
0253 
0254     RenderObject *lastBox = beforeChild;
0255     while (lastBox && lastBox->parent()->isAnonymous() &&
0256             !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION) {
0257         lastBox = lastBox->parent();
0258     }
0259     if (lastBox && lastBox->isAnonymous()) {
0260         lastBox->addChild(child, beforeChild);
0261         return;
0262     }
0263 
0264     if (beforeChild && !beforeChild->isTableSection()) {
0265         beforeChild = nullptr;
0266     }
0267     RenderTableSection *section = new(renderArena()) RenderTableSection(document() /* anonymous */);
0268     RenderStyle *newStyle = new RenderStyle();
0269     newStyle->inheritFrom(style());
0270     newStyle->setDisplay(TABLE_ROW_GROUP);
0271     section->setStyle(newStyle);
0272     addChild(section, beforeChild);
0273     section->addChild(child);
0274 }
0275 
0276 void RenderTable::calcWidth()
0277 {
0278     if (isPositioned()) {
0279         calcAbsoluteHorizontal();
0280     }
0281 
0282     RenderBlock *cb = containingBlock();
0283     int availableWidth = cb->lineWidth(m_y);
0284 
0285     LengthType widthType = style()->width().type();
0286     if (widthType > Relative && style()->width().isPositive()) {
0287         // Percent or fixed table
0288         // Percent is calculated from contentWidth, not available width
0289         m_width = calcBoxWidth(style()->width().minWidth(cb->contentWidth()));
0290     } else {
0291         // Subtract out any fixed margins from our available width for auto width tables.
0292         int marginTotal = 0;
0293         if (!style()->marginLeft().isAuto()) {
0294             marginTotal += style()->marginLeft().width(availableWidth);
0295         }
0296         if (!style()->marginRight().isAuto()) {
0297             marginTotal += style()->marginRight().width(availableWidth);
0298         }
0299 
0300         // Subtract out our margins to get the available content width.
0301         int availContentWidth = qMax(0, availableWidth - marginTotal);
0302 
0303         // Ensure we aren't bigger than our max width or smaller than our min width.
0304         m_width = qMin(availContentWidth, m_maxWidth);
0305     }
0306 
0307     m_width = qMax(m_width, m_minWidth);
0308 
0309     // Finally, with our true width determined, compute our margins for real.
0310     m_marginRight = 0;
0311     m_marginLeft = 0;
0312 
0313     calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth);
0314 }
0315 
0316 QList< QRectF > RenderTable::getClientRects()
0317 {
0318     RenderFlow *cap = caption();
0319     if (cap) {
0320         // tables with caption report y&height inclusive caption, but we need them
0321         // exclusive and a extra rect for the caption
0322         // NOTE: first table, then caption
0323         QList<QRectF> list;
0324 
0325         int x = 0;
0326         int y = 0;
0327         absolutePosition(x, y);
0328 
0329         QRectF tableRect(x, y + cap->height(), width(), height() - cap->height());
0330         list.append(clientRectToViewport(tableRect));
0331 
0332         QRectF captionRect(x, y, cap->width(), cap->height());
0333         list.append(clientRectToViewport(captionRect));
0334 
0335         return list;
0336     } else {
0337         return RenderObject::getClientRects();
0338     }
0339 }
0340 
0341 void RenderTable::layout()
0342 {
0343     KHTMLAssert(needsLayout());
0344     KHTMLAssert(minMaxKnown());
0345     KHTMLAssert(!needSectionRecalc);
0346 
0347     if (markedForRepaint()) {
0348         repaintDuringLayout();
0349         setMarkedForRepaint(false);
0350     }
0351 
0352     if (posChildNeedsLayout() && !normalChildNeedsLayout() && !selfNeedsLayout()) {
0353         // All we have to is lay out our positioned objects.
0354         layoutPositionedObjects(true);
0355         setNeedsLayout(false);
0356         return;
0357     }
0358 
0359     m_height = 0;
0360     initMaxMarginValues();
0361 
0362     int oldWidth = m_width;
0363     calcWidth();
0364     m_overflowWidth = m_width;
0365 
0366     if (tCaption && (oldWidth != m_width || tCaption->style()->height().isPercent())) {
0367         tCaption->setChildNeedsLayout(true);
0368     }
0369 
0370     // the optimization below doesn't work since the internal table
0371     // layout could have changed.  we need to add a flag to the table
0372     // layout that tells us if something has changed in the min max
0373     // calculations to do it correctly.
0374 //     if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() )
0375     tableLayout->layout();
0376 
0377 #ifdef DEBUG_LAYOUT
0378     qCDebug(KHTML_LOG) << renderName() << "(Table)::layout1() width=" << width() << ", marginLeft=" << marginLeft() << " marginRight=" << marginRight();
0379 #endif
0380 
0381     setCellWidths();
0382 
0383     // layout child objects
0384     int calculatedHeight = 0;
0385 
0386     RenderObject *child = firstChild();
0387     while (child) {
0388         // FIXME: What about a form that has a display value that makes it a table section?
0389         if (child->needsLayout() && !(child->element() && child->element()->id() == ID_FORM)) {
0390             child->layout();
0391         }
0392         if (child->isTableSection()) {
0393             static_cast<RenderTableSection *>(child)->calcRowHeight();
0394             calculatedHeight += static_cast<RenderTableSection *>(child)->layoutRows(0);
0395         }
0396         child = child->nextSibling();
0397     }
0398 
0399     // ### collapse caption margin
0400     if (tCaption && tCaption->style()->captionSide() != CAPBOTTOM) {
0401         tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height);
0402         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
0403     }
0404 
0405     int bpTop = borderTop() + (collapseBorders() ? 0 : paddingTop());
0406     int bpBottom = borderBottom() + (collapseBorders() ? 0 : paddingBottom());
0407 
0408     m_height += bpTop;
0409 
0410     int oldHeight = m_height;
0411     if (isPositioned()) {
0412         m_height += calculatedHeight + bpBottom;
0413     }
0414     calcHeight();
0415     int newHeight = m_height;
0416     m_height = oldHeight;
0417 
0418     Length h = style()->height();
0419     int th = -(bpTop + bpBottom); // Tables size as though CSS height includes border/padding.
0420     if (isPositioned()) {
0421         th += newHeight;
0422     } else if (h.isFixed()) {
0423         th += h.value();
0424     } else if (h.isPercent()) {
0425         th += calcPercentageHeight(h);
0426     }
0427 
0428     // layout rows
0429     if (th > calculatedHeight) {
0430         // we have to redistribute that height to get the constraint correctly
0431         // just force the first body to the height needed
0432         // ### FIXME This should take height constraints on all table sections into account and distribute
0433         // accordingly. For now this should be good enough
0434         if (firstBody) {
0435             firstBody->calcRowHeight();
0436             firstBody->layoutRows(th - calculatedHeight);
0437         } else if (!style()->htmlHacks()) {
0438             // Completely empty tables (with no sections or anything) should at least honor specified height
0439             // in strict mode.
0440             m_height += th;
0441         }
0442     }
0443 
0444     int bl = borderLeft();
0445     if (!collapseBorders()) {
0446         bl += paddingLeft();
0447     }
0448 
0449     // position the table sections
0450     RenderTableSection *section = head ? head : (firstBody ? firstBody : foot);
0451     while (section) {
0452         section->setPos(bl, m_height);
0453 
0454         m_height += section->height();
0455         m_overflowLeft = qMin(m_overflowLeft, section->effectiveXPos());
0456         m_overflowWidth = qMax(m_overflowWidth, section->effectiveXPos() + section->effectiveWidth());
0457         section = sectionBelow(section);
0458     }
0459 
0460     m_height += bpBottom;
0461 
0462     if (tCaption && tCaption->style()->captionSide() == CAPBOTTOM) {
0463         tCaption->setPos(tCaption->marginLeft(), tCaption->marginTop() + m_height);
0464         m_height += tCaption->height() + tCaption->marginTop() + tCaption->marginBottom();
0465     }
0466 
0467     if (canvas()->pagedMode()) {
0468         RenderObject *child = firstChild();
0469         // relayout taking real position into account
0470         while (child) {
0471             if (!(child->element() && child->element()->id() == ID_FORM)) {
0472                 child->setNeedsLayout(true);
0473                 child->layout();
0474                 if (child->containsPageBreak()) {
0475                     setContainsPageBreak(true);
0476                 }
0477                 if (child->needsPageClear()) {
0478                     setNeedsPageClear(true);
0479                 }
0480             }
0481             child = child->nextSibling();
0482         }
0483     }
0484 
0485     //qCDebug(KHTML_LOG) << "table height: " << m_height;
0486 
0487     // table can be containing block of positioned elements.
0488     // ### only pass true if width or height changed.
0489     layoutPositionedObjects(true);
0490 
0491     m_overflowHeight = m_height;
0492 
0493     setNeedsLayout(false);
0494 }
0495 
0496 void RenderTable::setCellWidths()
0497 {
0498 #ifdef DEBUG_LAYOUT
0499     qCDebug(KHTML_LOG) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()";
0500 #endif
0501 
0502     RenderObject *child = firstChild();
0503     while (child) {
0504         if (child->isTableSection()) {
0505             static_cast<RenderTableSection *>(child)->setCellWidths();
0506         }
0507         child = child->nextSibling();
0508     }
0509 }
0510 
0511 void RenderTable::paint(PaintInfo &pI, int _tx, int _ty)
0512 {
0513     if (needsLayout()) {
0514         return;
0515     }
0516 
0517     _tx += xPos();
0518     _ty += yPos();
0519 
0520 #ifdef TABLE_PRINT
0521     qCDebug(KHTML_LOG) << "RenderTable::paint() w/h = (" << width() << "/" << height() << ")";
0522 #endif
0523     if (!isRelPositioned() && !isPositioned()) {
0524         int os = 2 * maximalOutlineSize(pI.phase);
0525         if ((_ty > pI.r.y() + pI.r.height() + os) || (_ty + height() < pI.r.y() - os)) {
0526             return;
0527         }
0528         if ((_tx > pI.r.x() + pI.r.width() + os) || (_tx + width() < pI.r.x() - os)) {
0529             return;
0530         }
0531     }
0532 
0533 #ifdef TABLE_PRINT
0534     qCDebug(KHTML_LOG) << "RenderTable::paint(2) " << _tx << "/" << _ty << " (" << _y << "/" << _h << ")";
0535 #endif
0536 
0537     if (pI.phase == PaintActionOutline) {
0538         paintOutline(pI.p, _tx, _ty, width(), height(), style());
0539     }
0540 
0541     if ((pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground)
0542             && shouldPaintBackgroundOrBorder() && style()->visibility() == VISIBLE) {
0543         paintBoxDecorations(pI, _tx, _ty);
0544     }
0545 
0546     if (pI.phase == PaintActionElementBackground) {
0547         return;
0548     }
0549 
0550     PaintAction oldphase = pI.phase;
0551     if (pI.phase == PaintActionChildBackgrounds) {
0552         pI.phase = PaintActionChildBackground;
0553     }
0554 
0555     for (RenderObject *child = firstChild(); child; child = child->nextSibling())
0556         if (child->isTableSection() || child == tCaption) {
0557             child->paint(pI, _tx, _ty);
0558         }
0559 
0560     if (collapseBorders() &&
0561             (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground)
0562             && style()->visibility() == VISIBLE) {
0563         // Collect all the unique border styles that we want to paint in a sorted list.  Once we
0564         // have all the styles sorted, we then do individual passes, painting each style of border
0565         // from lowest precedence to highest precedence.
0566         pI.phase = PaintActionCollapsedTableBorders;
0567         QList<CollapsedBorderValue> borderStyles;
0568         collectBorders(borderStyles);
0569 #if 0
0570         QString m;
0571         for (uint i = 0; i < borderStyles.count(); i++) {
0572             m += QString("%1 ").arg((*borderStyles.at(i)).width());
0573         }
0574         // qCDebug(KHTML_LOG) << m;
0575 #endif
0576         QList<CollapsedBorderValue>::Iterator it = borderStyles.begin();
0577         QList<CollapsedBorderValue>::Iterator end = borderStyles.end();
0578         for (; it != end; ++it) {
0579             m_currentBorder = &*it;
0580             for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
0581                 if (child->isTableSection()) {
0582                     child->paint(pI, _tx, _ty);
0583                 }
0584             }
0585         }
0586         m_currentBorder = nullptr;
0587     }
0588 
0589     pI.phase = oldphase;
0590 #ifdef BOX_DEBUG
0591     outlineBox(pI.p, _tx, _ty, "blue");
0592 #endif
0593 }
0594 
0595 void RenderTable::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty)
0596 {
0597     int w = width();
0598     int h = height();
0599 
0600     // Account for the caption.
0601     if (tCaption) {
0602         int captionHeight = (tCaption->height() + tCaption->marginBottom() +  tCaption->marginTop());
0603         h -= captionHeight;
0604         if (tCaption->style()->captionSide() != CAPBOTTOM) {
0605             _ty += captionHeight;
0606         }
0607     }
0608     QRect cr;
0609     cr.setX(qMax(_tx, pI.r.x()));
0610     cr.setY(qMax(_ty, pI.r.y()));
0611     int mw, mh;
0612     if (_ty < pI.r.y()) {
0613         mh = qMax(0, h - (pI.r.y() - _ty));
0614     } else {
0615         mh = qMin(pI.r.height(), h);
0616     }
0617     if (_tx < pI.r.x()) {
0618         mw = qMax(0, w - (pI.r.x() - _tx));
0619     } else {
0620         mw = qMin(pI.r.width(), w);
0621     }
0622     cr.setWidth(mw);
0623     cr.setHeight(mh);
0624 
0625     paintOneBackground(pI.p, style()->backgroundColor(), style()->backgroundLayers(), cr, _tx, _ty, w, h);
0626 
0627     if (style()->hasBorder() && !collapseBorders()) {
0628         paintBorder(pI.p, _tx, _ty, w, h, style());
0629     }
0630 }
0631 
0632 void RenderTable::calcMinMaxWidth()
0633 {
0634     KHTMLAssert(!minMaxKnown());
0635 
0636     if (needSectionRecalc) {
0637         recalcSections();
0638     }
0639 
0640 #ifdef DEBUG_LAYOUT
0641     qCDebug(KHTML_LOG) << renderName() << "(Table " << this << ")::calcMinMaxWidth()";
0642 #endif
0643 
0644     tableLayout->calcMinMaxWidth();
0645 
0646     if (tCaption) {
0647         tCaption->calcWidth();
0648         if (tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth() > m_minWidth) {
0649             m_minWidth = tCaption->marginLeft() + tCaption->marginRight() + tCaption->minWidth();
0650         }
0651     }
0652 
0653     setMinMaxKnown();
0654 #ifdef DEBUG_LAYOUT
0655     qCDebug(KHTML_LOG) << renderName() << " END: (Table " << this << ")::calcMinMaxWidth() min = " << m_minWidth << " max = " << m_maxWidth;
0656 #endif
0657 }
0658 
0659 void RenderTable::close()
0660 {
0661 //    qCDebug(KHTML_LOG) << "RenderTable::close()";
0662     setNeedsLayoutAndMinMaxRecalc();
0663 }
0664 
0665 void RenderTable::splitColumn(int pos, int firstSpan)
0666 {
0667     // we need to add a new columnStruct
0668     int oldSize = columns.size();
0669     columns.resize(oldSize + 1);
0670     int oldSpan = columns[pos].span;
0671 //     qDebug("splitColumn( %d,%d ), oldSize=%d, oldSpan=%d", pos, firstSpan, oldSize, oldSpan );
0672     KHTMLAssert(oldSpan > firstSpan);
0673     columns[pos].span = firstSpan;
0674     memmove(columns.data() + pos + 1, columns.data() + pos, (oldSize - pos)*sizeof(ColumnStruct));
0675     columns[pos + 1].span = oldSpan - firstSpan;
0676 
0677     // change width of all rows.
0678     RenderObject *child = firstChild();
0679     while (child) {
0680         if (child->isTableSection()) {
0681             RenderTableSection *section = static_cast<RenderTableSection *>(child);
0682             int size = section->grid.size();
0683             int row = 0;
0684             if (section->cCol > pos) {
0685                 section->cCol++;
0686             }
0687             while (row < size) {
0688                 section->grid[row].row->resize(oldSize + 1);
0689                 RenderTableSection::Row &r = *section->grid[row].row;
0690                 memmove(r.data() + pos + 1, r.data() + pos, (oldSize - pos)*sizeof(RenderTableCell *));
0691 //      qDebug("moving from %d to %d, num=%d", pos, pos+1, (oldSize-pos-1) );
0692                 r[pos + 1] = r[pos] ? (RenderTableCell *) - 1 : nullptr;
0693                 row++;
0694             }
0695         }
0696         child = child->nextSibling();
0697     }
0698     columnPos.resize(numEffCols() + 1);
0699     setNeedsLayoutAndMinMaxRecalc();
0700 }
0701 
0702 void RenderTable::appendColumn(int span)
0703 {
0704     // easy case.
0705     int pos = columns.size();
0706 //     qDebug("appendColumn( %d ), size=%d", span, pos );
0707     int newSize = pos + 1;
0708     columns.resize(newSize);
0709     columns[pos].span = span;
0710     //qDebug("appending column at %d, span %d", pos,  span );
0711 
0712     // change width of all rows.
0713     RenderObject *child = firstChild();
0714     while (child) {
0715         if (child->isTableSection()) {
0716             RenderTableSection *section = static_cast<RenderTableSection *>(child);
0717             int size = section->grid.size();
0718             int row = 0;
0719             while (row < size) {
0720                 section->grid[row].row->resize(newSize);
0721                 section->cellAt(row, pos) = nullptr;
0722                 row++;
0723             }
0724 
0725         }
0726         child = child->nextSibling();
0727     }
0728     columnPos.resize(numEffCols() + 1);
0729     setNeedsLayoutAndMinMaxRecalc();
0730 }
0731 
0732 RenderTableCol *RenderTable::colElement(int col, bool *startEdge, bool *endEdge) const
0733 {
0734     if (!has_col_elems) {
0735         return nullptr;
0736     }
0737     RenderObject *child = firstChild();
0738     int cCol = 0;
0739     while (child) {
0740         if (child->isTableCol()) {
0741             RenderTableCol *colElem = static_cast<RenderTableCol *>(child);
0742             int span = colElem->span();
0743             if (!colElem->firstChild()) {
0744                 int startCol = cCol;
0745                 int endCol = cCol + span - 1;
0746                 cCol += span;
0747                 if (cCol > col) {
0748                     if (startEdge) {
0749                         *startEdge = startCol == col;
0750                     }
0751                     if (endEdge) {
0752                         *endEdge = endCol == col;
0753                     }
0754                     return colElem;
0755                 }
0756             }
0757 
0758             RenderObject *next = child->firstChild();
0759             if (!next) {
0760                 next = child->nextSibling();
0761             }
0762             if (!next && child->parent()->isTableCol()) {
0763                 next = child->parent()->nextSibling();
0764             }
0765             child = next;
0766         } else if (child == tCaption) {
0767             child = child->nextSibling();
0768         } else {
0769             break;
0770         }
0771     }
0772     return nullptr;
0773 }
0774 
0775 void RenderTable::recalcSections()
0776 {
0777     tCaption = nullptr;
0778     head = foot = firstBody = nullptr;
0779     has_col_elems = false;
0780 
0781     RenderObject *child = firstChild();
0782     // We need to get valid pointers to caption, head, foot and firstbody again
0783     while (child) {
0784         switch (child->style()->display()) {
0785         case TABLE_CAPTION:
0786             if (!tCaption && child->isRenderBlock()) {
0787                 tCaption = static_cast<RenderBlock *>(child);
0788                 tCaption->setNeedsLayout(true);
0789             }
0790             break;
0791         case TABLE_COLUMN:
0792         case TABLE_COLUMN_GROUP:
0793             has_col_elems = true;
0794             break;
0795         case TABLE_HEADER_GROUP:
0796             if (child->isTableSection()) {
0797                 RenderTableSection *section = static_cast<RenderTableSection *>(child);
0798                 if (!head) {
0799                     head = section;
0800                 } else if (!firstBody) {
0801                     firstBody = section;
0802                 }
0803                 if (section->needCellRecalc) {
0804                     section->recalcCells();
0805                 }
0806             }
0807             break;
0808         case TABLE_FOOTER_GROUP:
0809             if (child->isTableSection()) {
0810                 RenderTableSection *section = static_cast<RenderTableSection *>(child);
0811                 if (!foot) {
0812                     foot = section;
0813                 } else if (!firstBody) {
0814                     firstBody = section;
0815                 }
0816                 if (section->needCellRecalc) {
0817                     section->recalcCells();
0818                 }
0819             }
0820             break;
0821         case TABLE_ROW_GROUP:
0822             if (child->isTableSection()) {
0823                 RenderTableSection *section = static_cast<RenderTableSection *>(child);
0824                 if (!firstBody) {
0825                     firstBody = section;
0826                 }
0827                 if (section->needCellRecalc) {
0828                     section->recalcCells();
0829                 }
0830             }
0831             break;
0832         default:
0833             break;
0834         }
0835         child = child->nextSibling();
0836     }
0837 
0838     int maxCols = 0;
0839     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
0840         if (child->isTableSection()) {
0841             RenderTableSection *section = static_cast<RenderTableSection *>(child);
0842             int sectionCols = section->numColumns();
0843             if (sectionCols > maxCols) {
0844                 maxCols = sectionCols;
0845             }
0846         }
0847     }
0848 
0849     columns.resize(maxCols);
0850     columnPos.resize(maxCols + 1);
0851 
0852     needSectionRecalc = false;
0853     setNeedsLayout(true);
0854 }
0855 
0856 RenderObject *RenderTable::removeChildNode(RenderObject *child)
0857 {
0858     setNeedSectionRecalc();
0859     return RenderContainer::removeChildNode(child);
0860 }
0861 
0862 int RenderTable::borderLeft() const
0863 {
0864     if (collapseBorders()) {
0865         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
0866         // but I'm working to get this changed.  For now, follow the spec.
0867         return 0;
0868     }
0869     return RenderBlock::borderLeft();
0870 }
0871 
0872 int RenderTable::borderRight() const
0873 {
0874     if (collapseBorders()) {
0875         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
0876         // but I'm working to get this changed.  For now, follow the spec.
0877         return 0;
0878     }
0879     return RenderBlock::borderRight();
0880 }
0881 
0882 int RenderTable::borderTop() const
0883 {
0884     if (collapseBorders()) {
0885         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
0886         // but I'm working to get this changed.  For now, follow the spec.
0887         return 0;
0888     }
0889     return RenderBlock::borderTop();
0890 }
0891 
0892 int RenderTable::borderBottom() const
0893 {
0894     if (collapseBorders()) {
0895         // FIXME: For strict mode, returning 0 is correct, since the table border half spills into the margin,
0896         // but I'm working to get this changed.  For now, follow the spec.
0897         return 0;
0898     }
0899     return RenderBlock::borderBottom();
0900 }
0901 
0902 RenderTableSection *RenderTable::sectionAbove(const RenderTableSection *section, bool skipEmptySections)
0903 {
0904     if (needSectionRecalc) {
0905         recalcSections();
0906     }
0907 
0908     if (section == head) {
0909         return nullptr;
0910     }
0911     RenderObject *prevSection = section == foot ? lastChild() : section->previousSibling();
0912     while (prevSection) {
0913         if (prevSection->isTableSection() && prevSection != head && prevSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(prevSection)->numRows())) {
0914             break;
0915         }
0916         prevSection = prevSection->previousSibling();
0917     }
0918     if (!prevSection && head && (!skipEmptySections || head->numRows())) {
0919         prevSection = head;
0920     }
0921     return static_cast<RenderTableSection *>(prevSection);
0922 }
0923 
0924 RenderTableSection *RenderTable::sectionBelow(const RenderTableSection *section, bool skipEmptySections)
0925 {
0926     if (needSectionRecalc) {
0927         recalcSections();
0928     }
0929 
0930     if (section == foot) {
0931         return nullptr;
0932     }
0933     RenderObject *nextSection = section == head ? firstChild() : section->nextSibling();
0934     while (nextSection) {
0935         if (nextSection->isTableSection() && nextSection != head && nextSection != foot && (!skipEmptySections || static_cast<RenderTableSection *>(nextSection)->numRows())) {
0936             break;
0937         }
0938         nextSection = nextSection->nextSibling();
0939     }
0940     if (!nextSection && foot && (!skipEmptySections || foot->numRows())) {
0941         nextSection = foot;
0942     }
0943     return static_cast<RenderTableSection *>(nextSection);
0944 }
0945 
0946 RenderTableCell *RenderTable::cellAbove(const RenderTableCell *cell)
0947 {
0948     if (needSectionRecalc) {
0949         recalcSections();
0950     }
0951 
0952     // Find the section and row to look in
0953     int r = cell->row();
0954     RenderTableSection *section = nullptr;
0955     int rAbove = 0;
0956     if (r > 0) {
0957         // cell is not in the first row, so use the above row in its own section
0958         section = cell->section();
0959         rAbove = r - 1;
0960     } else {
0961         section = sectionAbove(cell->section(), true);
0962         if (section) {
0963             rAbove = section->numRows() - 1;
0964         }
0965     }
0966 
0967     // Look up the cell in the section's grid, which requires effective col index
0968     if (section) {
0969         int effCol = colToEffCol(cell->col());
0970         RenderTableCell *aboveCell;
0971         // If we hit a span back up to a real cell.
0972         do {
0973             aboveCell = section->cellAt(rAbove, effCol);
0974             effCol--;
0975         } while (aboveCell == (RenderTableCell *) - 1 && effCol >= 0);
0976         return (aboveCell == (RenderTableCell *) - 1) ? nullptr : aboveCell;
0977     } else {
0978         return nullptr;
0979     }
0980 }
0981 
0982 RenderTableCell *RenderTable::cellBelow(const RenderTableCell *cell)
0983 {
0984     if (needSectionRecalc) {
0985         recalcSections();
0986     }
0987 
0988     // Find the section and row to look in
0989     int r = cell->row() + cell->rowSpan() - 1;
0990     RenderTableSection *section = nullptr;
0991     int rBelow = 0;
0992     if (r < cell->section()->numRows() - 1) {
0993         // The cell is not in the last row, so use the next row in the section.
0994         section = cell->section();
0995         rBelow = r + 1;
0996     } else {
0997         section = sectionBelow(cell->section(), true);
0998         if (section) {
0999             rBelow = 0;
1000         }
1001     }
1002 
1003     // Look up the cell in the section's grid, which requires effective col index
1004     if (section) {
1005         int effCol = colToEffCol(cell->col());
1006         RenderTableCell *belowCell;
1007         // If we hit a colspan back up to a real cell.
1008         do {
1009             belowCell = section->cellAt(rBelow, effCol);
1010             effCol--;
1011         } while (belowCell == (RenderTableCell *) - 1 && effCol >= 0);
1012         return (belowCell == (RenderTableCell *) - 1) ? nullptr : belowCell;
1013     } else {
1014         return nullptr;
1015     }
1016 }
1017 
1018 RenderTableCell *RenderTable::cellBefore(const RenderTableCell *cell)
1019 {
1020     if (needSectionRecalc) {
1021         recalcSections();
1022     }
1023 
1024     RenderTableSection *section = cell->section();
1025     int effCol = colToEffCol(cell->col());
1026     if (effCol == 0) {
1027         return nullptr;
1028     }
1029 
1030     // If we hit a colspan back up to a real cell.
1031     RenderTableCell *prevCell;
1032     do {
1033         prevCell = section->cellAt(cell->row(), effCol - 1);
1034         effCol--;
1035     } while (prevCell == (RenderTableCell *) - 1 && effCol >= 0);
1036     return (prevCell == (RenderTableCell *) - 1) ? nullptr : prevCell;
1037 }
1038 
1039 RenderTableCell *RenderTable::cellAfter(const RenderTableCell *cell)
1040 {
1041     if (needSectionRecalc) {
1042         recalcSections();
1043     }
1044 
1045     int effCol = colToEffCol(cell->col() + cell->colSpan());
1046     if (effCol >= numEffCols()) {
1047         return nullptr;
1048     }
1049     RenderTableCell *result = cell->section()->cellAt(cell->row(), effCol);
1050     return (result == (RenderTableCell *) - 1) ? nullptr : result;
1051 }
1052 
1053 #ifdef ENABLE_DUMP
1054 void RenderTable::dump(QTextStream &stream, const QString &ind) const
1055 {
1056     RenderBlock::dump(stream, ind);
1057 
1058     if (tCaption) {
1059         stream << " tCaption";
1060     }
1061     if (head) {
1062         stream << " head";
1063     }
1064     if (foot) {
1065         stream << " foot";
1066     }
1067 
1068     stream << " [cspans:";
1069     for (int i = 0; i < columns.size(); i++) {
1070         stream << " " << columns[i].span;
1071     }
1072     stream << "]";
1073 }
1074 
1075 #endif
1076 
1077 FindSelectionResult RenderTable::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &state)
1078 {
1079     int off = offset;
1080     DOM::NodeImpl *nod = node;
1081 
1082     FindSelectionResult pos;
1083     TableSectionIterator it(this);
1084     for (; *it; ++it) {
1085         pos = (*it)->checkSelectionPoint(_x, _y, _tx + m_x, _ty + m_y, nod, off, state);
1086         switch (pos) {
1087         case SelectionPointBeforeInLine:
1088         case SelectionPointInside:
1089             //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset;
1090             node = nod;
1091             offset = off;
1092             return SelectionPointInside;
1093         case SelectionPointBefore:
1094             //x,y is before this element -> stop here
1095             if (state.m_lastNode) {
1096                 node = state.m_lastNode;
1097                 offset = state.m_lastOffset;
1098                 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " before this child "
1099                 //              << node << "-> returning SelectionPointInside, offset=" << offset;
1100                 return SelectionPointInside;
1101             } else {
1102                 node = nod;
1103                 offset = off;
1104                 //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset;
1105                 return SelectionPointBefore;
1106             }
1107             break;
1108         case SelectionPointAfter:
1109             if (state.m_afterInLine) {
1110                 break;
1111             }
1112         // fall through
1113         case SelectionPointAfterInLine:
1114             if (pos == SelectionPointAfterInLine) {
1115                 state.m_afterInLine = true;
1116             }
1117             //qCDebug(KHTML_LOG) << "RenderTable::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine;
1118             state.m_lastNode = nod;
1119             state.m_lastOffset = off;
1120             // No "return" here, obviously. We must keep looking into the children.
1121             break;
1122         }
1123     }
1124     // If we are after the last child, return lastNode/lastOffset
1125     // But lastNode can be 0L if there is no child, for instance.
1126     if (state.m_lastNode) {
1127         node = state.m_lastNode;
1128         offset = state.m_lastOffset;
1129     }
1130     // Fallback
1131     return SelectionPointAfter;
1132 }
1133 
1134 // --------------------------------------------------------------------------
1135 
1136 RenderTableSection::RenderTableSection(DOM::NodeImpl *node)
1137     : RenderBox(node)
1138 {
1139     // init RenderObject attributes
1140     setInline(false);   // our object is not Inline
1141     cCol = 0;
1142     cRow = -1;
1143     needCellRecalc = false;
1144 }
1145 
1146 RenderTableSection::~RenderTableSection()
1147 {
1148     clearGrid();
1149 }
1150 
1151 void RenderTableSection::detach()
1152 {
1153     // recalc cell info because RenderTable has unguarded pointers
1154     // stored that point to this RenderTableSection.
1155     if (table()) {
1156         table()->setNeedSectionRecalc();
1157     }
1158 
1159     RenderBox::detach();
1160 }
1161 
1162 void RenderTableSection::setStyle(RenderStyle *_style)
1163 {
1164     // we don't allow changing this one
1165     if (style()) {
1166         _style->setDisplay(style()->display());
1167     } else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP) {
1168         _style->setDisplay(TABLE_ROW_GROUP);
1169     }
1170 
1171     RenderBox::setStyle(_style);
1172 }
1173 
1174 void RenderTableSection::addChild(RenderObject *child, RenderObject *beforeChild)
1175 {
1176 #ifdef DEBUG_LAYOUT
1177     qCDebug(KHTML_LOG) << renderName() << "(TableSection)::addChild( " << child->renderName()  << ", beforeChild=" <<
1178              (beforeChild ? beforeChild->renderName() : "0") << " )";
1179 #endif
1180     if (!child->isTableRow()) {
1181         // TBODY > FORM quirk (???)
1182         if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM &&
1183                 static_cast<HTMLFormElementImpl *>(child->element())->isMalformed()) {
1184             RenderContainer::addChild(child, beforeChild);
1185             return;
1186         }
1187 
1188         RenderObject *last = beforeChild;
1189         if (!last) {
1190             last = lastChild();
1191         }
1192         if (last && last->isAnonymous()) {
1193             last->addChild(child);
1194             return;
1195         }
1196 
1197         // If beforeChild is inside an anonymous cell/row, insert into the cell or into
1198         // the anonymous row containing it, if there is one.
1199         RenderObject *lastBox = last;
1200         while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) {
1201             lastBox = lastBox->parent();
1202         }
1203         if (lastBox && lastBox->isAnonymous()) {
1204             lastBox->addChild(child, beforeChild);
1205             return;
1206         }
1207 
1208         RenderObject *row = new(renderArena()) RenderTableRow(document() /* anonymous table */);
1209         RenderStyle *newStyle = new RenderStyle();
1210         newStyle->inheritFrom(style());
1211         newStyle->setDisplay(TABLE_ROW);
1212         row->setStyle(newStyle);
1213         addChild(row, beforeChild);
1214         row->addChild(child);
1215         return;
1216     }
1217 
1218     if (beforeChild) {
1219         setNeedCellRecalc();
1220     }
1221 
1222     cRow++;
1223     cCol = 0;
1224 
1225     ensureRows(cRow + 1);
1226     KHTMLAssert(child->isTableRow());
1227     grid[cRow].rowRenderer = static_cast<RenderTableRow *>(child);
1228 
1229     if (!beforeChild) {
1230         grid[cRow].height = child->style()->height();
1231         if (grid[cRow].height.isRelative()) {
1232             grid[cRow].height = Length();
1233         }
1234     }
1235 
1236     RenderContainer::addChild(child, beforeChild);
1237 }
1238 
1239 void RenderTableSection::ensureRows(int numRows)
1240 {
1241     int nRows = grid.size();
1242     int nCols = table()->numEffCols();
1243     if (numRows > nRows) {
1244         grid.resize(numRows);
1245         for (int r = nRows; r < numRows; r++) {
1246             grid[r].row = new Row(nCols);
1247             grid[r].row->fill(nullptr);
1248             grid[r].rowRenderer = nullptr;
1249             grid[r].baseLine = 0;
1250             grid[r].height = Length();
1251         }
1252     }
1253 
1254 }
1255 
1256 void RenderTableSection::addCell(RenderTableCell *cell, RenderTableRow *row)
1257 {
1258     int rSpan = cell->rowSpan();
1259     int cSpan = cell->colSpan();
1260     const QVector<RenderTable::ColumnStruct> &columns = table()->columns;
1261     int nCols = columns.size();
1262 
1263     // ### mozilla still seems to do the old HTML way, even for strict DTD
1264     // (see the annotation on table cell layouting in the CSS specs and the testcase below:
1265     // <TABLE border>
1266     // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
1267     // <TR><TD colspan="2">5
1268     // </TABLE>
1269     while (cCol < nCols && cellAt(cRow, cCol)) {
1270         ++cCol;
1271     }
1272 
1273 //       qDebug("adding cell at %d/%d span=(%d/%d)",  cRow, cCol, rSpan, cSpan );
1274 
1275     if (rSpan == 1) {
1276         // we ignore height settings on rowspan cells
1277         Length height = cell->style()->height();
1278         if (height.isPositive() || (height.isRelative() && (height.isPositive() || height.isZero()))) {
1279             Length cRowHeight = grid[cRow].height;
1280             switch (height.type()) {
1281             case Percent:
1282                 if (!cRowHeight.isPercent() ||
1283                         (cRowHeight.isPercent() && cRowHeight.rawValue() < height.rawValue())) {
1284                     grid[cRow].height = height;
1285                 }
1286                 break;
1287             case Fixed:
1288                 if (cRowHeight.type() < Percent ||
1289                         (cRowHeight.isFixed() && cRowHeight.value() < height.value())) {
1290                     grid[cRow].height = height;
1291                 }
1292                 break;
1293             case Relative:
1294 #if 0
1295                 // we treat this as variable. This is correct according to HTML4, as it only specifies length for the height.
1296                 if (cRowHeight.type() == Auto ||
1297                         (cRowHeight.type() == Relative && cRowHeight.value() < height.value())) {
1298                     grid[cRow].height = height;
1299                 }
1300                 break;
1301 #endif
1302             default:
1303                 break;
1304             }
1305         }
1306     }
1307 
1308     //What:Postion the cell
1309     //How: we take care of special case when colspan or rowspan equals 0
1310     //setting an initial "guess" of a colspan and rowspan, look at
1311     //https://www.w3.org/TR/html401/struct/tables.html for details on cells with span = 0
1312     //after that position the cell normally, we do it to tell the cell where it is
1313     //and tell other cells where they can't be located (marking the cells as -1),
1314     //later taking the span into account (and in other function) the cell is
1315     //then painted (that's why we need to set the colspan and rowspan properly
1316     //when any of them is zero, so that the cell is properly painted)
1317 
1318     // make sure we have enough rows
1319     ensureRows(cRow + (rSpan ? rSpan : 1));
1320 
1321     grid[cRow].rowRenderer = row;
1322 
1323     int col = cCol;
1324     // tell the cell where it is
1325     RenderTableCell *set = cell;
1326 //     qCDebug(KHTML_LOG)<<"row"<<cRow<<"col"<<cCol;
1327 //     qCDebug(KHTML_LOG)<<"cSpan"<<cSpan<<"rSpan"<<rSpan;
1328 
1329     //check whether we need to update any of the cells with span = 0
1330     QList< int > columnsToAvoid;
1331     if (!cellsWithColSpanZero.isEmpty()) {
1332         //Update any column which its last span update was in a previous column
1333         int lowestCol = cellsWithColSpanZero.lowerBound(0).key();
1334         if (lowestCol < cCol) {
1335             //add the columns for this cell
1336             if (cCol >= nCols) {
1337                 table()->appendColumn(cSpan);
1338                 nCols = columns.size();
1339             }
1340             //check and update all the cells that are updated to a previous column
1341             while (lowestCol < nCols) {
1342                 while (RenderTableCell *cell = cellsWithColSpanZero.take(lowestCol)) {
1343                     const int cellRow = cell->row();
1344                     //const int cellRowSpan = cell->rowSpan();
1345                     int finalSpan = cell->colSpan();
1346                     for (int i = lowestCol; i < nCols; ++i) {
1347                         if (!cellAt(cellRow, i)) {
1348                             cellAt(cellRow, i) = (RenderTableCell *) - 1;
1349                         }
1350                         ++finalSpan;
1351                     }
1352                     cell->setColSpan(finalSpan);
1353                     cellsWithColSpanZero.insertMulti(nCols, cell);
1354                 }
1355                 lowestCol = cellsWithColSpanZero.lowerBound(0).key();
1356             }
1357         }
1358     }
1359 
1360     if (!cellsWithRowSpanZero.isEmpty()) {
1361         if (cellsWithRowSpanZero.contains(cRow)) {
1362             //No need to check if we have enough columns, we already found the first cell
1363             //when rowspan="0", and as such, we've already inserted it
1364             while (RenderTableCell *cell = cellsWithRowSpanZero.take(cRow)) {
1365                 const int cellCol = cell->col();
1366                 const int finalCol = cellCol + cell->colSpan() - 1;
1367                 RenderTableCell *set = cell;
1368                 for (int i = cellCol; i <= finalCol; ++i) {
1369                     if (!cellAt(cRow, i)) {
1370                         cellAt(cRow, i) = set;
1371                     }
1372                     set = (RenderTableCell *) - 1;
1373                     columnsToAvoid << i;
1374                 }
1375                 cell->setRowSpan(cell->rowSpan() + 1);
1376                 //mark it to be inserted in next row
1377                 cellsWithRowSpanZero.insertMulti(cRow + 1, cell);
1378             }
1379         }
1380     }
1381 
1382     //Save the column if the its span is 0
1383     if (!cSpan) {
1384         //Check if we only span in the current colgroup
1385         if (RenderTableCol *colgroup = table()->colElement(cCol)) {
1386             //Calculate the correct span and then handle the cell normally
1387 
1388             int firstColumnOfColgroup = cCol;
1389             while (--firstColumnOfColgroup >= 0 && colgroup == table()->colElement(firstColumnOfColgroup));
1390             ++firstColumnOfColgroup;
1391 
1392             int alreadyUsedSpan = 0;
1393             RenderTableCell *colgroupCell = cellAt(cRow, firstColumnOfColgroup + alreadyUsedSpan);
1394             while (firstColumnOfColgroup + alreadyUsedSpan < cCol) {
1395                 alreadyUsedSpan += colgroupCell->colSpan();
1396                 colgroupCell = cellAt(cRow, firstColumnOfColgroup + alreadyUsedSpan);
1397             }
1398 
1399             const int finalSpan = colgroup->span() - alreadyUsedSpan;
1400             cell->setColSpan(finalSpan);
1401 
1402             //We know exactly the cSpan so we can handle the cell as a normal cell
1403             //unless, of course, the rowspan is also 0
1404             cSpan = finalSpan;
1405         }
1406         //or if we span in the whole table and mark it as inserted till
1407         //this column and updated whenever another column is inserted
1408         else {
1409             //We define the proper span and let the other code handle it
1410             const int finalSpan = nCols - cCol;
1411             cSpan = finalSpan;
1412 
1413             cell->setColSpan(finalSpan);
1414 
1415             cellsWithColSpanZero.insertMulti(cCol + finalSpan - 1, cell);
1416         }
1417     }
1418 
1419     if (!rSpan) {
1420         //For now we span 1
1421         rSpan = 1;
1422         cell->setRowSpan(1);
1423 
1424         //mark it to be inserted in next row
1425         cellsWithRowSpanZero.insertMulti(cRow + 1, cell);
1426     }
1427 
1428     while (cSpan) {
1429         int currentSpan;
1430         if (cCol >= nCols) {
1431             table()->appendColumn(cSpan);
1432             currentSpan = cSpan;
1433         } else {
1434             if (cSpan < columns[cCol].span) {
1435                 table()->splitColumn(cCol, cSpan);
1436             }
1437             currentSpan = columns[cCol].span;
1438         }
1439 
1440         while (columnsToAvoid.contains(cCol)) {
1441             ++cCol;
1442         }
1443 
1444         int r = 0;
1445         while (r < rSpan) {
1446             if (!cellAt(cRow + r, cCol)) {
1447 //     qDebug("    adding cell at %d, %d",  cRow + r, cCol );
1448                 cellAt(cRow + r, cCol) = set;
1449             }
1450             ++r;
1451         }
1452         ++cCol;
1453         cSpan -= currentSpan;
1454         set = (RenderTableCell *) - 1;
1455     }
1456 
1457     if (cell) {
1458         cell->setRow(cRow);
1459         cell->setCol(table()->effColToCol(col));
1460     }
1461 }
1462 
1463 void RenderTableSection::setCellWidths()
1464 {
1465 #ifdef DEBUG_LAYOUT
1466     qCDebug(KHTML_LOG) << renderName() << "(Table, this=0x" << this << ")::setCellWidths()";
1467 #endif
1468     const QVector<int> &columnPos = table()->columnPos;
1469 
1470     int rows = grid.size();
1471     for (int i = 0; i < rows; i++) {
1472         Row &row = *grid[i].row;
1473         int cols = row.size();
1474         for (int j = 0; j < cols; j++) {
1475             RenderTableCell *cell = row[j];
1476 //      qDebug("cell[%d,%d] = %p", i, j, cell );
1477             if (!cell || cell == (RenderTableCell *) - 1) {
1478                 continue;
1479             }
1480             int endCol = j;
1481             int cspan = cell->colSpan();
1482             while (cspan && endCol < cols) {
1483                 cspan -= table()->columns[endCol].span;
1484                 endCol++;
1485             }
1486             int w = columnPos[endCol] - columnPos[j] - table()->borderHSpacing();
1487 #ifdef DEBUG_LAYOUT
1488             qCDebug(KHTML_LOG) << "setting width of cell " << cell << " " << cell->row() << "/" << cell->col() << " to " << w << " colspan=" << cell->colSpan() << " start=" << j << " end=" << endCol;
1489 #endif
1490             int oldWidth = cell->width();
1491             if (w != oldWidth) {
1492                 cell->setNeedsLayout(true);
1493                 cell->setWidth(w);
1494             }
1495         }
1496     }
1497 }
1498 
1499 void RenderTableSection::calcRowHeight()
1500 {
1501     int indx;
1502     RenderTableCell *cell;
1503 
1504     int totalRows = grid.size();
1505     int vspacing = table()->borderVSpacing();
1506 
1507     rowPos.resize(totalRows + 1);
1508     rowPos[0] =  vspacing + borderTop();
1509 
1510     for (int r = 0; r < totalRows; r++) {
1511         rowPos[r + 1] = 0;
1512 
1513         int baseline = 0;
1514         int bdesc = 0;
1515         grid[r].baseLine = 0;
1516 //  qDebug("height of row %d is %d/%d", r, grid[r].height.value, grid[r].height.type );
1517         int ch = grid[r].height.minWidth(0);
1518         int pos = rowPos[r] + ch + (grid[r].rowRenderer ? vspacing : 0);
1519 
1520         if (pos > rowPos[r + 1]) {
1521             rowPos[r + 1] = pos;
1522         }
1523 
1524         Row *row = grid[r].row;
1525         int totalCols = row->size();
1526         int totalRows = grid.size();
1527         bool pagedMode = canvas()->pagedMode();
1528 
1529         grid[r].needFlex = false;
1530 
1531         for (int c = 0; c < totalCols; c++) {
1532             cell = cellAt(r, c);
1533             if (!cell || cell == (RenderTableCell *) - 1) {
1534                 continue;
1535             }
1536             if (r < totalRows - 1 && cellAt(r + 1, c) == cell) {
1537                 continue;
1538             }
1539 
1540             if ((indx = r - cell->rowSpan() + 1) < 0) {
1541                 indx = 0;
1542             }
1543 
1544             if (cell->cellPercentageHeight() != -1) {
1545                 cell->setCellPercentageHeight(-1);
1546                 cell->setChildNeedsLayout(true, false);
1547                 if (cell->hasFlexedAnonymous()) {
1548                     for (RenderObject *o = cell->firstChild(); o; o = o->nextSibling())
1549                         if (o->isAnonymousBlock()) {
1550                             o->setChildNeedsLayout(true, false);
1551                         }
1552                 }
1553                 if (pagedMode) {
1554                     cell->setNeedsLayout(true);
1555                 }
1556                 cell->layoutIfNeeded();
1557             }
1558 
1559             ch = cell->style()->height().width(0);
1560             if (cell->height() > ch) {
1561                 ch = cell->height();
1562             }
1563 
1564             if (!cell->style()->height().isAuto()) {
1565                 grid[r].needFlex = true;
1566             }
1567 
1568             pos = rowPos[indx] + ch + (grid[r].rowRenderer ? vspacing : 0);
1569 
1570             if (pos > rowPos[r + 1]) {
1571                 rowPos[r + 1] = pos;
1572             }
1573 
1574             // find out the baseline
1575             EVerticalAlign va = cell->style()->verticalAlign();
1576             if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
1577                     || va == SUPER || va == SUB) {
1578                 int b = cell->baselinePosition();
1579                 if (b > cell->borderTop() + cell->paddingTop()) {
1580                     if (b > baseline) {
1581                         baseline = b;
1582                     }
1583 
1584                     int td = rowPos[ indx ] + ch - b;
1585                     if (td > bdesc) {
1586                         bdesc = td;
1587                     }
1588                 }
1589             }
1590         }
1591 
1592         //do we have baseline aligned elements?
1593         if (baseline) {
1594             // increase rowheight if baseline requires
1595             int bRowPos = baseline + bdesc + (grid[r].rowRenderer ? vspacing : 0);
1596             if (rowPos[r + 1] < bRowPos) {
1597                 rowPos[r + 1] = bRowPos;
1598             }
1599 
1600             grid[r].baseLine = baseline;
1601         }
1602 
1603         if (rowPos[r + 1] < rowPos[r]) {
1604             rowPos[r + 1] = rowPos[r];
1605         }
1606 //      qDebug("rowpos(%d)=%d",  r, rowPos[r] );
1607     }
1608 }
1609 
1610 int RenderTableSection::layoutRows(int toAdd)
1611 {
1612     int rHeight;
1613     int rindx;
1614     int totalRows = grid.size();
1615     int hspacing = table()->borderHSpacing();
1616     int vspacing = table()->borderVSpacing();
1617 
1618     // Set the width of our section now.  The rows will also be this width.
1619     m_width = table()->contentWidth();
1620 
1621     if (markedForRepaint()) {
1622         repaintDuringLayout();
1623         setMarkedForRepaint(false);
1624     }
1625 
1626     if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
1627 
1628         int totalHeight = rowPos[totalRows] + toAdd;
1629 //  qDebug("layoutRows: totalHeight = %d",  totalHeight );
1630 
1631         int dh = toAdd;
1632         int totalPercent = 0;
1633         int numAuto = 0;
1634         for (int r = 0; r < totalRows; r++) {
1635             if (grid[r].height.isAuto() && !emptyRow(r)) {
1636                 numAuto++;
1637             } else if (grid[r].height.isPercent()) {
1638                 totalPercent += grid[r].height.rawValue();
1639             }
1640         }
1641         if (totalPercent) {
1642 //      qDebug("distributing %d over percent rows totalPercent=%d", dh,  totalPercent );
1643             // try to satisfy percent
1644             int add = 0;
1645             if (totalPercent > 100 * PERCENT_SCALE_FACTOR) {
1646                 totalPercent = 100 * PERCENT_SCALE_FACTOR;
1647             }
1648             int rh = rowPos[1] - rowPos[0];
1649             for (int r = 0; r < totalRows; r++) {
1650                 if (totalPercent > 0 && grid[r].height.isPercent()) {
1651                     int toAdd = qMin(dh, (totalHeight * grid[r].height.rawValue() / (100 * PERCENT_SCALE_FACTOR)) - rh);
1652                     // If toAdd is negative, then we don't want to shrink the row (this bug
1653                     // affected Outlook Web Access).
1654                     toAdd = qMax(0, toAdd);
1655                     add += toAdd;
1656                     dh -= toAdd;
1657                     totalPercent -= grid[r].height.rawValue();
1658 //          qDebug( "adding %d to row %d", toAdd, r );
1659                 }
1660                 if (r < totalRows - 1) {
1661                     rh = rowPos[r + 2] - rowPos[r + 1];
1662                 }
1663                 rowPos[r + 1] += add;
1664             }
1665         }
1666         if (numAuto) {
1667             // distribute over non-empty variable rows
1668 //      qDebug("distributing %d over variable rows numAuto=%d", dh,  numAuto );
1669             int add = 0;
1670             int toAdd = dh / numAuto;
1671             for (int r = 0; r < totalRows; r++) {
1672                 if (grid[r].height.isAuto() && !emptyRow(r)) {
1673                     add += toAdd;
1674                 }
1675                 rowPos[r + 1] += add;
1676             }
1677             dh -= add;
1678         }
1679         if (dh > 0 && rowPos[totalRows]) {
1680             // if some left overs, distribute weighted.
1681             int tot = rowPos[totalRows];
1682             int add = 0;
1683             int prev = rowPos[0];
1684             for (int r = 0; r < totalRows; r++) {
1685                 //weight with the original height
1686                 add += dh * (rowPos[r + 1] - prev) / tot;
1687                 prev = rowPos[r + 1];
1688                 rowPos[r + 1] += add;
1689             }
1690             dh -= add;
1691         }
1692         if (dh > totalRows) {
1693             // distribute to tables with all empty rows
1694             int add = 0;
1695             int toAdd = dh / totalRows;
1696             for (int r = 0; r < totalRows; r++) {
1697                 add += toAdd;
1698                 rowPos[r + 1] += add;
1699             }
1700             dh -= add;
1701         }
1702         // Finally distribute round-off values
1703         if (dh > 0) {
1704             // There is not enough for every row
1705             int add = 0;
1706             for (int r = 0; r < totalRows; r++) {
1707                 if (add < dh) {
1708                     add++;
1709                 }
1710                 rowPos[r + 1] += add;
1711             }
1712             dh -= add;
1713         }
1714         assert(dh == 0);
1715     }
1716 
1717     int leftOffset = borderLeft() + hspacing;
1718 
1719     int nEffCols = table()->numEffCols();
1720     for (int r = 0; r < totalRows; r++) {
1721         Row *row = grid[r].row;
1722         int totalCols = row->size();
1723 
1724         // Set the row's x/y position and width/height.
1725         if (grid[r].rowRenderer) {
1726             grid[r].rowRenderer->setPos(0, rowPos[r]);
1727             grid[r].rowRenderer->setWidth(m_width);
1728             grid[r].rowRenderer->setHeight(rowPos[r + 1] - rowPos[r] - vspacing);
1729         }
1730 
1731         for (int c = 0; c < nEffCols; c++) {
1732             RenderTableCell *cell = cellAt(r, c);
1733             if (!cell || cell == (RenderTableCell *) - 1) {
1734                 continue;
1735             }
1736             if (r < totalRows - 1 && cell == cellAt(r + 1, c)) {
1737                 continue;
1738             }
1739 
1740             if ((rindx = r - cell->rowSpan() + 1) < 0) {
1741                 rindx = 0;
1742             }
1743 
1744             rHeight = rowPos[r + 1] - rowPos[rindx] - vspacing;
1745 
1746             // Force percent height children to lay themselves out again.
1747             // This will cause, e.g., textareas to grow to
1748             // fill the area.  FIXME: <div>s and blocks still don't
1749             // work right.  We'll need to have an efficient way of
1750             // invalidating all percent height objects in a render subtree.
1751             // For now, we just handle immediate children. -dwh
1752 
1753             bool flexAllChildren = grid[r].needFlex || (!table()->style()->height().isAuto() && rHeight != cell->height());
1754             cell->setHasFlexedAnonymous(false);
1755             if (flexAllChildren && flexCellChildren(cell)) {
1756                 cell->setCellPercentageHeight(qMax(0,
1757                                                    rHeight - cell->borderTop() - cell->paddingTop() -
1758                                                    cell->borderBottom() - cell->paddingBottom()));
1759                 cell->layoutIfNeeded();
1760 
1761                 // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
1762                 EVerticalAlign va = cell->style()->verticalAlign();
1763                 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) {
1764                     int b = cell->baselinePosition();
1765                     if (b > cell->borderTop() + cell->paddingTop()) {
1766                         grid[r].baseLine = qMax(grid[r].baseLine, b);
1767                     }
1768                 }
1769             }
1770             {
1771 #ifdef DEBUG_LAYOUT
1772                 qCDebug(KHTML_LOG) << "setting position " << r << "/" << c << ": "
1773                          << table()->columnPos[c] /*+ padding */ << "/" << rowPos[rindx] << " height=" << rHeight;
1774 #endif
1775 
1776                 EVerticalAlign va = cell->style()->verticalAlign();
1777                 int te = 0;
1778                 switch (va) {
1779                 case SUB:
1780                 case SUPER:
1781                 case TEXT_TOP:
1782                 case TEXT_BOTTOM:
1783                 case BASELINE:
1784                     te = getBaseline(r) - cell->baselinePosition();
1785                     break;
1786                 case TOP:
1787                     te = 0;
1788                     break;
1789                 case MIDDLE:
1790                     te = (rHeight - cell->height()) / 2;
1791                     break;
1792                 case BOTTOM:
1793                     te = rHeight - cell->height();
1794                     break;
1795                 default:
1796                     break;
1797                 }
1798                 te = qMax(0, te);
1799 #ifdef DEBUG_LAYOUT
1800                 qCDebug(KHTML_LOG) << "CELL " << cell << " te=" << te << ", be=" << rHeight - cell->height() - te << ", rHeight=" << rHeight << ", valign=" << va;
1801 #endif
1802                 cell->setCellTopExtra(te);
1803                 cell->setCellBottomExtra(rHeight - cell->height() - te);
1804             }
1805             if (style()->direction() == RTL) {
1806                 cell->setPos(
1807                     table()->columnPos[(int)totalCols] -
1808                     table()->columnPos[table()->colToEffCol(cell->col() + cell->colSpan())] +
1809                     leftOffset,
1810                     0);
1811             } else {
1812                 cell->setPos(table()->columnPos[c] + leftOffset, 0);
1813             }
1814         }
1815     }
1816 
1817     m_height = rowPos[totalRows];
1818     return m_height;
1819 }
1820 
1821 bool RenderTableSection::flexCellChildren(RenderObject *p) const
1822 {
1823     if (!p) {
1824         return false;
1825     }
1826     RenderObject *o = p->firstChild();
1827     bool didFlex = false;
1828     while (o) {
1829         if (!o->isText() && o->style()->height().isPercent()) {
1830             o->setNeedsLayout(true, false);
1831             p->setChildNeedsLayout(true, false);
1832             didFlex = true;
1833         } else if (o->isAnonymousBlock() && flexCellChildren(o)) {
1834             p->setChildNeedsLayout(true, false);
1835             if (p->isTableCell()) {
1836                 static_cast<RenderTableCell *>(p)->setHasFlexedAnonymous();
1837             }
1838             didFlex = true;
1839         }
1840         o = o->nextSibling();
1841     }
1842     return didFlex;
1843 }
1844 
1845 inline static RenderTableRow *firstTableRow(RenderObject *row)
1846 {
1847     while (row && !row->isTableRow()) {
1848         row = row->nextSibling();
1849     }
1850     return static_cast<RenderTableRow *>(row);
1851 }
1852 
1853 inline static RenderTableRow *nextTableRow(RenderObject *row)
1854 {
1855     row = row ? row->nextSibling() : row;
1856     while (row && !row->isTableRow()) {
1857         row = row->nextSibling();
1858     }
1859     return static_cast<RenderTableRow *>(row);
1860 }
1861 
1862 int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
1863 {
1864     int bottom = RenderBox::lowestPosition(includeOverflowInterior, includeSelf);
1865     if (!includeOverflowInterior && hasOverflowClip()) {
1866         return bottom;
1867     }
1868 
1869     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
1870         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
1871             if (cell->isTableCell()) {
1872                 int bp = row->yPos() + cell->lowestPosition(false);
1873                 bottom = qMax(bottom, bp);
1874             }
1875     }
1876 
1877     return bottom;
1878 }
1879 
1880 int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
1881 {
1882     int right = RenderBox::rightmostPosition(includeOverflowInterior, includeSelf);
1883     if (!includeOverflowInterior && hasOverflowClip()) {
1884         return right;
1885     }
1886 
1887     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
1888         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
1889             if (cell->isTableCell()) {
1890                 int rp = cell->xPos() + cell->rightmostPosition(false);
1891                 right = qMax(right, rp);
1892             }
1893     }
1894 
1895     return right;
1896 }
1897 
1898 int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
1899 {
1900     int left = RenderBox::leftmostPosition(includeOverflowInterior, includeSelf);
1901     if (!includeOverflowInterior && hasOverflowClip()) {
1902         return left;
1903     }
1904 
1905     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
1906         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
1907             if (cell->isTableCell()) {
1908                 int lp = cell->xPos() + cell->leftmostPosition(false);
1909                 left = qMin(left, lp);
1910             }
1911     }
1912 
1913     return left;
1914 }
1915 
1916 int RenderTableSection::highestPosition(bool includeOverflowInterior, bool includeSelf) const
1917 {
1918     int top = RenderBox::highestPosition(includeOverflowInterior, includeSelf);
1919     if (!includeOverflowInterior && hasOverflowClip()) {
1920         return top;
1921     }
1922 
1923     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
1924         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
1925             if (cell->isTableCell()) {
1926                 int hp = row->yPos() + cell->highestPosition(false);
1927                 top = qMin(top, hp);
1928             }
1929     }
1930 
1931     return top;
1932 }
1933 
1934 // Search from first_row to last_row for the row containing y
1935 static unsigned int findRow(unsigned int first_row, unsigned int last_row,
1936                             const QVector<int> &rowPos, int y)
1937 {
1938     unsigned int under = first_row;
1939     unsigned int over = last_row;
1940     int offset = (over - under) / 2;
1941     while (over - under > 1) {
1942         if (rowPos[under + offset] <= y) {
1943             under = under + offset;
1944         } else {
1945             over = under + offset;
1946         }
1947         offset = (over - under) / 2;
1948     }
1949 
1950     assert(under == first_row || rowPos[under] <= y);
1951     assert(over == last_row || rowPos[over] > y);
1952 
1953     return under;
1954 }
1955 
1956 static void findRowCover(unsigned int &startrow, unsigned int &endrow,
1957                          const QVector<int> &rowPos,
1958                          int min_y, int max_y)
1959 {
1960     assert(max_y >= min_y);
1961     unsigned int totalRows = endrow;
1962 
1963     unsigned int index = 0;
1964     // Initial binary search boost:
1965     if (totalRows >= 8) {
1966         int offset = (endrow - startrow) / 2;
1967         while (endrow - startrow > 1) {
1968             index = startrow + offset;
1969             if (rowPos[index] <= min_y)
1970                 // index is below both min_y and max_y
1971             {
1972                 startrow = index;
1973             } else if (rowPos[index] > max_y)
1974                 // index is above both min_y and max_y
1975             {
1976                 endrow = index;
1977             } else
1978                 // index is within the selection
1979             {
1980                 break;
1981             }
1982             offset = (endrow - startrow) / 2;
1983         }
1984     }
1985 
1986     // Binary search for startrow
1987     startrow = findRow(startrow, endrow, rowPos, min_y);
1988     // Binary search for endrow
1989     endrow = findRow(startrow, endrow, rowPos, max_y) + 1;
1990     if (endrow > totalRows) {
1991         endrow = totalRows;
1992     }
1993 }
1994 
1995 void RenderTableSection::paint(PaintInfo &pI, int tx, int ty)
1996 {
1997     unsigned int totalRows = grid.size();
1998     unsigned int totalCols = table()->columns.size();
1999 
2000     tx += m_x;
2001     ty += m_y;
2002 
2003     if (pI.phase == PaintActionOutline) {
2004         paintOutline(pI.p, tx, ty, width(), height(), style());
2005     }
2006 
2007     CollapsedBorderValue *cbs = table()->currentBorderStyle();
2008     int cbsw2 = cbs ? cbs->width() / 2 : 0;
2009     int cbsw21 = cbs ? (cbs->width() + 1) / 2 : 0;
2010 
2011     int x = pI.r.x(), y = pI.r.y(), w = pI.r.width(), h = pI.r.height();
2012     // check which rows and cols are visible and only paint these
2013     int os = 2 * maximalOutlineSize(pI.phase);
2014     unsigned int startrow = 0;
2015     unsigned int endrow = totalRows;
2016     findRowCover(startrow, endrow, rowPos, y - os - ty - qMax(cbsw21, os), y + h + os - ty + qMax(cbsw2, os));
2017 
2018     // A binary search is probably not worthwhile for columns
2019     unsigned int startcol = 0;
2020     unsigned int endcol = totalCols;
2021     if (style()->direction() == LTR) {
2022         for (; startcol < totalCols; startcol++) {
2023             if (tx + table()->columnPos[startcol + 1] + qMax(cbsw21, os) > x - os) {
2024                 break;
2025             }
2026         }
2027         for (; endcol > 0; endcol--) {
2028             if (tx + table()->columnPos[endcol - 1] - qMax(cbsw2, os) < x + w + os) {
2029                 break;
2030             }
2031         }
2032     }
2033     if (startcol < endcol) {
2034         // draw the cells
2035         for (unsigned int r = startrow; r < endrow; r++) {
2036             // paint the row
2037             if (grid[r].rowRenderer) {
2038                 int height = rowPos[r + 1] - rowPos[r] - table()->borderVSpacing();
2039                 grid[r].rowRenderer->paintRow(pI, tx, ty + rowPos[r], width(), height);
2040             }
2041 
2042             unsigned int c = startcol;
2043             Row *row = grid[r].row;
2044             Row *nextrow = (r < endrow - 1) ? grid[r + 1].row : nullptr;
2045             // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
2046             while (c && (*row)[c] == (RenderTableCell *) - 1) {
2047                 c--;
2048             }
2049             for (; c < endcol; c++) {
2050                 RenderTableCell *cell = (*row)[c];
2051                 if (!cell || cell == (RenderTableCell *) - 1 || (nextrow && (*nextrow)[c] == cell)) {
2052                     continue;
2053                 }
2054                 RenderObject *rowr = cell->parent();
2055                 int rtx = tx + rowr->xPos();
2056                 int rty = ty + rowr->yPos();
2057 #ifdef TABLE_PRINT
2058                 qCDebug(KHTML_LOG) << "painting cell " << r << "/" << c;
2059 #endif
2060                 if (pI.phase == PaintActionElementBackground || pI.phase == PaintActionChildBackground) {
2061                     // We need to handle painting a stack of backgrounds.  This stack (from bottom to top) consists of
2062                     // the column group, column, row group, row, and then the cell.
2063                     RenderObject *col = table()->colElement(c);
2064                     RenderObject *colGroup = nullptr;
2065                     if (col) {
2066                         RenderStyle *style = col->parent()->style();
2067                         if (style->display() == TABLE_COLUMN_GROUP) {
2068                             colGroup = col->parent();
2069                         }
2070                     }
2071 
2072                     // ###
2073                     // Column groups and columns first.
2074                     // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
2075                     // the stack, since we have already opened a transparency layer (potentially) for the table row group.
2076                     // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
2077                     // cell.
2078                     cell->paintBackgroundsBehindCell(pI, rtx, rty, colGroup);
2079                     cell->paintBackgroundsBehindCell(pI, rtx, rty, col);
2080 
2081                     // Paint the row group next.
2082                     cell->paintBackgroundsBehindCell(pI, rtx, rty, this);
2083 
2084                     // Paint the row next, but only if it doesn't have a layer.  If a row has a layer, it will be responsible for
2085                     // painting the row background for the cell.
2086                     if (!rowr->layer()) {
2087                         cell->paintBackgroundsBehindCell(pI, rtx, rty, rowr);
2088                     }
2089                 }
2090 
2091                 if ((!cell->layer() && !cell->parent()->layer()) || pI.phase == PaintActionCollapsedTableBorders) {
2092                     cell->paint(pI, rtx, rty);
2093                 }
2094             }
2095         }
2096     }
2097 }
2098 
2099 int RenderTableSection::numColumns() const
2100 {
2101     int result = 0;
2102 
2103     for (int r = 0; r < numRows(); ++r) {
2104         for (int c = result; c < table()->numEffCols(); ++c) {
2105             if (cellAt(r, c)) {
2106                 result = c;
2107             }
2108         }
2109     }
2110 
2111     return result + 1;
2112 }
2113 
2114 void RenderTableSection::recalcCells()
2115 {
2116     cCol = 0;
2117     cRow = -1;
2118     clearGrid();
2119     grid.resize(0);
2120     cellsWithColSpanZero.clear();
2121     cellsWithRowSpanZero.clear();
2122 
2123     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
2124         if (row->isTableRow())  {
2125             cRow++;
2126             cCol = 0;
2127             ensureRows(cRow + 1);
2128             grid[cRow].rowRenderer = static_cast<RenderTableRow *>(row);
2129 
2130             for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
2131                 if (cell->isTableCell()) {
2132                     addCell(static_cast<RenderTableCell *>(cell), static_cast<RenderTableRow *>(row));
2133                 }
2134 
2135         }
2136     }
2137     needCellRecalc = false;
2138     setNeedsLayout(true);
2139 }
2140 
2141 void RenderTableSection::clearGrid()
2142 {
2143     int rows = grid.size();
2144     while (rows--) {
2145         delete grid[rows].row;
2146     }
2147 }
2148 
2149 bool RenderTableSection::emptyRow(int rowNum)
2150 {
2151     Row &r = *grid[rowNum].row;
2152     const int s = r.size();
2153     RenderTableCell *cell;
2154     for (int i = 0; i < s; i++) {
2155         cell = r[i];
2156         if (!cell || cell == (RenderTableCell *) - 1) {
2157             continue;
2158         }
2159         return false;
2160     }
2161     return true;
2162 }
2163 
2164 RenderObject *RenderTableSection::removeChildNode(RenderObject *child)
2165 {
2166     setNeedCellRecalc();
2167     return RenderContainer::removeChildNode(child);
2168 }
2169 
2170 bool RenderTableSection::canClear(RenderObject * /*child*/, PageBreakLevel level)
2171 {
2172     // We cannot clear rows yet.
2173     return parent()->canClear(this, level);
2174 }
2175 
2176 void RenderTableSection::addSpaceAt(int pos, int dy)
2177 {
2178     const int totalRows = numRows();
2179     for (int r = 0; r < totalRows; r++) {
2180         if (rowPos[r] > pos) {
2181             rowPos[r] += dy;
2182             if (grid[r].rowRenderer) {
2183                 grid[r].rowRenderer->setPos(0, rowPos[r]);
2184             }
2185         }
2186     }
2187     if (rowPos[totalRows] > pos) {
2188         rowPos[totalRows] += dy;
2189     }
2190     m_height = rowPos[totalRows];
2191 
2192     setContainsPageBreak(true);
2193 }
2194 
2195 #ifdef ENABLE_DUMP
2196 void RenderTableSection::dump(QTextStream &stream, const QString &ind) const
2197 {
2198     RenderContainer::dump(stream, ind);
2199 
2200     stream << " grid=(" << grid.size() << "," << table()->numEffCols() << ")";
2201     for (int r = 0; r < grid.size(); r++) {
2202         for (int c = 0; c < table()->numEffCols(); c++) {
2203             if (cellAt(r,  c) && cellAt(r, c) != (RenderTableCell *) - 1)
2204                 stream << " (" << cellAt(r, c)->row() << "," << cellAt(r, c)->col() << ","
2205                        << cellAt(r, c)->rowSpan() << "," << cellAt(r, c)->colSpan() << ") ";
2206             else {
2207                 stream << " null cell";
2208             }
2209         }
2210     }
2211 }
2212 #endif
2213 
2214 static RenderTableCell *seekCell(RenderTableSection *section, int row, int col)
2215 {
2216     if (row < 0 || col < 0 || row >= section->numRows()) {
2217         return nullptr;
2218     }
2219     // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
2220     while (col && section->cellAt(row, col) == (RenderTableCell *) - 1) {
2221         col--;
2222     }
2223 
2224     return section->cellAt(row, col);
2225 }
2226 
2227 /** Looks for the first element suitable for text selection, beginning from
2228  * the last.
2229  * @param base search is restricted within this node. This node must have
2230  *  a renderer.
2231  * @return the element or @p base if no suitable element found.
2232  */
2233 static NodeImpl *findLastSelectableNode(NodeImpl *base)
2234 {
2235     NodeImpl *last = base;
2236     // Look for last text/cdata node that has a renderer,
2237     // or last childless replaced element
2238     while (last && !(last->renderer()
2239                      && ((last->nodeType() == Node::TEXT_NODE || last->nodeType() == Node::CDATA_SECTION_NODE)
2240                          || (last->renderer()->isReplaced() && !last->renderer()->lastChild())))) {
2241         NodeImpl *next = last->lastChild();
2242         if (!next) {
2243             next = last->previousSibling();
2244         }
2245         while (last && last != base && !next) {
2246             last = last->parentNode();
2247             if (last && last != base) {
2248                 next = last->previousSibling();
2249             }
2250         }
2251         last = next;
2252     }
2253 
2254     return last ? last : base;
2255 }
2256 
2257 FindSelectionResult RenderTableSection::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl *&node, int &offset, SelPointState &state)
2258 {
2259     Q_UNUSED(node);
2260     Q_UNUSED(offset);
2261     // Table sections need extra treatment for selections. The rows are scanned
2262     // from top to bottom, and within each row, only the cell that matches
2263     // the given position best is descended into.
2264 
2265     unsigned int totalRows = grid.size();
2266     unsigned int totalCols = table()->columns.size();
2267 
2268 //    absolutePosition(_tx, _ty, false);
2269 
2270     _tx += m_x;
2271     _ty += m_y;
2272 
2273 //    bool save_last = false;   // true to save last investigated cell
2274 
2275     if (needsLayout() || _y < _ty) {
2276         return SelectionPointBefore;
2277     }
2278 //    else if (_y >= _ty + height()) save_last = true;
2279 
2280     // Find the row containing the pointer
2281     int row_idx = findRow(0, totalRows, rowPos, _y - _ty);
2282 
2283     int col_idx;
2284     if (style()->direction() == LTR) {
2285         for (col_idx = (int)totalCols - 1; col_idx >= 0; col_idx--) {
2286             if (_tx + table()->columnPos[col_idx] < _x) {
2287                 break;
2288             }
2289         }
2290         if (col_idx < 0) {
2291             col_idx = 0;
2292         }
2293     } else {
2294         for (col_idx = 0; col_idx < (int)totalCols; col_idx++) {
2295             if (_tx + table()->columnPos[col_idx] > _x) {
2296                 break;
2297             }
2298         }
2299         if (col_idx >= (int)totalCols) {
2300             col_idx = (int)totalCols - 1;
2301         }
2302     }
2303 
2304     FindSelectionResult pos = SelectionPointBefore;
2305 
2306     RenderTableCell *cell = seekCell(this, row_idx, col_idx);
2307     // ### dunno why cell can be 0, maybe due to weird spans? (LS)
2308     if (cell) {
2309         SelPointState localState;
2310 //        pos = cell->checkSelectionPoint(_x, _y, _tx, _ty, node, offset, localState);
2311     }
2312 
2313     if (pos != SelectionPointBefore) {
2314         return pos;
2315     }
2316 
2317     // store last column of last line
2318     row_idx--;
2319     col_idx = totalCols - 1;
2320     cell = seekCell(this, row_idx, col_idx);
2321 
2322     // end of section? take previous section
2323     RenderTableSection *sec = this;
2324     if (!cell) {
2325         sec = *--TableSectionIterator(sec);
2326         if (!sec) {
2327             return pos;
2328         }
2329 
2330         cell = seekCell(sec, sec->grid.size() - 1, col_idx);
2331         if (!cell) {
2332             return pos;
2333         }
2334     }
2335 
2336     // take last child of previous cell, and store this one as last node
2337     NodeImpl *element = cell->element();
2338     if (!element) {
2339         return SelectionPointBefore;
2340     }
2341 
2342     element = findLastSelectableNode(element);
2343 
2344     state.m_lastNode = element;
2345     state.m_lastOffset = element->maxOffset();
2346     return SelectionPointBefore;
2347 }
2348 
2349 // Hit Testing
2350 bool RenderTableSection::nodeAtPoint(NodeInfo &info, int x, int y, int tx, int ty, HitTestAction action, bool inside)
2351 {
2352     // Table sections cannot ever be hit tested.  Effectively they do not exist.
2353     // Just forward to our children always.
2354     tx += m_x;
2355     ty += m_y;
2356 
2357     for (RenderObject *child = lastChild(); child; child = child->previousSibling()) {
2358         // FIXME: We have to skip over inline flows, since they can show up inside table rows
2359         // at the moment (a demoted inline <form> for example). If we ever implement a
2360         // table-specific hit-test method (which we should do for performance reasons anyway),
2361         // then we can remove this check.
2362         if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) {
2363             return true;
2364         }
2365     }
2366 
2367     return false;
2368 }
2369 
2370 // -------------------------------------------------------------------------
2371 
2372 RenderTableRow::RenderTableRow(DOM::NodeImpl *node)
2373     : RenderBox(node)
2374 {
2375     // init RenderObject attributes
2376     setInline(false);   // our object is not Inline
2377 }
2378 
2379 RenderObject *RenderTableRow::removeChildNode(RenderObject *child)
2380 {
2381     RenderTableSection *s = section();
2382     if (s) {
2383         s->setNeedCellRecalc();
2384     }
2385 
2386     return RenderContainer::removeChildNode(child);
2387 }
2388 
2389 void RenderTableRow::detach()
2390 {
2391     RenderTableSection *s = section();
2392     if (s) {
2393         s->setNeedCellRecalc();
2394     }
2395 
2396     RenderContainer::detach();
2397 }
2398 
2399 void RenderTableRow::setStyle(RenderStyle *newStyle)
2400 {
2401     if (section() && style() && style()->height() != newStyle->height()) {
2402         section()->setNeedCellRecalc();
2403     }
2404 
2405     newStyle->setDisplay(TABLE_ROW);
2406     RenderContainer::setStyle(newStyle);
2407 }
2408 
2409 void RenderTableRow::addChild(RenderObject *child, RenderObject *beforeChild)
2410 {
2411 #ifdef DEBUG_LAYOUT
2412     qCDebug(KHTML_LOG) << renderName() << "(TableRow)::addChild( " << child->renderName() << " )"  << ", " <<
2413              (beforeChild ? beforeChild->renderName() : "0") << " )";
2414 #endif
2415 
2416     if (!child->isTableCell()) {
2417         // TR > FORM quirk (???)
2418         if (child->element() && child->element()->isHTMLElement() && child->element()->id() == ID_FORM &&
2419                 static_cast<HTMLFormElementImpl *>(child->element())->isMalformed()) {
2420             RenderContainer::addChild(child, beforeChild);
2421             return;
2422         }
2423 
2424         RenderObject *last = beforeChild;
2425         if (!last) {
2426             last = lastChild();
2427         }
2428         if (last && last->isAnonymous() && last->isTableCell()) {
2429             last->addChild(child);
2430             return;
2431         }
2432 
2433         // If beforeChild is inside an anonymous cell, insert into the cell.
2434         if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous()) {
2435             last->parent()->addChild(child, beforeChild);
2436             return;
2437         }
2438 
2439         RenderTableCell *cell = new(renderArena()) RenderTableCell(document() /* anonymous object */);
2440         RenderStyle *newStyle = new RenderStyle();
2441         newStyle->inheritFrom(style());
2442         newStyle->setDisplay(TABLE_CELL);
2443         cell->setStyle(newStyle);
2444         addChild(cell, beforeChild);
2445         cell->addChild(child);
2446         return;
2447     }
2448 
2449     RenderTableCell *cell = static_cast<RenderTableCell *>(child);
2450 
2451     section()->addCell(cell, this);
2452 
2453     RenderContainer::addChild(cell, beforeChild);
2454 
2455     if (beforeChild || nextSibling()) {
2456         section()->setNeedCellRecalc();
2457     }
2458 }
2459 
2460 void RenderTableRow::layout()
2461 {
2462     KHTMLAssert(needsLayout());
2463     KHTMLAssert(minMaxKnown());
2464 
2465     RenderObject *child = firstChild();
2466     const bool pagedMode = canvas()->pagedMode();
2467     while (child) {
2468         if (child->isTableCell()) {
2469             RenderTableCell *cell = static_cast<RenderTableCell *>(child);
2470             if (pagedMode) {
2471                 cell->setNeedsLayout(true);
2472                 int oldHeight = child->height();
2473                 cell->layout();
2474                 if (oldHeight > 0 && child->containsPageBreak() && child->height() != oldHeight) {
2475                     // child has grown to accommodate a page-page, grow the same amount ourselves,
2476                     // and shift following rows down without relayouting the entire table
2477                     int adjust = child->height() - oldHeight;
2478                     setHeight(height() + adjust);
2479                     section()->addSpaceAt(yPos() + 1, adjust);
2480                 }
2481             } else if (child->needsLayout()) {
2482                 if (markedForRepaint()) {
2483                     cell->setMarkedForRepaint(true);
2484                 }
2485                 cell->calcVerticalMargins();
2486                 cell->layout();
2487                 cell->setCellTopExtra(0);
2488                 cell->setCellBottomExtra(0);
2489                 if (child->containsPageBreak()) {
2490                     setContainsPageBreak(true);
2491                 }
2492             }
2493         }
2494         child = child->nextSibling();
2495     }
2496     setMarkedForRepaint(false);
2497     setNeedsLayout(false);
2498 }
2499 
2500 int RenderTableRow::offsetLeft() const
2501 {
2502     RenderObject *child = firstChild();
2503     while (child && !child->isTableCell()) {
2504         child = child->nextSibling();
2505     }
2506 
2507     if (!child) {
2508         return 0;
2509     }
2510 
2511     return child->offsetLeft();
2512 }
2513 
2514 int RenderTableRow::offsetTop() const
2515 {
2516     RenderObject *child = firstChild();
2517     while (child && !child->isTableCell()) {
2518         child = child->nextSibling();
2519     }
2520 
2521     if (!child) {
2522         return 0;
2523     }
2524 
2525     return child->offsetTop() -
2526            static_cast<RenderTableCell *>(child)->cellTopExtra();
2527 }
2528 
2529 int RenderTableRow::offsetHeight() const
2530 {
2531     RenderObject *child = firstChild();
2532     while (child && !child->isTableCell()) {
2533         child = child->nextSibling();
2534     }
2535 
2536     if (!child) {
2537         return 0;
2538     }
2539 
2540     return child->offsetHeight();
2541 }
2542 
2543 short RenderTableRow::offsetWidth() const
2544 {
2545     RenderObject *fc = firstChild();
2546     RenderObject *lc = lastChild();
2547     while (fc && !fc->isTableCell()) {
2548         fc = fc->nextSibling();
2549     }
2550     while (lc && !lc->isTableCell()) {
2551         lc = lc->previousSibling();
2552     }
2553     if (!lc || !fc) {
2554         return 0;
2555     }
2556 
2557     return lc->xPos() + lc->width() - fc->xPos();
2558 }
2559 
2560 void RenderTableRow::paintRow(PaintInfo &pI, int tx, int ty, int w, int h)
2561 {
2562     if (pI.phase == PaintActionOutline) {
2563         paintOutline(pI.p, tx, ty, w, h, style());
2564     }
2565 }
2566 
2567 void RenderTableRow::paint(PaintInfo &i, int tx, int ty)
2568 {
2569     KHTMLAssert(layer());
2570     if (!layer()) {
2571         return;
2572     }
2573 
2574     tx += m_x;
2575     ty += m_y;
2576 
2577     for (RenderObject *child = firstChild(); child; child = child->nextSibling()) {
2578         if (child->isTableCell()) {
2579             // Paint the row background behind the cell.
2580             if (i.phase == PaintActionElementBackground || i.phase == PaintActionChildBackground) {
2581                 RenderTableCell *cell = static_cast<RenderTableCell *>(child);
2582                 cell->paintBackgroundsBehindCell(i, tx, ty, this);
2583             }
2584             if (!child->layer()) {
2585                 child->paint(i, tx, ty);
2586             }
2587         }
2588     }
2589 }
2590 
2591 // Hit Testing
2592 bool RenderTableRow::nodeAtPoint(NodeInfo &info, int x, int y, int tx, int ty, HitTestAction action, bool inside)
2593 {
2594     // Table rows cannot ever be hit tested.  Effectively they do not exist.
2595     // Just forward to our children always.
2596     tx += m_x;
2597     ty += m_y;
2598 
2599     for (RenderObject *child = lastChild(); child; child = child->previousSibling()) {
2600         // FIXME: We have to skip over inline flows, since they can show up inside table rows
2601         // at the moment (a demoted inline <form> for example). If we ever implement a
2602         // table-specific hit-test method (which we should do for performance reasons anyway),
2603         // then we can remove this check.
2604         if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action, inside)) {
2605             return true;
2606         }
2607     }
2608 
2609     return false;
2610 }
2611 
2612 // -------------------------------------------------------------------------
2613 
2614 RenderTableCell::RenderTableCell(DOM::NodeImpl *_node)
2615     : RenderBlock(_node)
2616 {
2617     _col = -1;
2618     _row = -1;
2619     cSpan = 1;
2620     rSpan = 1;
2621     updateFromElement();
2622     setShouldPaintBackgroundOrBorder(true);
2623     _topExtra = 0;
2624     _bottomExtra = 0;
2625     m_percentageHeight = -1;
2626     m_hasFlexedAnonymous = false;
2627     m_widthChanged = false;
2628 }
2629 
2630 void RenderTableCell::detach()
2631 {
2632     if (parent() && section()) {
2633         section()->setNeedCellRecalc();
2634     }
2635 
2636     RenderBlock::detach();
2637 }
2638 
2639 void RenderTableCell::updateFromElement()
2640 {
2641     DOM::NodeImpl *node = element();
2642     if (node && (node->id() == ID_TD || node->id() == ID_TH)) {
2643         DOM::HTMLTableCellElementImpl *tc = static_cast<DOM::HTMLTableCellElementImpl *>(node);
2644         int oldCSpan = cSpan;
2645         int oldRSpan = rSpan;
2646 
2647         cSpan = tc->colSpan();
2648         rSpan = tc->rowSpan();
2649         if ((oldRSpan != rSpan || oldCSpan != cSpan) && style() && parent()) {
2650             setNeedsLayoutAndMinMaxRecalc();
2651             if (section()) {
2652                 section()->setNeedCellRecalc();
2653             }
2654         }
2655     } else {
2656         cSpan = rSpan = 1;
2657     }
2658 }
2659 
2660 Length RenderTableCell::styleOrColWidth()
2661 {
2662     Length w = style()->width();
2663     if (colSpan() > 1 || !w.isAuto()) {
2664         return w;
2665     }
2666     RenderTableCol *col = table()->colElement(_col);
2667     if (col) {
2668         w = col->style()->width();
2669 
2670         // Column widths specified on <col> apply to the border box of the cell.
2671         // Percentages don't need to be handled since they're always treated this way (even when specified on the cells).
2672         if (w.isFixed() && w.isPositive()) {
2673             w = Length(qMax(0, w.value() - borderLeft() - borderRight() - paddingLeft() - paddingRight()), Fixed);
2674         }
2675     }
2676     return w;
2677 }
2678 
2679 void RenderTableCell::calcMinMaxWidth()
2680 {
2681     KHTMLAssert(!minMaxKnown());
2682 #ifdef DEBUG_LAYOUT
2683     qCDebug(KHTML_LOG) << renderName() << "(TableCell)::calcMinMaxWidth() known=" << minMaxKnown();
2684 #endif
2685 
2686     if (section()->needCellRecalc) {
2687         section()->recalcCells();
2688     }
2689 
2690     RenderBlock::calcMinMaxWidth();
2691     if (element() && style()->whiteSpace() == NORMAL) {
2692         // See if nowrap was set.
2693         Length w = styleOrColWidth();
2694         DOMString nowrap = static_cast<ElementImpl *>(element())->getAttribute(ATTR_NOWRAP);
2695         if (!nowrap.isNull() && w.isFixed() &&
2696                 m_minWidth < w.value())
2697             // Nowrap is set, but we didn't actually use it because of the
2698             // fixed width set on the cell.  Even so, it is a WinIE/Moz trait
2699             // to make the minwidth of the cell into the fixed width.  They do this
2700             // even in strict mode, so do not make this a quirk.  Affected the top
2701             // of hiptop.com.
2702         {
2703             m_minWidth = w.value();
2704         }
2705     }
2706 
2707     setMinMaxKnown();
2708 }
2709 
2710 void RenderTableCell::calcWidth()
2711 {
2712 }
2713 
2714 void RenderTableCell::setWidth(int width)
2715 {
2716     if (width != m_width) {
2717         m_width = width;
2718         m_widthChanged = true;
2719     }
2720 }
2721 
2722 void RenderTableCell::layout()
2723 {
2724     layoutBlock(m_widthChanged);
2725     m_widthChanged = false;
2726 }
2727 
2728 void RenderTableCell::close()
2729 {
2730     RenderBlock::close();
2731 
2732 #ifdef DEBUG_LAYOUT
2733     qCDebug(KHTML_LOG) << renderName() << "(RenderTableCell)::close() total height =" << m_height;
2734 #endif
2735 }
2736 
2737 bool RenderTableCell::requiresLayer() const
2738 {
2739     // table-cell display is never positioned (css 2.1-9.7)
2740     return style()->opacity() < 1.0f || hasOverflowClip() || isRelPositioned();
2741 }
2742 
2743 void RenderTableCell::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
2744 {
2745     RenderBlock::repaintRectangle(x, y, w, h + _topExtra + _bottomExtra, p, f);
2746 }
2747 
2748 int RenderTableCell::pageTopAfter(int y) const
2749 {
2750     return parent()->pageTopAfter(y + m_y + _topExtra) - (m_y  + _topExtra);
2751 }
2752 
2753 short RenderTableCell::baselinePosition(bool) const
2754 {
2755     RenderObject *o = firstChild();
2756     int offset = paddingTop() + borderTop();
2757     if (!o) {
2758         return offset + contentHeight();
2759     }
2760     while (o->firstChild()) {
2761         if (!o->isInline()) {
2762             offset += o->paddingTop() + o->borderTop();
2763         }
2764         o = o->firstChild();
2765     }
2766 
2767     if (!o->isInline()) {
2768         return paddingTop() + borderTop() + contentHeight();
2769     }
2770 
2771     offset += o->baselinePosition(true);
2772     return offset;
2773 }
2774 
2775 void RenderTableCell::setStyle(RenderStyle *newStyle)
2776 {
2777     if (parent() && section() && style() && style()->height() != newStyle->height()) {
2778         section()->setNeedCellRecalc();
2779     }
2780 
2781     newStyle->setDisplay(TABLE_CELL);
2782     RenderBlock::setStyle(newStyle);
2783     setShouldPaintBackgroundOrBorder(true);
2784 
2785     if (newStyle->whiteSpace() == KHTML_NOWRAP) {
2786         // Figure out if we are really nowrapping or if we should just
2787         // use normal instead.  If the width of the cell is fixed, then
2788         // we don't actually use NOWRAP.
2789         if (newStyle->width().isFixed()) {
2790             newStyle->setWhiteSpace(NORMAL);
2791         } else {
2792             newStyle->setWhiteSpace(NOWRAP);
2793         }
2794     }
2795 }
2796 
2797 bool RenderTableCell::nodeAtPoint(NodeInfo &info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
2798 {
2799     int tx = _tx + m_x;
2800     int ty = _ty + m_y;
2801 
2802     // also include the top and bottom extra space
2803     inside |= hitTestAction != HitTestChildrenOnly && style()->visibility() != HIDDEN
2804               && (_y >= ty) && (_y < ty + height() + _topExtra + _bottomExtra)
2805               && (_x >= tx) && (_x < tx + width());
2806 
2807     return RenderBlock::nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction, inside);
2808 }
2809 
2810 // The following rules apply for resolving conflicts and figuring out which border
2811 // to use.
2812 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting
2813 // borders. Any border with this value suppresses all borders at this location.
2814 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all
2815 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is
2816 // the default value for the border style.)
2817 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders
2818 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred
2819 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
2820 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row,
2821 // which wins over a row group, column, column group and, lastly, table. It is undefined which color
2822 // is used when two elements of the same type disagree.
2823 static CollapsedBorderValue compareBorders(const CollapsedBorderValue &border1,
2824         const CollapsedBorderValue &border2)
2825 {
2826     // Sanity check the values passed in.  If either is null, return the other.
2827     if (!border2.exists()) {
2828         return border1;
2829     }
2830     if (!border1.exists()) {
2831         return border2;
2832     }
2833 
2834     // Rule #1 above.
2835     if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) {
2836         return CollapsedBorderValue();    // No border should exist at this location.
2837     }
2838 
2839     // Rule #2 above.  A style of 'none' has lowest priority and always loses to any other border.
2840     if (border2.style() == BNONE) {
2841         return border1;
2842     }
2843     if (border1.style() == BNONE) {
2844         return border2;
2845     }
2846 
2847     // The first part of rule #3 above. Wider borders win.
2848     if (border1.width() != border2.width()) {
2849         return border1.width() > border2.width() ? border1 : border2;
2850     }
2851 
2852     // The borders have equal width.  Sort by border style.
2853     if (border1.style() != border2.style()) {
2854         return border1.style() > border2.style() ? border1 : border2;
2855     }
2856 
2857     // The border have the same width and style.  Rely on precedence (cell over row over row group, etc.)
2858     return border1.precedence >= border2.precedence ? border1 : border2;
2859 }
2860 
2861 CollapsedBorderValue RenderTableCell::collapsedLeftBorder(bool rtl) const
2862 {
2863     RenderTable *tableElt = table();
2864     bool leftmostColumn;
2865     if (!rtl) {
2866         leftmostColumn = col() == 0;
2867     } else {
2868         int effCol = tableElt->colToEffCol(col() + colSpan() - 1);
2869         leftmostColumn = effCol == tableElt->numEffCols() - 1;
2870     }
2871 
2872     // For border left, we need to check, in order of precedence:
2873     // (1) Our left border.
2874     CollapsedBorderValue result(&style()->borderLeft(), BCELL);
2875 
2876     // (2) The right border of the cell to the left.
2877     RenderTableCell *prevCell = rtl ? tableElt->cellAfter(this) : tableElt->cellBefore(this);
2878     if (prevCell) {
2879         result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL));
2880         if (!result.exists()) {
2881             return result;
2882         }
2883     } else if (leftmostColumn) {
2884         // (3) Our row's left border.
2885         result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW));
2886         if (!result.exists()) {
2887             return result;
2888         }
2889 
2890         // (4) Our row group's left border.
2891         result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderLeft(), BROWGROUP));
2892         if (!result.exists()) {
2893             return result;
2894         }
2895     }
2896 
2897     // (5) Our column and column group's left borders.
2898     bool startColEdge;
2899     bool endColEdge;
2900     RenderTableCol *colElt = table()->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge);
2901     if (colElt && (!rtl ? startColEdge : endColEdge)) {
2902         result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
2903         if (!result.exists()) {
2904             return result;
2905         }
2906         if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) {
2907             result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), BCOLGROUP));
2908             if (!result.exists()) {
2909                 return result;
2910             }
2911         }
2912     }
2913 
2914     // (6) The previous column's right border.
2915     if (!leftmostColumn) {
2916         colElt = table()->colElement(col() + (rtl ? colSpan() : -1), &startColEdge, &endColEdge);
2917         if (colElt && (!rtl ? endColEdge : startColEdge)) {
2918             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
2919             if (!result.exists()) {
2920                 return result;
2921             }
2922         }
2923     } else {
2924         // (7) The table's left border.
2925         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderLeft(), BTABLE));
2926         if (!result.exists()) {
2927             return result;
2928         }
2929     }
2930 
2931     return result;
2932 }
2933 
2934 CollapsedBorderValue RenderTableCell::collapsedRightBorder(bool rtl) const
2935 {
2936     RenderTable *tableElt = table();
2937     bool rightmostColumn;
2938     if (rtl) {
2939         rightmostColumn = col() == 0;
2940     } else {
2941         int effCol = tableElt->colToEffCol(col() + colSpan() - 1);
2942         rightmostColumn = effCol == tableElt->numEffCols() - 1;
2943     }
2944 
2945     // For border right, we need to check, in order of precedence:
2946     // (1) Our right border.
2947     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL);
2948 
2949     // (2) The left border of the cell to the right.
2950     if (!rightmostColumn) {
2951         RenderTableCell *nextCell = rtl ? tableElt->cellBefore(this) : tableElt->cellAfter(this);
2952         if (nextCell && nextCell->style()) {
2953             result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL));
2954             if (!result.exists()) {
2955                 return result;
2956             }
2957         }
2958     } else {
2959         // (3) Our row's right border.
2960         result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW));
2961         if (!result.exists()) {
2962             return result;
2963         }
2964 
2965         // (4) Our row group's right border.
2966         result = compareBorders(result, CollapsedBorderValue(&section()->style()->borderRight(), BROWGROUP));
2967         if (!result.exists()) {
2968             return result;
2969         }
2970     }
2971 
2972     // (5) Our column and column group's right borders.
2973     bool startColEdge;
2974     bool endColEdge;
2975     RenderTableCol *colElt = tableElt->colElement(col() + (rtl ? 0 : colSpan() - 1), &startColEdge, &endColEdge);
2976     if (colElt && (!rtl ? endColEdge : startColEdge)) {
2977         result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL));
2978         if (!result.exists()) {
2979             return result;
2980         }
2981         if (colElt->parent()->isTableCol() && (!rtl ? !colElt->nextSibling() : !colElt->previousSibling())) {
2982             result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderRight(), BCOLGROUP));
2983             if (!result.exists()) {
2984                 return result;
2985             }
2986         }
2987     }
2988 
2989     // (6) The next column's left border.
2990     if (!rightmostColumn) {
2991         colElt = tableElt->colElement(col() + (rtl ? -1 : colSpan()), &startColEdge, &endColEdge);
2992         if (colElt && (!rtl ? startColEdge : endColEdge)) {
2993             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL));
2994             if (!result.exists()) {
2995                 return result;
2996             }
2997         }
2998     } else {
2999         // (7) The table's right border.
3000         result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE));
3001         if (!result.exists()) {
3002             return result;
3003         }
3004     }
3005 
3006     return result;
3007 }
3008 
3009 CollapsedBorderValue RenderTableCell::collapsedTopBorder() const
3010 {
3011     // For border top, we need to check, in order of precedence:
3012     // (1) Our top border.
3013     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL);
3014 
3015     RenderTableCell *prevCell = table()->cellAbove(this);
3016     if (prevCell) {
3017         // (2) A previous cell's bottom border.
3018         result = compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL));
3019         if (!result.exists()) {
3020             return result;
3021         }
3022     }
3023 
3024     // (3) Our row's top border.
3025     result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW));
3026     if (!result.exists()) {
3027         return result;
3028     }
3029 
3030     // (4) The previous row's bottom border.
3031     if (prevCell) {
3032         RenderObject *prevRow = nullptr;
3033         if (prevCell->section() == section()) {
3034             prevRow = parent()->previousSibling();
3035         } else {
3036             prevRow = prevCell->section()->lastChild();
3037         }
3038 
3039         if (prevRow) {
3040             result = compareBorders(result, CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW));
3041             if (!result.exists()) {
3042                 return result;
3043             }
3044         }
3045     }
3046 
3047     // Now check row groups.
3048     RenderTableSection *currSection = section();
3049     if (row() == 0) {
3050         // (5) Our row group's top border.
3051         result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
3052         if (!result.exists()) {
3053             return result;
3054         }
3055 
3056         // (6) Previous row group's bottom border.
3057         currSection = table()->sectionAbove(currSection);
3058         if (currSection) {
3059             result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
3060             if (!result.exists()) {
3061                 return result;
3062             }
3063         }
3064     }
3065 
3066     if (!currSection) {
3067         // (8) Our column and column group's top borders.
3068         RenderTableCol *colElt = table()->colElement(col());
3069         if (colElt) {
3070             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL));
3071             if (!result.exists()) {
3072                 return result;
3073             }
3074             if (colElt->parent()->isTableCol()) {
3075                 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderTop(), BCOLGROUP));
3076                 if (!result.exists()) {
3077                     return result;
3078                 }
3079             }
3080         }
3081 
3082         // (9) The table's top border.
3083         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE));
3084         if (!result.exists()) {
3085             return result;
3086         }
3087     }
3088 
3089     return result;
3090 }
3091 
3092 CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const
3093 {
3094     // For border top, we need to check, in order of precedence:
3095     // (1) Our bottom border.
3096     CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL);
3097 
3098     RenderTableCell *nextCell = table()->cellBelow(this);
3099     if (nextCell) {
3100         // (2) A following cell's top border.
3101         result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL));
3102         if (!result.exists()) {
3103             return result;
3104         }
3105     }
3106 
3107     // (3) Our row's bottom border. (FIXME: Deal with rowspan!)
3108     result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW));
3109     if (!result.exists()) {
3110         return result;
3111     }
3112 
3113     // (4) The next row's top border.
3114     if (nextCell) {
3115         result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW));
3116         if (!result.exists()) {
3117             return result;
3118         }
3119     }
3120 
3121     // Now check row groups.
3122     RenderTableSection *currSection = section();
3123     if (row() + rowSpan() >= static_cast<RenderTableSection *>(currSection)->numRows()) {
3124         // (5) Our row group's bottom border.
3125         result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP));
3126         if (!result.exists()) {
3127             return result;
3128         }
3129 
3130         // (6) Following row group's top border.
3131         currSection = table()->sectionBelow(currSection);
3132         if (currSection) {
3133             result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP));
3134             if (!result.exists()) {
3135                 return result;
3136             }
3137         }
3138     }
3139 
3140     if (!currSection) {
3141         // (8) Our column and column group's bottom borders.
3142         RenderTableCol *colElt = table()->colElement(col());
3143         if (colElt) {
3144             result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL));
3145             if (!result.exists()) {
3146                 return result;
3147             }
3148             if (colElt->parent()->isTableCol()) {
3149                 result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderBottom(), BCOLGROUP));
3150                 if (!result.exists()) {
3151                     return result;
3152                 }
3153             }
3154         }
3155 
3156         // (9) The table's bottom border.
3157         result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE));
3158         if (!result.exists()) {
3159             return result;
3160         }
3161     }
3162 
3163     return result;
3164 }
3165 
3166 int RenderTableCell::borderLeft() const
3167 {
3168     if (table()->collapseBorders()) {
3169         CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL);
3170         if (border.exists()) {
3171             return (border.width() + 1) / 2;    // Give the extra pixel to top and left.
3172         }
3173         return 0;
3174     }
3175     return RenderBlock::borderLeft();
3176 }
3177 
3178 int RenderTableCell::borderRight() const
3179 {
3180     if (table()->collapseBorders()) {
3181         CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL);
3182         if (border.exists()) {
3183             return border.width() / 2;
3184         }
3185         return 0;
3186     }
3187     return RenderBlock::borderRight();
3188 }
3189 
3190 int RenderTableCell::borderTop() const
3191 {
3192     if (table()->collapseBorders()) {
3193         CollapsedBorderValue border = collapsedTopBorder();
3194         if (border.exists()) {
3195             return (border.width() + 1) / 2;    // Give the extra pixel to top and left.
3196         }
3197         return 0;
3198     }
3199     return RenderBlock::borderTop();
3200 }
3201 
3202 int RenderTableCell::borderBottom() const
3203 {
3204     if (table()->collapseBorders()) {
3205         CollapsedBorderValue border = collapsedBottomBorder();
3206         if (border.exists()) {
3207             return border.width() / 2;
3208         }
3209         return 0;
3210     }
3211     return RenderBlock::borderBottom();
3212 }
3213 
3214 #ifdef BOX_DEBUG
3215 #include <qpainter.h>
3216 
3217 static void outlineBox(QPainter *p, int _tx, int _ty, int w, int h)
3218 {
3219     p->setPen(QPen(QColor("yellow"), 3, Qt::DotLine));
3220     p->setBrush(Qt::NoBrush);
3221     p->drawRect(_tx, _ty, w, h);
3222 }
3223 #endif
3224 
3225 void RenderTableCell::paint(PaintInfo &pI, int _tx, int _ty)
3226 {
3227 
3228 #ifdef TABLE_PRINT
3229     qCDebug(KHTML_LOG) << renderName() << "(RenderTableCell)::paint() w/h = (" << width() << "/" << height() << ")" << " _y/_h=" << pI.r.y() << "/" << pI.r.height();
3230 #endif
3231 
3232     if (needsLayout()) {
3233         return;
3234     }
3235 
3236     _tx += m_x;
3237     _ty += m_y/* + _topExtra*/;
3238 
3239     RenderTable *tbl = table();
3240 
3241     // check if we need to do anything at all...
3242     int os = qMax(tbl->currentBorderStyle() ? (tbl->currentBorderStyle()->border->width + 1) / 2 : 0, 2 * maximalOutlineSize(pI.phase));
3243     if ((_ty >= pI.r.y() + pI.r.height() + os)
3244             || (_ty + _topExtra + m_height + _bottomExtra <= pI.r.y() - os)) {
3245         return;
3246     }
3247 
3248     if (pI.phase == PaintActionOutline) {
3249         paintOutline(pI.p, _tx, _ty, width(), height() + borderTopExtra() + borderBottomExtra(), style());
3250     }
3251 
3252     if (pI.phase == PaintActionCollapsedTableBorders && style()->visibility() == VISIBLE) {
3253         int w = width();
3254         int h = height() + borderTopExtra() + borderBottomExtra();
3255         paintCollapsedBorder(pI.p, _tx, _ty, w, h);
3256     } else {
3257         RenderBlock::paintObject(pI, _tx, _ty + _topExtra, false);
3258     }
3259 
3260 #ifdef BOX_DEBUG
3261     ::outlineBox(pI.p, _tx, _ty - _topExtra, width(), height() + borderTopExtra() + borderBottomExtra());
3262 #endif
3263 }
3264 
3265 static EBorderStyle collapsedBorderStyle(EBorderStyle style)
3266 {
3267     if (style == OUTSET) {
3268         style = GROOVE;
3269     } else if (style == INSET) {
3270         style = RIDGE;
3271     }
3272     return style;
3273 }
3274 
3275 struct CollapsedBorder {
3276     CollapsedBorder() {}
3277 
3278     CollapsedBorderValue border;
3279     RenderObject::BorderSide side;
3280     bool shouldPaint;
3281     int x1;
3282     int y1;
3283     int x2;
3284     int y2;
3285     EBorderStyle style;
3286 };
3287 
3288 class CollapsedBorders
3289 {
3290 public:
3291     CollapsedBorders() : count(0) {}
3292 
3293     void addBorder(const CollapsedBorderValue &b, RenderObject::BorderSide s, bool paint,
3294                    int _x1, int _y1, int _x2, int _y2,
3295                    EBorderStyle _style)
3296     {
3297         if (b.exists() && paint) {
3298             borders[count].border = b;
3299             borders[count].side = s;
3300             borders[count].shouldPaint = paint;
3301             borders[count].x1 = _x1;
3302             borders[count].x2 = _x2;
3303             borders[count].y1 = _y1;
3304             borders[count].y2 = _y2;
3305             borders[count].style = _style;
3306             count++;
3307         }
3308     }
3309 
3310     CollapsedBorder *nextBorder()
3311     {
3312         for (int i = 0; i < count; i++) {
3313             if (borders[i].border.exists() && borders[i].shouldPaint) {
3314                 borders[i].shouldPaint = false;
3315                 return &borders[i];
3316             }
3317         }
3318 
3319         return nullptr;
3320     }
3321 
3322     CollapsedBorder borders[4];
3323     int count;
3324 };
3325 
3326 static void addBorderStyle(QList<CollapsedBorderValue> &borderStyles, CollapsedBorderValue borderValue)
3327 {
3328     if (!borderValue.exists() || borderStyles.contains(borderValue)) {
3329         return;
3330     }
3331 
3332     QList<CollapsedBorderValue>::Iterator it = borderStyles.begin();
3333     QList<CollapsedBorderValue>::Iterator end = borderStyles.end();
3334     for (; it != end; ++it) {
3335         CollapsedBorderValue result = compareBorders(*it, borderValue);
3336         if (result == *it) {
3337             borderStyles.insert(it, borderValue);
3338             return;
3339         }
3340     }
3341 
3342     borderStyles.append(borderValue);
3343 }
3344 
3345 void RenderTableCell::collectBorders(QList<CollapsedBorderValue> &borderStyles)
3346 {
3347     bool rtl = table()->style()->direction() == RTL;
3348     addBorderStyle(borderStyles, collapsedLeftBorder(rtl));
3349     addBorderStyle(borderStyles, collapsedRightBorder(rtl));
3350     addBorderStyle(borderStyles, collapsedTopBorder());
3351     addBorderStyle(borderStyles, collapsedBottomBorder());
3352 }
3353 
3354 void RenderTableCell::paintCollapsedBorder(QPainter *p, int _tx, int _ty, int w, int h)
3355 {
3356     if (!table()->currentBorderStyle()) {
3357         return;
3358     }
3359 
3360     bool rtl = table()->style()->direction() == RTL;
3361     CollapsedBorderValue leftVal = collapsedLeftBorder(rtl);
3362     CollapsedBorderValue rightVal = collapsedRightBorder(rtl);
3363     CollapsedBorderValue topVal = collapsedTopBorder();
3364     CollapsedBorderValue bottomVal = collapsedBottomBorder();
3365 
3366     // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
3367     int topWidth = topVal.width();
3368     int bottomWidth = bottomVal.width();
3369     int leftWidth = leftVal.width();
3370     int rightWidth = rightVal.width();
3371 
3372     _tx -= leftWidth / 2;
3373     _ty -= topWidth / 2;
3374     w += leftWidth / 2 + (rightWidth + 1) / 2;
3375     h += topWidth / 2 + (bottomWidth + 1) / 2;
3376 
3377     bool tt = topVal.isTransparent();
3378     bool bt = bottomVal.isTransparent();
3379     bool rt = rightVal.isTransparent();
3380     bool lt = leftVal.isTransparent();
3381 
3382     EBorderStyle ts = collapsedBorderStyle(topVal.style());
3383     EBorderStyle bs = collapsedBorderStyle(bottomVal.style());
3384     EBorderStyle ls = collapsedBorderStyle(leftVal.style());
3385     EBorderStyle rs = collapsedBorderStyle(rightVal.style());
3386 
3387     bool render_t = ts > BHIDDEN && !tt && (topVal.precedence != BCELL || *topVal.border == style()->borderTop());
3388     bool render_l = ls > BHIDDEN && !lt && (leftVal.precedence != BCELL || *leftVal.border == style()->borderLeft());
3389     bool render_r = rs > BHIDDEN && !rt && (rightVal.precedence != BCELL || *rightVal.border == style()->borderRight());
3390     bool render_b = bs > BHIDDEN && !bt && (bottomVal.precedence != BCELL || *bottomVal.border == style()->borderBottom());
3391 
3392     // We never paint diagonals at the joins.  We simply let the border with the highest
3393     // precedence paint on top of borders with lower precedence.
3394     CollapsedBorders borders;
3395     borders.addBorder(topVal, BSTop, render_t, _tx, _ty, _tx + w, _ty + topWidth, ts);
3396     borders.addBorder(bottomVal, BSBottom, render_b, _tx, _ty + h - bottomWidth, _tx + w, _ty + h, bs);
3397     borders.addBorder(leftVal, BSLeft, render_l, _tx, _ty, _tx + leftWidth, _ty + h, ls);
3398     borders.addBorder(rightVal, BSRight, render_r, _tx + w - rightWidth, _ty, _tx + w, _ty + h, rs);
3399 
3400     for (CollapsedBorder *border = borders.nextBorder(); border; border = borders.nextBorder()) {
3401         if (border->border == *table()->currentBorderStyle())
3402             drawBorder(p, border->x1, border->y1, border->x2, border->y2, border->side,
3403                        border->border.color(), style()->color(), border->style, 0, 0);
3404     }
3405 }
3406 
3407 void RenderTableCell::paintBackgroundsBehindCell(PaintInfo &pI, int _tx, int _ty, RenderObject *bgObj)
3408 {
3409     if (!bgObj || style()->visibility() != VISIBLE) {
3410         return;
3411     }
3412 
3413     RenderTable *tableElt = table();
3414 
3415     int w = bgObj->width();
3416     int h = bgObj->height() + bgObj->borderTopExtra() + bgObj->borderBottomExtra();
3417 
3418     int cellx = _tx;
3419     int celly = _ty;
3420     int cellw = w;
3421     int cellh = h;
3422     if (bgObj != this) {
3423         cellx += m_x;
3424         celly += m_y;
3425         cellw = width();
3426         cellh = height() + borderTopExtra() + borderBottomExtra();
3427     }
3428 
3429     QRect cr;
3430     cr.setX(qMax(cellx, pI.r.x()));
3431     cr.setY(qMax(celly, pI.r.y()));
3432     cr.setWidth(cellx < pI.r.x() ? qMax(0, cellw - (pI.r.x() - cellx)) : qMin(pI.r.width(), cellw));
3433     cr.setHeight(celly < pI.r.y() ? qMax(0, cellh - (pI.r.y() - celly)) : qMin(pI.r.height(), cellh));
3434 
3435     QColor c = bgObj->style()->backgroundColor();
3436     const BackgroundLayer *bgLayer = bgObj->style()->backgroundLayers();
3437 
3438     if (bgLayer->hasImage() || c.isValid()) {
3439         // We have to clip here because the background would paint
3440         // on top of the borders otherwise.  This only matters for cells and rows.
3441         bool hasLayer = bgObj->layer() && (bgObj == this || bgObj == parent());
3442         if (hasLayer && tableElt->collapseBorders()) {
3443             pI.p->save();
3444             QRect clipRect(cellx + borderLeft(), celly + borderTop(), cellw - borderLeft() - borderRight(), cellh - borderTop() - borderBottom());
3445             clipRect = pI.p->combinedMatrix().mapRect(clipRect);
3446             QRegion creg(clipRect);
3447             QRegion old = pI.p->clipRegion();
3448             if (!old.isEmpty()) {
3449                 creg = old.intersected(creg);
3450             }
3451             pI.p->setClipRegion(creg);
3452         }
3453         KHTMLAssert(bgObj->isBox());
3454         static_cast<RenderBox *>(bgObj)->paintAllBackgrounds(pI.p, c, bgLayer, cr, _tx, _ty, w, h);
3455         if (hasLayer && tableElt->collapseBorders()) {
3456             pI.p->restore();
3457         }
3458     }
3459 }
3460 
3461 void RenderTableCell::paintBoxDecorations(PaintInfo &pI, int _tx, int _ty)
3462 {
3463     RenderTable *tableElt = table();
3464     bool drawBorders = true;
3465     // Moz paints bgcolor/bgimage on <td>s in quirks mode even if
3466     // empty-cells are on. Fixes regression on #43426, attachment #354
3467     if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) {
3468         drawBorders = false;
3469     }
3470     if (!style()->htmlHacks() && !drawBorders) {
3471         return;
3472     }
3473 
3474     _ty -= borderTopExtra();
3475 
3476     // Paint our cell background.
3477     paintBackgroundsBehindCell(pI, _tx, _ty, this);
3478 
3479     int w = width();
3480     int h = height() + borderTopExtra() + borderBottomExtra();
3481 
3482     if (drawBorders && style()->hasBorder() && !tableElt->collapseBorders()) {
3483         paintBorder(pI.p, _tx, _ty, w, h, style());
3484     }
3485 }
3486 
3487 #ifdef ENABLE_DUMP
3488 void RenderTableCell::dump(QTextStream &stream, const QString &ind) const
3489 {
3490     RenderFlow::dump(stream, ind);
3491     stream << " row=" << _row;
3492     stream << " col=" << _col;
3493     stream << " rSpan=" << rSpan;
3494     stream << " cSpan=" << cSpan;
3495 //    *stream << " nWrap=" << nWrap;
3496 }
3497 #endif
3498 
3499 // -------------------------------------------------------------------------
3500 
3501 RenderTableCol::RenderTableCol(DOM::NodeImpl *node)
3502     : RenderBox(node), m_span(1)
3503 {
3504     // init RenderObject attributes
3505     setInline(true);   // our object is not Inline
3506     updateFromElement();
3507 }
3508 
3509 void RenderTableCol::updateFromElement()
3510 {
3511     DOM::NodeImpl *node = element();
3512     if (node && (node->id() == ID_COL || node->id() == ID_COLGROUP)) {
3513         DOM::HTMLTableColElementImpl *tc = static_cast<DOM::HTMLTableColElementImpl *>(node);
3514         m_span = tc->span();
3515     } else {
3516         m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP);
3517     }
3518 }
3519 
3520 #ifdef ENABLE_DUMP
3521 void RenderTableCol::dump(QTextStream &stream, const QString &ind) const
3522 {
3523     RenderContainer::dump(stream, ind);
3524     stream << " _span=" << m_span;
3525 }
3526 #endif
3527 
3528 // -------------------------------------------------------------------------
3529 
3530 TableSectionIterator::TableSectionIterator(RenderTable *table, bool fromEnd)
3531 {
3532     if (fromEnd) {
3533         sec = table->foot;
3534         if (sec) {
3535             return;
3536         }
3537 
3538         sec = static_cast<RenderTableSection *>(table->lastChild());
3539         while (sec && (!sec->isTableSection()
3540                        || sec == table->head || sec == table->foot)) {
3541             sec = static_cast<RenderTableSection *>(sec->previousSibling());
3542         }
3543         if (sec) {
3544             return;
3545         }
3546 
3547         sec = table->head;
3548     } else {
3549         sec = table->head;
3550         if (sec) {
3551             return;
3552         }
3553 
3554         sec = static_cast<RenderTableSection *>(table->firstChild());
3555         while (sec && (!sec->isTableSection()
3556                        || sec == table->head || sec == table->foot)) {
3557             sec = static_cast<RenderTableSection *>(sec->nextSibling());
3558         }
3559         if (sec) {
3560             return;
3561         }
3562 
3563         sec = table->foot;
3564     }/*end if*/
3565 
3566 }
3567 
3568 TableSectionIterator &TableSectionIterator::operator ++()
3569 {
3570     RenderTable *table = sec->table();
3571     if (sec == table->head) {
3572 
3573         sec = static_cast<RenderTableSection *>(table->firstChild());
3574         while (sec && (!sec->isTableSection()
3575                        || sec == table->head || sec == table->foot)) {
3576             sec = static_cast<RenderTableSection *>(sec->nextSibling());
3577         }
3578         if (sec) {
3579             return *this;
3580         }
3581 
3582     } else if (sec == table->foot) {
3583         sec = nullptr;
3584         return *this;
3585 
3586     } else {
3587 
3588         do {
3589             sec = static_cast<RenderTableSection *>(sec->nextSibling());
3590         } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot));
3591         if (sec) {
3592             return *this;
3593         }
3594 
3595     }/*end if*/
3596 
3597     sec = table->foot;
3598     return *this;
3599 }
3600 
3601 TableSectionIterator &TableSectionIterator::operator --()
3602 {
3603     RenderTable *table = sec->table();
3604     if (sec == table->foot) {
3605 
3606         sec = static_cast<RenderTableSection *>(table->lastChild());
3607         while (sec && (!sec->isTableSection()
3608                        || sec == table->head || sec == table->foot)) {
3609             sec = static_cast<RenderTableSection *>(sec->previousSibling());
3610         }
3611         if (sec) {
3612             return *this;
3613         }
3614 
3615     } else if (sec == table->head) {
3616         sec = nullptr;
3617         return *this;
3618 
3619     } else {
3620 
3621         do {
3622             sec = static_cast<RenderTableSection *>(sec->previousSibling());
3623         } while (sec && (!sec->isTableSection() || sec == table->head || sec == table->foot));
3624         if (sec) {
3625             return *this;
3626         }
3627 
3628     }/*end if*/
3629 
3630     sec = table->foot;
3631     return *this;
3632 }
3633 
3634 #undef TABLE_DEBUG
3635 #undef DEBUG_LAYOUT
3636 #undef BOX_DEBUG