ab4d70af768fb5c08b66dd49c3f9198f0c4405e1
[WebKit-https.git] / WebCore / rendering / RenderTableSection.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 1997 Martin Jones (mjones@kde.org)
5  *           (C) 1997 Torben Weis (weis@kde.org)
6  *           (C) 1998 Waldo Bastian (bastian@kde.org)
7  *           (C) 1999 Lars Knoll (knoll@kde.org)
8  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
9  * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
10  * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public License
23  * along with this library; see the file COPYING.LIB.  If not, write to
24  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 #include "config.h"
29 #include "RenderTableSection.h"
30 #include "RenderTableCell.h"
31 #include "RenderTableRow.h"
32 #include "RenderTableCol.h"
33 #include "Document.h"
34 #include "HTMLNames.h"
35 #include "KWQTextStream.h"
36
37 using namespace std;
38
39 namespace WebCore {
40
41 using namespace HTMLNames;
42
43 RenderTableSection::RenderTableSection(Node* node)
44     : RenderContainer(node)
45 {
46     // init RenderObject attributes
47     setInline(false);   // our object is not Inline
48     gridRows = 0;
49     cCol = 0;
50     cRow = -1;
51     needCellRecalc = false;
52 }
53
54 RenderTableSection::~RenderTableSection()
55 {
56     clearGrid();
57 }
58
59 void RenderTableSection::destroy()
60 {
61     // recalc cell info because RenderTable has unguarded pointers
62     // stored that point to this RenderTableSection.
63     if (table())
64         table()->setNeedSectionRecalc();
65
66     RenderContainer::destroy();
67 }
68
69 void RenderTableSection::setStyle(RenderStyle* _style)
70 {
71     // we don't allow changing this one
72     if (style())
73         _style->setDisplay(style()->display());
74     else if (_style->display() != TABLE_FOOTER_GROUP && _style->display() != TABLE_HEADER_GROUP)
75         _style->setDisplay(TABLE_ROW_GROUP);
76
77     RenderContainer::setStyle(_style);
78 }
79
80 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
81 {
82     bool isTableSection = element() && (element()->hasTagName(theadTag) || element()->hasTagName(tbodyTag) || element()->hasTagName(tfootTag));
83
84     if (!child->isTableRow()) {
85         if (isTableSection && child->element() && child->element()->hasTagName(formTag)) {
86             RenderContainer::addChild(child, beforeChild);
87             return;
88         }
89
90         RenderObject* last = beforeChild;
91         if (!last)
92             last = lastChild();
93         if (last && last->isAnonymous()) {
94             last->addChild(child);
95             return;
96         }
97
98         // If beforeChild is inside an anonymous cell/row, insert into the cell or into
99         // the anonymous row containing it, if there is one.
100         RenderObject* lastBox = last;
101         while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
102             lastBox = lastBox->parent();
103         if (lastBox && lastBox->isAnonymous()) {
104             lastBox->addChild(child, beforeChild);
105             return;
106         }
107
108         RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table */);
109         RenderStyle* newStyle = new (renderArena()) RenderStyle();
110         newStyle->inheritFrom(style());
111         newStyle->setDisplay(TABLE_ROW);
112         row->setStyle(newStyle);
113         addChild(row, beforeChild);
114         row->addChild(child);
115         return;
116     }
117
118     if (beforeChild)
119         setNeedCellRecalc();
120
121     ++cRow;
122     cCol = 0;
123
124     ensureRows(cRow + 1);
125
126     if (!beforeChild) {
127         grid[cRow].height = child->style()->height();
128         if (grid[cRow].height.isRelative())
129             grid[cRow].height = Length();
130     }
131
132     RenderContainer::addChild(child, beforeChild);
133 }
134
135 bool RenderTableSection::ensureRows(int numRows)
136 {
137     int nRows = gridRows;
138     if (numRows > nRows) {
139         if (numRows > static_cast<int>(grid.size()))
140             if (!grid.resize(numRows*2+1))
141                 return false;
142
143         gridRows = numRows;
144         int nCols = table()->numEffCols();
145         CellStruct emptyCellStruct;
146         emptyCellStruct.cell = 0;
147         emptyCellStruct.inColSpan = false;
148         for (int r = nRows; r < numRows; r++) {
149             grid[r].row = new Row(nCols);
150             grid[r].row->fill(emptyCellStruct);
151             grid[r].rowRenderer = 0;
152             grid[r].baseLine = 0;
153             grid[r].height = Length();
154         }
155     }
156
157     return true;
158 }
159
160 void RenderTableSection::addCell(RenderTableCell *cell, RenderObject* row)
161 {
162     int rSpan = cell->rowSpan();
163     int cSpan = cell->colSpan();
164     DeprecatedArray<RenderTable::ColumnStruct> &columns = table()->columns;
165     int nCols = columns.size();
166
167     // ### mozilla still seems to do the old HTML way, even for strict DTD
168     // (see the annotation on table cell layouting in the CSS specs and the testcase below:
169     // <TABLE border>
170     // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
171     // <TR><TD colspan="2">5
172     // </TABLE>
173
174     while (cCol < nCols && (cellAt(cRow, cCol).cell || cellAt(cRow, cCol).inColSpan))
175         cCol++;
176
177     if (rSpan == 1) {
178         // we ignore height settings on rowspan cells
179         Length height = cell->style()->height();
180         if (height.value() > 0 || (height.isRelative() && height.value() >= 0)) {
181             Length cRowHeight = grid[cRow].height;
182             switch (height.type()) {
183                 case Percent:
184                     if (!(cRowHeight.isPercent()) ||
185                         (cRowHeight.isPercent() && cRowHeight.value() < height.value()))
186                         grid[cRow].height = height;
187                         break;
188                 case Fixed:
189                     if (cRowHeight.type() < Percent ||
190                         (cRowHeight.isFixed() && cRowHeight.value() < height.value()))
191                         grid[cRow].height = height;
192                     break;
193                 case Relative:
194                 default:
195                     break;
196             }
197         }
198     }
199
200     // make sure we have enough rows
201     if (!ensureRows(cRow + rSpan))
202         return;
203
204     grid[cRow].rowRenderer = row;
205
206     int col = cCol;
207     // tell the cell where it is
208     CellStruct currentCell;
209     currentCell.cell = cell;
210     currentCell.inColSpan = false;
211     while (cSpan) {
212         int currentSpan;
213         if (cCol >= nCols) {
214             table()->appendColumn(cSpan);
215             currentSpan = cSpan;
216         } else {
217             if (cSpan < columns[cCol].span)
218                 table()->splitColumn(cCol, cSpan);
219             currentSpan = columns[cCol].span;
220         }
221
222         for (int r = 0; r < rSpan; r++) {
223             CellStruct& c = cellAt(cRow + r, cCol);
224             if (currentCell.cell && !c.cell)
225                 c.cell = currentCell.cell;
226             if (currentCell.inColSpan)
227                 c.inColSpan = true;
228         }
229         cCol++;
230         cSpan -= currentSpan;
231         currentCell.cell = 0;
232         currentCell.inColSpan = true;
233     }
234     if (cell) {
235         cell->setRow(cRow);
236         cell->setCol(table()->effColToCol(col));
237     }
238 }
239
240
241
242 void RenderTableSection::setCellWidths()
243 {
244     DeprecatedArray<int> &columnPos = table()->columnPos;
245
246     int rows = gridRows;
247     for (int i = 0; i < rows; i++) {
248         Row &row = *grid[i].row;
249         int cols = row.size();
250         for (int j = 0; j < cols; j++) {
251             CellStruct current = row[j];
252             RenderTableCell *cell = current.cell;
253
254             if (!cell)
255                 continue;
256             int endCol = j;
257             int cspan = cell->colSpan();
258             while (cspan && endCol < cols) {
259                 cspan -= table()->columns[endCol].span;
260                 endCol++;
261             }
262             int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
263             int oldWidth = cell->width();
264             if (w != oldWidth) {
265                 bool neededLayout = cell->selfNeedsLayout();
266                 cell->setNeedsLayout(true);
267                 if (!neededLayout && !selfNeedsLayout() && cell->checkForRepaintDuringLayout())
268                     cell->repaintObjectsBeforeLayout();
269                 cell->setWidth(w);
270             }
271         }
272     }
273 }
274
275
276 void RenderTableSection::calcRowHeight()
277 {
278     int indx;
279     RenderTableCell *cell;
280
281     int totalRows = gridRows;
282     int spacing = table()->vBorderSpacing();
283
284     rowPos.resize(totalRows + 1);
285     rowPos[0] = spacing;
286
287     for (int r = 0; r < totalRows; r++) {
288         rowPos[r + 1] = 0;
289
290         int baseline = 0;
291         int bdesc = 0;
292         int ch = grid[r].height.calcMinValue(0);
293         int pos = rowPos[r + 1] + ch + spacing;
294
295         if (pos > rowPos[r + 1])
296             rowPos[r + 1] = pos;
297
298         Row *row = grid[r].row;
299         int totalCols = row->size();
300         int totalRows = gridRows;
301
302         for (int c = 0; c < totalCols; c++) {
303             CellStruct current = cellAt(r, c);
304             cell = current.cell;
305             if (!cell || current.inColSpan)
306                 continue;
307             if (r < totalRows - 1 && cellAt(r + 1, c).cell == cell)
308                 continue;
309
310             if ((indx = r - cell->rowSpan() + 1) < 0)
311                 indx = 0;
312
313             if (cell->overrideSize() != -1) {
314                 cell->setOverrideSize(-1);
315                 cell->setChildNeedsLayout(true, false);
316                 cell->layoutIfNeeded();
317             }
318             
319             // Explicit heights use the border box in quirks mode.  In strict mode do the right
320             // thing and actually add in the border and padding.
321             ch = cell->style()->height().calcValue(0) + 
322                 (cell->style()->htmlHacks() ? 0 : (cell->paddingTop() + cell->paddingBottom() +
323                                                    cell->borderTop() + cell->borderBottom()));
324             if (cell->height() > ch)
325                 ch = cell->height();
326
327             pos = rowPos[ indx ] + ch + spacing;
328
329             if (pos > rowPos[r + 1])
330                 rowPos[r + 1] = pos;
331
332             // find out the baseline
333             EVerticalAlign va = cell->style()->verticalAlign();
334             if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP
335                 || va == SUPER || va == SUB) {
336                 int b = cell->baselinePosition();
337                 if (b > cell->borderTop() + cell->paddingTop()) {
338                     if (b > baseline)
339                         baseline = b;
340
341                     int td = rowPos[indx] + ch - b;
342                     if (td > bdesc)
343                         bdesc = td;
344                 }
345             }
346         }
347
348         //do we have baseline aligned elements?
349         if (baseline) {
350             // increase rowheight if baseline requires
351             int bRowPos = baseline + bdesc  + spacing ; // + 2*padding
352             if (rowPos[r + 1] < bRowPos)
353                 rowPos[r + 1] = bRowPos;
354
355             grid[r].baseLine = baseline;
356         }
357
358         if (rowPos[r + 1] < rowPos[r])
359             rowPos[r + 1] = rowPos[r];
360     }
361 }
362
363 int RenderTableSection::layoutRows(int toAdd)
364 {
365     int rHeight;
366     int rindx;
367     int totalRows = gridRows;
368     int hspacing = table()->hBorderSpacing();
369     int vspacing = table()->vBorderSpacing();
370     
371     // Set the width of our section now.  The rows will also be this width.
372     m_width = table()->contentWidth();
373     
374     if (toAdd && totalRows && (rowPos[totalRows] || !nextSibling())) {
375
376         int totalHeight = rowPos[totalRows] + toAdd;
377
378         int dh = toAdd;
379         int totalPercent = 0;
380         int numAuto = 0;
381         for (int r = 0; r < totalRows; r++) {
382             if (grid[r].height.isAuto())
383                 numAuto++;
384             else if (grid[r].height.isPercent())
385                 totalPercent += grid[r].height.value();
386         }
387         if (totalPercent) {
388             // try to satisfy percent
389             int add = 0;
390             if (totalPercent > 100)
391                 totalPercent = 100;
392             int rh = rowPos[1] - rowPos[0];
393             for (int r = 0; r < totalRows; r++) {
394                 if (totalPercent > 0 && grid[r].height.isPercent()) {
395                     int toAdd = min(dh, (totalHeight * grid[r].height.value() / 100) - rh);
396                     // If toAdd is negative, then we don't want to shrink the row (this bug
397                     // affected Outlook Web Access).
398                     toAdd = max(0, toAdd);
399                     add += toAdd;
400                     dh -= toAdd;
401                     totalPercent -= grid[r].height.value();
402                 }
403                 if (r < totalRows - 1)
404                     rh = rowPos[r + 2] - rowPos[r + 1];
405                 rowPos[r + 1] += add;
406             }
407         }
408         if (numAuto) {
409             // distribute over variable cols
410             int add = 0;
411             for (int r = 0; r < totalRows; r++) {
412                 if (numAuto > 0 && grid[r].height.isAuto()) {
413                     int toAdd = dh/numAuto;
414                     add += toAdd;
415                     dh -= toAdd;
416                     numAuto--;
417                 }
418                 rowPos[r + 1] += add;
419             }
420         }
421         if (dh > 0 && rowPos[totalRows]) {
422             // if some left overs, distribute equally.
423             int tot = rowPos[totalRows];
424             int add = 0;
425             int prev = rowPos[0];
426             for (int r = 0; r < totalRows; r++) {
427                 //weight with the original height
428                 add += dh * (rowPos[r + 1] - prev) / tot;
429                 prev = rowPos[r + 1];
430                 rowPos[r + 1] += add;
431             }
432         }
433     }
434
435     int leftOffset = hspacing;
436
437     int nEffCols = table()->numEffCols();
438     for (int r = 0; r < totalRows; r++) {
439         Row *row = grid[r].row;
440         int totalCols = row->size();
441         
442         // Set the row's x/y position and width/height.
443         if (grid[r].rowRenderer) {
444             grid[r].rowRenderer->setPos(0, rowPos[r]);
445             grid[r].rowRenderer->setWidth(m_width);
446             grid[r].rowRenderer->setHeight(rowPos[r+1] - rowPos[r] - vspacing);
447         }
448
449         for (int c = 0; c < nEffCols; c++) {
450             CellStruct current = cellAt(r, c);
451             RenderTableCell* cell = current.cell;
452             
453             if (!cell)
454                 continue;
455             if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell)
456                 continue;
457
458             if ((rindx = r-cell->rowSpan() + 1) < 0)
459                 rindx = 0;
460
461             rHeight = rowPos[r + 1] - rowPos[rindx] - vspacing;
462             
463             // Force percent height children to lay themselves out again.
464             // This will cause these children to grow to fill the cell.
465             // FIXME: There is still more work to do here to fully match WinIE (should
466             // it become necessary to do so).  In quirks mode, WinIE behaves like we
467             // do, but it will clip the cells that spill out of the table section.  In
468             // strict mode, Mozilla and WinIE both regrow the table to accommodate the
469             // new height of the cell (thus letting the percentages cause growth one
470             // time only).  We may also not be handling row-spanning cells correctly.
471             //
472             // Note also the oddity where replaced elements always flex, and yet blocks/tables do
473             // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
474             // match the behavior perfectly, but we'll continue to refine it as we discover new
475             // bugs. :)
476             bool cellChildrenFlex = false;
477             bool flexAllChildren = cell->style()->height().isFixed() || 
478                 (!table()->style()->height().isAuto() && rHeight != cell->height());
479
480             for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
481                 if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || o->scrollsOverflow() || flexAllChildren)) {
482                     // Tables with no sections do not flex.
483                     if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) {
484                         o->setNeedsLayout(true, false);
485                         cell->setChildNeedsLayout(true, false);
486                         cellChildrenFlex = true;
487                     }
488                 }
489             }
490             if (cellChildrenFlex) {
491                 // Alignment within a cell is based off the calculated
492                 // height, which becomes irrelevant once the cell has
493                 // been resized based off its percentage. -dwh
494                 cell->setCellTopExtra(0);
495                 cell->setCellBottomExtra(0);
496
497                 cell->setOverrideSize(max(0, 
498                                            rHeight - cell->borderTop() - cell->paddingTop() - 
499                                                      cell->borderBottom() - cell->paddingBottom()));
500                 cell->layoutIfNeeded();
501             } else {
502                 EVerticalAlign va = cell->style()->verticalAlign();
503                 int te = 0;
504                 switch (va) {
505                     case SUB:
506                     case SUPER:
507                     case TEXT_TOP:
508                     case TEXT_BOTTOM:
509                     case BASELINE:
510                         te = getBaseline(r) - cell->baselinePosition() ;
511                         break;
512                     case TOP:
513                         te = 0;
514                         break;
515                     case MIDDLE:
516                         te = (rHeight - cell->height())/2;
517                         break;
518                     case BOTTOM:
519                         te = rHeight - cell->height();
520                         break;
521                     default:
522                         break;
523                 }
524                 
525                 int oldTe = cell->borderTopExtra();
526                 int oldBe = cell->borderBottomExtra();
527                 
528                 int be = rHeight - cell->height() - te;
529                 cell->setCellTopExtra(te);
530                 cell->setCellBottomExtra(be);
531                 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout() && (te != oldTe || be > oldBe))
532                     cell->repaint();
533             }
534             
535             int oldCellX = cell->xPos();
536             int oldCellY = cell->yPos() - cell->borderTopExtra();
537         
538             if (style()->direction() == RTL) {
539                 cell->setPos(
540                     table()->columnPos[(int)totalCols] -
541                     table()->columnPos[table()->colToEffCol(cell->col()+cell->colSpan())] +
542                     leftOffset,
543                     rowPos[rindx]);
544             } else
545                 cell->setPos(table()->columnPos[c] + leftOffset, rowPos[rindx]);
546
547             // If the cell moved, we have to repaint it as well as any floating/positioned
548             // descendants.  An exception is if we need a layout.  In this case, we know we're going to
549             // repaint ourselves (and the cell) anyway.
550             if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
551                 cell->repaintDuringLayoutIfMoved(oldCellX, oldCellY);
552         }
553     }
554
555     m_height = rowPos[totalRows];
556     return m_height;
557 }
558
559 int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const
560 {
561     int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf);
562     if (!includeOverflowInterior && hasOverflowClip())
563         return bottom;
564
565     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
566         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
567             if (cell->isTableCell()) {
568                 int bp = cell->yPos() + cell->lowestPosition(false);
569                 bottom = max(bottom, bp);
570         }
571     }
572     
573     return bottom;
574 }
575
576 int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const
577 {
578     int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf);
579     if (!includeOverflowInterior && hasOverflowClip())
580         return right;
581
582     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
583         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
584             if (cell->isTableCell()) {
585                 int rp = cell->xPos() + cell->rightmostPosition(false);
586                 right = max(right, rp);
587         }
588     }
589     
590     return right;
591 }
592
593 int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const
594 {
595     int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf);
596     if (!includeOverflowInterior && hasOverflowClip())
597         return left;
598     
599     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
600         for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
601             if (cell->isTableCell()) {
602                 int lp = cell->xPos() + cell->leftmostPosition(false);
603                 left = min(left, lp);
604         }
605     }
606     
607     return left;
608 }
609
610 void RenderTableSection::paint(PaintInfo& i, int tx, int ty)
611 {
612     unsigned int totalRows = gridRows;
613     unsigned int totalCols = table()->columns.size();
614
615     tx += m_x;
616     ty += m_y;
617
618     // check which rows and cols are visible and only paint these
619     // ### fixme: could use a binary search here
620     PaintPhase paintPhase = i.phase;
621     int x = i.r.x();
622     int y = i.r.y();
623     int w = i.r.width();
624     int h = i.r.height();
625
626     int os = 2 * maximalOutlineSize(paintPhase);
627     unsigned int startrow = 0;
628     unsigned int endrow = totalRows;
629     for (; startrow < totalRows; startrow++)
630         if (ty + rowPos[startrow+1] >= y - os)
631             break;
632
633     for (; endrow > 0; endrow--)
634         if ( ty + rowPos[endrow-1] <= y + h + os)
635             break;
636
637     unsigned int startcol = 0;
638     unsigned int endcol = totalCols;
639     if (style()->direction() == LTR) {
640         for (; startcol < totalCols; startcol++) {
641             if (tx + table()->columnPos[startcol + 1] >= x - os)
642                 break;
643         }
644         for (; endcol > 0; endcol--) {
645             if (tx + table()->columnPos[endcol - 1] <= x + w + os)
646                 break;
647         }
648     }
649
650     if (startcol < endcol) {
651         // draw the cells
652         for (unsigned int r = startrow; r < endrow; r++) {
653             unsigned int c = startcol;
654             // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it
655             while (c && cellAt(r, c).inColSpan)
656                 c--;
657             for (; c < endcol; c++) {
658                 CellStruct current = cellAt(r, c);
659                 RenderTableCell *cell = current.cell;
660                     
661                 // Cells must always paint in the order in which they appear taking into account
662                 // their upper left originating row/column.  For cells with rowspans, avoid repainting
663                 // if we've already seen the cell.
664                 if (!cell || (r > startrow && (cellAt(r-1, c).cell == cell)))
665                     continue;
666
667                 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
668                     // We need to handle painting a stack of backgrounds.  This stack (from bottom to top) consists of
669                     // the column group, column, row group, row, and then the cell.
670                     RenderObject* col = table()->colElement(c);
671                     RenderObject* colGroup = 0;
672                     if (col) {
673                         RenderStyle *style = col->parent()->style();
674                         if (style->display() == TABLE_COLUMN_GROUP)
675                             colGroup = col->parent();
676                     }
677                     RenderObject* row = cell->parent();
678                     
679                     // Column groups and columns first.
680                     // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
681                     // the stack, since we have already opened a transparency layer (potentially) for the table row group.
682                     // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
683                     // cell.
684                     cell->paintBackgroundsBehindCell(i, tx, ty, colGroup);
685                     cell->paintBackgroundsBehindCell(i, tx, ty, col);
686                     
687                     // Paint the row group next.
688                     cell->paintBackgroundsBehindCell(i, tx, ty, this);
689                     
690                     // Paint the row next, but only if it doesn't have a layer.  If a row has a layer, it will be responsible for
691                     // painting the row background for the cell.
692                     if (!row->layer())
693                         cell->paintBackgroundsBehindCell(i, tx, ty, row);
694                 }
695
696                 if ((!cell->layer() && !cell->parent()->layer()) || i.phase == PaintPhaseCollapsedTableBorders)
697                     cell->paint(i, tx, ty);
698             }
699         }
700     }
701 }
702
703 void RenderTableSection::recalcCells()
704 {
705     cCol = 0;
706     cRow = -1;
707     clearGrid();
708     gridRows = 0;
709
710     for (RenderObject *row = firstChild(); row; row = row->nextSibling()) {
711         if (row->isTableRow()) {
712             cRow++;
713             cCol = 0;
714             ensureRows(cRow + 1);
715             for (RenderObject *cell = row->firstChild(); cell; cell = cell->nextSibling())
716                 if (cell->isTableCell())
717                     addCell(static_cast<RenderTableCell *>(cell), row);
718         }
719     }
720     needCellRecalc = false;
721     setNeedsLayout(true);
722 }
723
724 void RenderTableSection::clearGrid()
725 {
726     int rows = gridRows;
727     while (rows--)
728         delete grid[rows].row;
729 }
730
731 int RenderTableSection::numColumns() const
732 {
733     int result = 0;
734     
735     for (int r = 0; r < gridRows; ++r) {
736         for (int c = result; c < table()->numEffCols(); ++c) {
737             const CellStruct& cell = cellAt(r, c);
738             if (cell.cell || cell.inColSpan)
739                 result = c;
740         }
741     }
742     
743     return result + 1;
744 }
745
746 RenderObject* RenderTableSection::removeChildNode(RenderObject* child)
747 {
748     setNeedCellRecalc();
749     return RenderContainer::removeChildNode(child);
750 }
751
752 // Hit Testing
753 bool RenderTableSection::nodeAtPoint(NodeInfo& info, int x, int y, int tx, int ty, HitTestAction action)
754 {
755     // Table sections cannot ever be hit tested.  Effectively they do not exist.
756     // Just forward to our children always.
757     tx += m_x;
758     ty += m_y;
759
760     for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
761         // FIXME: We have to skip over inline flows, since they can show up inside table rows
762         // at the moment (a demoted inline <form> for example). If we ever implement a
763         // table-specific hit-test method (which we should do for performance reasons anyway),
764         // then we can remove this check.
765         if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, x, y, tx, ty, action)) {
766             setInnerNode(info);
767             return true;
768         }
769     }
770     
771     return false;
772 }
773
774 #ifndef NDEBUG
775 void RenderTableSection::dump(QTextStream *stream, DeprecatedString ind) const
776 {
777     *stream << endl << ind << "grid=(" << gridRows << "," << table()->numEffCols() << ")" << endl << ind;
778     for (int r = 0; r < gridRows; r++) {
779         for (int c = 0; c < table()->numEffCols(); c++) {
780             if (cellAt( r, c).cell && !cellAt(r, c).inColSpan)
781                 *stream << "(" << cellAt(r, c).cell->row() << "," << cellAt(r, c).cell->col() << ","
782                         << cellAt(r, c).cell->rowSpan() << "," << cellAt(r, c).cell->colSpan() << ") ";
783             else
784                 *stream << cellAt(r, c).cell << "null cell ";
785         }
786         *stream << endl << ind;
787     }
788     RenderContainer::dump(stream,ind);
789 }
790 #endif
791
792 }