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