1/*
2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 *           (C) 1997 Torben Weis (weis@kde.org)
4 *           (C) 1998 Waldo Bastian (bastian@kde.org)
5 *           (C) 1999 Lars Knoll (knoll@kde.org)
6 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18 * Library General Public License for more details.
19 *
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB.  If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
24 */
25
26#include "config.h"
27#include "core/rendering/RenderTableSection.h"
28
29// FIXME: Remove 'RuntimeEnabledFeatures.h' when http://crbug.com/78724 is closed.
30#include "RuntimeEnabledFeatures.h"
31#include <limits>
32#include "core/rendering/HitTestResult.h"
33#include "core/rendering/PaintInfo.h"
34#include "core/rendering/RenderTableCell.h"
35#include "core/rendering/RenderTableCol.h"
36#include "core/rendering/RenderTableRow.h"
37#include "core/rendering/RenderView.h"
38#include "wtf/HashSet.h"
39#include "wtf/Vector.h"
40
41using namespace std;
42
43namespace WebCore {
44
45using namespace HTMLNames;
46
47// Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
48static unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
49static float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
50
51static inline void setRowLogicalHeightToRowStyleLogicalHeight(RenderTableSection::RowStruct& row)
52{
53    ASSERT(row.rowRenderer);
54    row.logicalHeight = row.rowRenderer->style()->logicalHeight();
55}
56
57static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row, const RenderTableCell* cell)
58{
59    // We ignore height settings on rowspan cells.
60    if (cell->rowSpan() != 1)
61        return;
62
63    Length logicalHeight = cell->style()->logicalHeight();
64    if (logicalHeight.isPositive()) {
65        Length cRowLogicalHeight = row.logicalHeight;
66        switch (logicalHeight.type()) {
67        case Percent:
68            if (!(cRowLogicalHeight.isPercent())
69                || (cRowLogicalHeight.isPercent() && cRowLogicalHeight.percent() < logicalHeight.percent()))
70                row.logicalHeight = logicalHeight;
71            break;
72        case Fixed:
73            if (cRowLogicalHeight.type() < Percent
74                || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
75                row.logicalHeight = logicalHeight;
76            break;
77        default:
78            break;
79        }
80    }
81}
82
83
84RenderTableSection::RenderTableSection(Element* element)
85    : RenderBox(element)
86    , m_cCol(0)
87    , m_cRow(0)
88    , m_outerBorderStart(0)
89    , m_outerBorderEnd(0)
90    , m_outerBorderBefore(0)
91    , m_outerBorderAfter(0)
92    , m_needsCellRecalc(false)
93    , m_hasMultipleCellLevels(false)
94{
95    // init RenderObject attributes
96    setInline(false); // our object is not Inline
97}
98
99RenderTableSection::~RenderTableSection()
100{
101}
102
103void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
104{
105    RenderBox::styleDidChange(diff, oldStyle);
106    propagateStyleToAnonymousChildren();
107
108    // If border was changed, notify table.
109    RenderTable* table = this->table();
110    if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border())
111        table->invalidateCollapsedBorders();
112}
113
114void RenderTableSection::willBeRemovedFromTree()
115{
116    RenderBox::willBeRemovedFromTree();
117
118    // Preventively invalidate our cells as we may be re-inserted into
119    // a new table which would require us to rebuild our structure.
120    setNeedsCellRecalc();
121}
122
123void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
124{
125    if (!child->isTableRow()) {
126        RenderObject* last = beforeChild;
127        if (!last)
128            last = lastChild();
129        if (last && last->isAnonymous() && !last->isBeforeOrAfterContent()) {
130            if (beforeChild == last)
131                beforeChild = last->firstChild();
132            last->addChild(child, beforeChild);
133            return;
134        }
135
136        if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
137            RenderObject* row = beforeChild->previousSibling();
138            if (row && row->isTableRow() && row->isAnonymous()) {
139                row->addChild(child);
140                return;
141            }
142        }
143
144        // If beforeChild is inside an anonymous cell/row, insert into the cell or into
145        // the anonymous row containing it, if there is one.
146        RenderObject* lastBox = last;
147        while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
148            lastBox = lastBox->parent();
149        if (lastBox && lastBox->isAnonymous() && !lastBox->isBeforeOrAfterContent()) {
150            lastBox->addChild(child, beforeChild);
151            return;
152        }
153
154        RenderObject* row = RenderTableRow::createAnonymousWithParentRenderer(this);
155        addChild(row, beforeChild);
156        row->addChild(child);
157        return;
158    }
159
160    if (beforeChild)
161        setNeedsCellRecalc();
162
163    unsigned insertionRow = m_cRow;
164    ++m_cRow;
165    m_cCol = 0;
166
167    ensureRows(m_cRow);
168
169    RenderTableRow* row = toRenderTableRow(child);
170    m_grid[insertionRow].rowRenderer = row;
171    row->setRowIndex(insertionRow);
172
173    if (!beforeChild)
174        setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
175
176    if (beforeChild && beforeChild->parent() != this)
177        beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
178
179    ASSERT(!beforeChild || beforeChild->isTableRow());
180    RenderBox::addChild(child, beforeChild);
181}
182
183void RenderTableSection::ensureRows(unsigned numRows)
184{
185    if (numRows <= m_grid.size())
186        return;
187
188    unsigned oldSize = m_grid.size();
189    m_grid.grow(numRows);
190
191    unsigned effectiveColumnCount = max(1u, table()->numEffCols());
192    for (unsigned row = oldSize; row < m_grid.size(); ++row)
193        m_grid[row].row.grow(effectiveColumnCount);
194}
195
196void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
197{
198    // We don't insert the cell if we need cell recalc as our internal columns' representation
199    // will have drifted from the table's representation. Also recalcCells will call addCell
200    // at a later time after sync'ing our columns' with the table's.
201    if (needsCellRecalc())
202        return;
203
204    unsigned rSpan = cell->rowSpan();
205    unsigned cSpan = cell->colSpan();
206    const Vector<RenderTable::ColumnStruct>& columns = table()->columns();
207    unsigned nCols = columns.size();
208    unsigned insertionRow = row->rowIndex();
209
210    // ### mozilla still seems to do the old HTML way, even for strict DTD
211    // (see the annotation on table cell layouting in the CSS specs and the testcase below:
212    // <TABLE border>
213    // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
214    // <TR><TD colspan="2">5
215    // </TABLE>
216    while (m_cCol < nCols && (cellAt(insertionRow, m_cCol).hasCells() || cellAt(insertionRow, m_cCol).inColSpan))
217        m_cCol++;
218
219    updateLogicalHeightForCell(m_grid[insertionRow], cell);
220
221    ensureRows(insertionRow + rSpan);
222
223    m_grid[insertionRow].rowRenderer = row;
224
225    unsigned col = m_cCol;
226    // tell the cell where it is
227    bool inColSpan = false;
228    while (cSpan) {
229        unsigned currentSpan;
230        if (m_cCol >= nCols) {
231            table()->appendColumn(cSpan);
232            currentSpan = cSpan;
233        } else {
234            if (cSpan < columns[m_cCol].span)
235                table()->splitColumn(m_cCol, cSpan);
236            currentSpan = columns[m_cCol].span;
237        }
238        for (unsigned r = 0; r < rSpan; r++) {
239            CellStruct& c = cellAt(insertionRow + r, m_cCol);
240            ASSERT(cell);
241            c.cells.append(cell);
242            // If cells overlap then we take the slow path for painting.
243            if (c.cells.size() > 1)
244                m_hasMultipleCellLevels = true;
245            if (inColSpan)
246                c.inColSpan = true;
247        }
248        m_cCol++;
249        cSpan -= currentSpan;
250        inColSpan = true;
251    }
252    cell->setCol(table()->effColToCol(col));
253}
254
255void RenderTableSection::populateSpanningRowsHeightFromCell(RenderTableCell* cell, struct SpanningRowsHeight& spanningRowsHeight)
256{
257    const unsigned rowSpan = cell->rowSpan();
258    const unsigned rowIndex = cell->rowIndex();
259
260    spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing = cell->logicalHeightForRowSizing();
261
262    spanningRowsHeight.rowHeight.resize(rowSpan);
263    spanningRowsHeight.totalRowsHeight = 0;
264    for (unsigned row = 0; row < rowSpan; row++) {
265        unsigned actualRow = row + rowIndex;
266        spanningRowsHeight.rowHeight[row] = m_rowPos[actualRow + 1] - m_rowPos[actualRow] - borderSpacingForRow(actualRow);
267        spanningRowsHeight.totalRowsHeight += spanningRowsHeight.rowHeight[row];
268        spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing -= borderSpacingForRow(actualRow);
269    }
270    // We don't span the following row so its border-spacing (if any) should be included.
271    spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing += borderSpacingForRow(rowIndex + rowSpan - 1);
272}
273
274void RenderTableSection::distributeExtraRowSpanHeightToPercentRows(RenderTableCell* cell, int totalPercent, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
275{
276    if (!extraRowSpanningHeight || !totalPercent)
277        return;
278
279    const unsigned rowSpan = cell->rowSpan();
280    const unsigned rowIndex = cell->rowIndex();
281    int percent = min(totalPercent, 100);
282    const int tableHeight = m_rowPos[m_grid.size()] + extraRowSpanningHeight;
283
284    // Our algorithm matches Firefox. Extra spanning height would be distributed Only in first percent height rows
285    // those total percent is 100. Other percent rows would be uneffected even extra spanning height is remain.
286    int accumulatedPositionIncrease = 0;
287    for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
288        if (percent > 0 && extraRowSpanningHeight > 0) {
289            if (m_grid[row].logicalHeight.isPercent()) {
290                int toAdd = (tableHeight * m_grid[row].logicalHeight.percent() / 100) - rowsHeight[row - rowIndex];
291                // FIXME: Note that this is wrong if we have a percentage above 100% and may make us grow
292                // above the available space.
293
294                toAdd = min(toAdd, extraRowSpanningHeight);
295                accumulatedPositionIncrease += toAdd;
296                extraRowSpanningHeight -= toAdd;
297                percent -= m_grid[row].logicalHeight.percent();
298            }
299        }
300        m_rowPos[row + 1] += accumulatedPositionIncrease;
301    }
302}
303
304void RenderTableSection::distributeExtraRowSpanHeightToAutoRows(RenderTableCell* cell, int totalAutoRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
305{
306    if (!extraRowSpanningHeight || !totalAutoRowsHeight)
307        return;
308
309    const unsigned rowSpan = cell->rowSpan();
310    const unsigned rowIndex = cell->rowIndex();
311    int accumulatedPositionIncrease = 0;
312    int remainder = 0;
313
314    // Aspect ratios of auto rows should not change otherwise table may look different than user expected.
315    // So extra height distributed in auto spanning rows based on their weight in spanning cell.
316    for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
317        if (m_grid[row].logicalHeight.isAuto()) {
318            accumulatedPositionIncrease += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) / totalAutoRowsHeight;
319            remainder += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) % totalAutoRowsHeight;
320
321            // While whole extra spanning height is distributing in auto spanning rows, rational parts remains
322            // in every integer division. So accumulating all remainder part in integer division and when total remainder
323            // is equvalent to divisor then 1 unit increased in row position.
324            // Note that this algorithm is biased towards adding more space towards the lower rows.
325            if (remainder >= totalAutoRowsHeight) {
326                remainder -= totalAutoRowsHeight;
327                accumulatedPositionIncrease++;
328            }
329        }
330        m_rowPos[row + 1] += accumulatedPositionIncrease;
331    }
332
333    ASSERT(!remainder);
334
335    extraRowSpanningHeight -= accumulatedPositionIncrease;
336}
337
338void RenderTableSection::distributeExtraRowSpanHeightToRemainingRows(RenderTableCell* cell, int totalRemainingRowsHeight, int& extraRowSpanningHeight, Vector<int>& rowsHeight)
339{
340    if (!extraRowSpanningHeight || !totalRemainingRowsHeight)
341        return;
342
343    const unsigned rowSpan = cell->rowSpan();
344    const unsigned rowIndex = cell->rowIndex();
345    int accumulatedPositionIncrease = 0;
346    int remainder = 0;
347
348    // Aspect ratios of the rows should not change otherwise table may look different than user expected.
349    // So extra height distribution in remaining spanning rows based on their weight in spanning cell.
350    for (unsigned row = rowIndex; row < (rowIndex + rowSpan); row++) {
351        if (!m_grid[row].logicalHeight.isPercent()) {
352            accumulatedPositionIncrease += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) / totalRemainingRowsHeight;
353            remainder += (extraRowSpanningHeight * rowsHeight[row - rowIndex]) % totalRemainingRowsHeight;
354
355            // While whole extra spanning height is distributing in remaining spanning rows, rational parts remains
356            // in every integer division. So accumulating all remainder part in integer division and when total remainder
357            // is equvalent to divisor then 1 unit increased in row position.
358            // Note that this algorithm is biased towards adding more space towards the lower rows.
359            if (remainder >= totalRemainingRowsHeight) {
360                remainder -= totalRemainingRowsHeight;
361                accumulatedPositionIncrease++;
362            }
363        }
364        m_rowPos[row + 1] += accumulatedPositionIncrease;
365    }
366
367    ASSERT(!remainder);
368
369    extraRowSpanningHeight -= accumulatedPositionIncrease;
370}
371
372// To avoid unneeded extra height distributions, we apply the following sorting algorithm:
373// 1. We sort by increasing start row but decreasing last row (ie the top-most, shortest cells first).
374// 2. For cells spanning the same rows, we sort by intrinsic size.
375static bool compareRowSpanCellsInHeightDistributionOrder(const RenderTableCell* cell2, const RenderTableCell* cell1)
376{
377    unsigned cellRowIndex1 = cell1->rowIndex();
378    unsigned cellRowSpan1 = cell1->rowSpan();
379    unsigned cellRowIndex2 = cell2->rowIndex();
380    unsigned cellRowSpan2 = cell2->rowSpan();
381
382    if (cellRowIndex1 == cellRowIndex2 && cellRowSpan1 == cellRowSpan2)
383        return (cell2->logicalHeightForRowSizing() > cell1->logicalHeightForRowSizing());
384
385    return (cellRowIndex2 >= cellRowIndex1 && (cellRowIndex2 + cellRowSpan2) <= (cellRowIndex1 + cellRowSpan1));
386}
387
388// Distribute rowSpan cell height in rows those comes in rowSpan cell based on the ratio of row's height if
389// 1. RowSpan cell height is greater then the total height of rows in rowSpan cell
390void RenderTableSection::distributeRowSpanHeightToRows(SpanningRenderTableCells& rowSpanCells)
391{
392    ASSERT(rowSpanCells.size());
393
394    // 'rowSpanCells' list is already sorted based on the cells rowIndex in ascending order
395    // Arrange row spanning cell in the order in which we need to process first.
396    std::sort(rowSpanCells.begin(), rowSpanCells.end(), compareRowSpanCellsInHeightDistributionOrder);
397
398    unsigned extraHeightToPropagate = 0;
399    unsigned lastRowIndex = 0;
400    unsigned lastRowSpan = 0;
401
402    for (unsigned i = 0; i < rowSpanCells.size(); i++) {
403        RenderTableCell* cell = rowSpanCells[i];
404
405        unsigned rowIndex = cell->rowIndex();
406
407        unsigned rowSpan = cell->rowSpan();
408
409        unsigned spanningCellEndIndex = rowIndex + rowSpan;
410        unsigned lastSpanningCellEndIndex = lastRowIndex + lastRowSpan;
411
412        // Only heightest spanning cell will distribute it's extra height in row if more then one spanning cells
413        // present at same level.
414        if (rowIndex == lastRowIndex && rowSpan == lastRowSpan)
415            continue;
416
417        int originalBeforePosition = m_rowPos[spanningCellEndIndex];
418
419        // When 2 spanning cells are ending at same row index then while extra height distribution of first spanning
420        // cell updates position of the last row so getting the original position of the last row in second spanning
421        // cell need to reduce the height changed by first spanning cell.
422        if (spanningCellEndIndex == lastSpanningCellEndIndex)
423            originalBeforePosition -= extraHeightToPropagate;
424
425        if (extraHeightToPropagate) {
426            for (unsigned row = lastSpanningCellEndIndex + 1; row <= spanningCellEndIndex; row++)
427                m_rowPos[row] += extraHeightToPropagate;
428        }
429
430        lastRowIndex = rowIndex;
431        lastRowSpan = rowSpan;
432
433        struct SpanningRowsHeight spanningRowsHeight;
434
435        populateSpanningRowsHeightFromCell(cell, spanningRowsHeight);
436
437        if (!spanningRowsHeight.totalRowsHeight || spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing <= spanningRowsHeight.totalRowsHeight)
438            continue;
439
440        int totalPercent = 0;
441        int totalAutoRowsHeight = 0;
442        int totalRemainingRowsHeight = spanningRowsHeight.totalRowsHeight;
443
444        // FIXME: Inner spanning cell height should not change if it have fixed height when it's parent spanning cell
445        // is distributing it's extra height in rows.
446
447        // Calculate total percentage, total auto rows height and total rows height except percent rows.
448        for (unsigned row = rowIndex; row < spanningCellEndIndex; row++) {
449            if (m_grid[row].logicalHeight.isPercent()) {
450                totalPercent += m_grid[row].logicalHeight.percent();
451                totalRemainingRowsHeight -= spanningRowsHeight.rowHeight[row - rowIndex];
452            } else if (m_grid[row].logicalHeight.isAuto()) {
453                totalAutoRowsHeight += spanningRowsHeight.rowHeight[row - rowIndex];
454            }
455        }
456
457        int extraRowSpanningHeight = spanningRowsHeight.spanningCellHeightIgnoringBorderSpacing - spanningRowsHeight.totalRowsHeight;
458
459        distributeExtraRowSpanHeightToPercentRows(cell, totalPercent, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
460        distributeExtraRowSpanHeightToAutoRows(cell, totalAutoRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
461        distributeExtraRowSpanHeightToRemainingRows(cell, totalRemainingRowsHeight, extraRowSpanningHeight, spanningRowsHeight.rowHeight);
462
463        ASSERT(!extraRowSpanningHeight);
464
465        // Getting total changed height in the table
466        extraHeightToPropagate = m_rowPos[spanningCellEndIndex] - originalBeforePosition;
467    }
468
469    if (extraHeightToPropagate) {
470        // Apply changed height by rowSpan cells to rows present at the end of the table
471        for (unsigned row = lastRowIndex + lastRowSpan + 1; row <= m_grid.size(); row++)
472            m_rowPos[row] += extraHeightToPropagate;
473    }
474}
475
476// Find out the baseline of the cell
477// If the cell's baseline is more then the row's baseline then the cell's baseline become the row's baseline
478// and if the row's baseline goes out of the row's boundries then adjust row height accordingly.
479void RenderTableSection::updateBaselineForCell(RenderTableCell* cell, unsigned row, LayoutUnit& baselineDescent)
480{
481    if (!cell->isBaselineAligned())
482        return;
483
484    // Ignoring the intrinsic padding as it depends on knowing the row's baseline, which won't be accurate
485    // until the end of this function.
486    LayoutUnit baselinePosition = cell->cellBaselinePosition() - cell->intrinsicPaddingBefore();
487    if (baselinePosition > cell->borderBefore() + (cell->paddingBefore() - cell->intrinsicPaddingBefore())) {
488        m_grid[row].baseline = max(m_grid[row].baseline, baselinePosition);
489
490        int cellStartRowBaselineDescent = 0;
491        if (cell->rowSpan() == 1) {
492            baselineDescent = max(baselineDescent, cell->logicalHeightForRowSizing() - baselinePosition);
493            cellStartRowBaselineDescent = baselineDescent;
494        }
495        m_rowPos[row + 1] = max<int>(m_rowPos[row + 1], m_rowPos[row] + m_grid[row].baseline + cellStartRowBaselineDescent);
496    }
497}
498
499int RenderTableSection::calcRowLogicalHeight()
500{
501#ifndef NDEBUG
502    SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
503#endif
504
505    ASSERT(!needsLayout());
506
507    RenderTableCell* cell;
508
509    RenderView* viewRenderer = view();
510    LayoutStateMaintainer statePusher(viewRenderer);
511
512    m_rowPos.resize(m_grid.size() + 1);
513    m_rowPos[0] = table()->vBorderSpacing();
514
515    SpanningRenderTableCells rowSpanCells;
516
517    for (unsigned r = 0; r < m_grid.size(); r++) {
518        m_grid[r].baseline = 0;
519        LayoutUnit baselineDescent = 0;
520
521        // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells).
522        m_rowPos[r + 1] = max(m_rowPos[r] + minimumValueForLength(m_grid[r].logicalHeight, 0, viewRenderer).round(), 0);
523
524        Row& row = m_grid[r].row;
525        unsigned totalCols = row.size();
526
527        for (unsigned c = 0; c < totalCols; c++) {
528            CellStruct& current = cellAt(r, c);
529            for (unsigned i = 0; i < current.cells.size(); i++) {
530                cell = current.cells[i];
531                if (current.inColSpan && cell->rowSpan() == 1)
532                    continue;
533
534                if (RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled()) {
535                    if (cell->rowSpan() > 1) {
536                        // For row spanning cells, we only handle them for the first row they span. This ensures we take their baseline into account.
537                        if (cell->rowIndex() == r) {
538                            rowSpanCells.append(cell);
539
540                            // Find out the baseline. The baseline is set on the first row in a rowSpan.
541                            updateBaselineForCell(cell, r, baselineDescent);
542                        }
543                        continue;
544                    }
545
546                    ASSERT(cell->rowSpan() == 1);
547                } else {
548                    // FIXME: We add all the logical row of a rowspan to the last rows
549                    // until crbug.com/78724 is fixed and the runtime flag removed.
550                    // This avoids propagating temporary regressions while we fix the bug.
551                    if ((cell->rowIndex() + cell->rowSpan() - 1) != r)
552                        continue;
553                }
554
555                if (cell->hasOverrideHeight()) {
556                    if (!statePusher.didPush()) {
557                        // Technically, we should also push state for the row, but since
558                        // rows don't push a coordinate transform, that's not necessary.
559                        statePusher.push(this, locationOffset());
560                    }
561                    cell->clearIntrinsicPadding();
562                    cell->clearOverrideSize();
563                    cell->forceChildLayout();
564                }
565
566                if (RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled()) {
567                    m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r] + cell->logicalHeightForRowSizing());
568
569                    // Find out the baseline.
570                    updateBaselineForCell(cell, r, baselineDescent);
571                } else {
572                    // For row spanning cells, |r| is the last row in the span.
573                    unsigned cellStartRow = cell->rowIndex();
574
575                    m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cell->logicalHeightForRowSizing());
576
577                    // Find out the baseline.
578                    updateBaselineForCell(cell, cellStartRow, baselineDescent);
579                }
580            }
581        }
582
583        // Add the border-spacing to our final position.
584        m_rowPos[r + 1] += borderSpacingForRow(r);
585        m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]);
586    }
587
588    if (!rowSpanCells.isEmpty()) {
589        ASSERT(RuntimeEnabledFeatures::rowSpanLogicalHeightSpreadingEnabled());
590        distributeRowSpanHeightToRows(rowSpanCells);
591    }
592
593    ASSERT(!needsLayout());
594
595    statePusher.pop();
596
597    return m_rowPos[m_grid.size()];
598}
599
600void RenderTableSection::layout()
601{
602    StackStats::LayoutCheckPoint layoutCheckPoint;
603    ASSERT(needsLayout());
604    ASSERT(!needsCellRecalc());
605    ASSERT(!table()->needsSectionRecalc());
606
607    // addChild may over-grow m_grid but we don't want to throw away the memory too early as addChild
608    // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure.
609    m_grid.shrinkToFit();
610
611    LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
612
613    const Vector<int>& columnPos = table()->columnPositions();
614
615    for (unsigned r = 0; r < m_grid.size(); ++r) {
616        Row& row = m_grid[r].row;
617        unsigned cols = row.size();
618        // First, propagate our table layout's information to the cells. This will mark the row as needing layout
619        // if there was a column logical width change.
620        for (unsigned startColumn = 0; startColumn < cols; ++startColumn) {
621            CellStruct& current = row[startColumn];
622            RenderTableCell* cell = current.primaryCell();
623            if (!cell || current.inColSpan)
624                continue;
625
626            unsigned endCol = startColumn;
627            unsigned cspan = cell->colSpan();
628            while (cspan && endCol < cols) {
629                ASSERT(endCol < table()->columns().size());
630                cspan -= table()->columns()[endCol].span;
631                endCol++;
632            }
633            int tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing();
634            cell->setCellLogicalWidth(tableLayoutLogicalWidth);
635        }
636
637        if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer)
638            rowRenderer->layoutIfNeeded();
639    }
640
641    statePusher.pop();
642    clearNeedsLayout();
643}
644
645void RenderTableSection::distributeExtraLogicalHeightToPercentRows(int& extraLogicalHeight, int totalPercent)
646{
647    if (!totalPercent)
648        return;
649
650    unsigned totalRows = m_grid.size();
651    int totalHeight = m_rowPos[totalRows] + extraLogicalHeight;
652    int totalLogicalHeightAdded = 0;
653    totalPercent = min(totalPercent, 100);
654    int rowHeight = m_rowPos[1] - m_rowPos[0];
655    for (unsigned r = 0; r < totalRows; ++r) {
656        if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) {
657            int toAdd = min<int>(extraLogicalHeight, (totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight);
658            // If toAdd is negative, then we don't want to shrink the row (this bug
659            // affected Outlook Web Access).
660            toAdd = max(0, toAdd);
661            totalLogicalHeightAdded += toAdd;
662            extraLogicalHeight -= toAdd;
663            totalPercent -= m_grid[r].logicalHeight.percent();
664        }
665        ASSERT(totalRows >= 1);
666        if (r < totalRows - 1)
667            rowHeight = m_rowPos[r + 2] - m_rowPos[r + 1];
668        m_rowPos[r + 1] += totalLogicalHeightAdded;
669    }
670}
671
672void RenderTableSection::distributeExtraLogicalHeightToAutoRows(int& extraLogicalHeight, unsigned autoRowsCount)
673{
674    if (!autoRowsCount)
675        return;
676
677    int totalLogicalHeightAdded = 0;
678    for (unsigned r = 0; r < m_grid.size(); ++r) {
679        if (autoRowsCount > 0 && m_grid[r].logicalHeight.isAuto()) {
680            // Recomputing |extraLogicalHeightForRow| guarantees that we properly ditribute round |extraLogicalHeight|.
681            int extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount;
682            totalLogicalHeightAdded += extraLogicalHeightForRow;
683            extraLogicalHeight -= extraLogicalHeightForRow;
684            --autoRowsCount;
685        }
686        m_rowPos[r + 1] += totalLogicalHeightAdded;
687    }
688}
689
690void RenderTableSection::distributeRemainingExtraLogicalHeight(int& extraLogicalHeight)
691{
692    unsigned totalRows = m_grid.size();
693
694    if (extraLogicalHeight <= 0 || !m_rowPos[totalRows])
695        return;
696
697    // FIXME: m_rowPos[totalRows] - m_rowPos[0] is the total rows' size.
698    int totalRowSize = m_rowPos[totalRows];
699    int totalLogicalHeightAdded = 0;
700    int previousRowPosition = m_rowPos[0];
701    for (unsigned r = 0; r < totalRows; r++) {
702        // weight with the original height
703        totalLogicalHeightAdded += extraLogicalHeight * (m_rowPos[r + 1] - previousRowPosition) / totalRowSize;
704        previousRowPosition = m_rowPos[r + 1];
705        m_rowPos[r + 1] += totalLogicalHeightAdded;
706    }
707
708    extraLogicalHeight -= totalLogicalHeightAdded;
709}
710
711int RenderTableSection::distributeExtraLogicalHeightToRows(int extraLogicalHeight)
712{
713    if (!extraLogicalHeight)
714        return extraLogicalHeight;
715
716    unsigned totalRows = m_grid.size();
717    if (!totalRows)
718        return extraLogicalHeight;
719
720    if (!m_rowPos[totalRows] && nextSibling())
721        return extraLogicalHeight;
722
723    unsigned autoRowsCount = 0;
724    int totalPercent = 0;
725    for (unsigned r = 0; r < totalRows; r++) {
726        if (m_grid[r].logicalHeight.isAuto())
727            ++autoRowsCount;
728        else if (m_grid[r].logicalHeight.isPercent())
729            totalPercent += m_grid[r].logicalHeight.percent();
730    }
731
732    int remainingExtraLogicalHeight = extraLogicalHeight;
733    distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent);
734    distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount);
735    distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight);
736    return extraLogicalHeight - remainingExtraLogicalHeight;
737}
738
739void RenderTableSection::layoutRows()
740{
741#ifndef NDEBUG
742    SetLayoutNeededForbiddenScope layoutForbiddenScope(this);
743#endif
744
745    ASSERT(!needsLayout());
746
747    unsigned totalRows = m_grid.size();
748
749    // Set the width of our section now.  The rows will also be this width.
750    setLogicalWidth(table()->contentLogicalWidth());
751    m_overflow.clear();
752    m_overflowingCells.clear();
753    m_forceSlowPaintPathWithOverflowingCell = false;
754
755    int vspacing = table()->vBorderSpacing();
756    unsigned nEffCols = table()->numEffCols();
757
758    LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
759
760    for (unsigned r = 0; r < totalRows; r++) {
761        // Set the row's x/y position and width/height.
762        if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
763            rowRenderer->setLocation(LayoutPoint(0, m_rowPos[r]));
764            rowRenderer->setLogicalWidth(logicalWidth());
765            rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
766            rowRenderer->updateLayerTransform();
767        }
768
769        int rowHeightIncreaseForPagination = 0;
770
771        for (unsigned c = 0; c < nEffCols; c++) {
772            CellStruct& cs = cellAt(r, c);
773            RenderTableCell* cell = cs.primaryCell();
774
775            if (!cell || cs.inColSpan)
776                continue;
777
778            int rowIndex = cell->rowIndex();
779            int rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing;
780
781            // Force percent height children to lay themselves out again.
782            // This will cause these children to grow to fill the cell.
783            // FIXME: There is still more work to do here to fully match WinIE (should
784            // it become necessary to do so).  In quirks mode, WinIE behaves like we
785            // do, but it will clip the cells that spill out of the table section.  In
786            // strict mode, Mozilla and WinIE both regrow the table to accommodate the
787            // new height of the cell (thus letting the percentages cause growth one
788            // time only).  We may also not be handling row-spanning cells correctly.
789            //
790            // Note also the oddity where replaced elements always flex, and yet blocks/tables do
791            // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
792            // match the behavior perfectly, but we'll continue to refine it as we discover new
793            // bugs. :)
794            bool cellChildrenFlex = false;
795            bool flexAllChildren = cell->style()->logicalHeight().isFixed()
796                || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight());
797
798            for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
799                if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) {
800                    // Tables with no sections do not flex.
801                    if (!o->isTable() || toRenderTable(o)->hasSections()) {
802                        o->setNeedsLayout(MarkOnlyThis);
803                        cellChildrenFlex = true;
804                    }
805                }
806            }
807
808            if (TrackedRendererListHashSet* percentHeightDescendants = cell->percentHeightDescendants()) {
809                TrackedRendererListHashSet::iterator end = percentHeightDescendants->end();
810                for (TrackedRendererListHashSet::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
811                    RenderBox* box = *it;
812                    if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren)
813                        continue;
814
815                    while (box != cell) {
816                        if (box->normalChildNeedsLayout())
817                            break;
818                        box->setChildNeedsLayout(MarkOnlyThis);
819                        box = box->containingBlock();
820                        ASSERT(box);
821                        if (!box)
822                            break;
823                    }
824                    cellChildrenFlex = true;
825                }
826            }
827
828            if (cellChildrenFlex) {
829                // Alignment within a cell is based off the calculated
830                // height, which becomes irrelevant once the cell has
831                // been resized based off its percentage.
832                cell->setOverrideLogicalContentHeightFromRowHeight(rHeight);
833                cell->forceChildLayout();
834
835                // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
836                if (cell->isBaselineAligned()) {
837                    LayoutUnit baseline = cell->cellBaselinePosition();
838                    if (baseline > cell->borderBefore() + cell->paddingBefore())
839                        m_grid[r].baseline = max(m_grid[r].baseline, baseline);
840                }
841            }
842
843            cell->computeIntrinsicPadding(rHeight);
844
845            LayoutRect oldCellRect = cell->frameRect();
846
847            setLogicalPositionForCell(cell, c);
848
849            if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())
850                cell->setChildNeedsLayout(MarkOnlyThis);
851
852            cell->layoutIfNeeded();
853
854            // FIXME: Make pagination work with vertical tables.
855            if (view()->layoutState()->pageLogicalHeight() && cell->logicalHeight() != rHeight) {
856                // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
857                // We'll also do a basic increase of the row height to accommodate the cell if it's bigger, but this isn't quite right
858                // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize.
859                if (cell->logicalHeight() > rHeight)
860                    rowHeightIncreaseForPagination = max<int>(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight);
861                cell->setLogicalHeight(rHeight);
862            }
863
864            LayoutSize childOffset(cell->location() - oldCellRect.location());
865            if (childOffset.width() || childOffset.height()) {
866                view()->addLayoutDelta(childOffset);
867
868                // If the child moved, we have to repaint it as well as any floating/positioned
869                // descendants.  An exception is if we need a layout.  In this case, we know we're going to
870                // repaint ourselves (and the child) anyway.
871                if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
872                    cell->repaintDuringLayoutIfMoved(oldCellRect);
873            }
874        }
875        if (rowHeightIncreaseForPagination) {
876            for (unsigned rowIndex = r + 1; rowIndex <= totalRows; rowIndex++)
877                m_rowPos[rowIndex] += rowHeightIncreaseForPagination;
878            for (unsigned c = 0; c < nEffCols; ++c) {
879                Vector<RenderTableCell*, 1>& cells = cellAt(r, c).cells;
880                for (size_t i = 0; i < cells.size(); ++i)
881                    cells[i]->setLogicalHeight(cells[i]->logicalHeight() + rowHeightIncreaseForPagination);
882            }
883        }
884    }
885
886    ASSERT(!needsLayout());
887
888    setLogicalHeight(m_rowPos[totalRows]);
889
890    computeOverflowFromCells(totalRows, nEffCols);
891
892    statePusher.pop();
893}
894
895void RenderTableSection::computeOverflowFromCells()
896{
897    unsigned totalRows = m_grid.size();
898    unsigned nEffCols = table()->numEffCols();
899    computeOverflowFromCells(totalRows, nEffCols);
900}
901
902void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols)
903{
904    unsigned totalCellsCount = nEffCols * totalRows;
905    int maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount;
906
907#ifndef NDEBUG
908    bool hasOverflowingCell = false;
909#endif
910    // Now that our height has been determined, add in overflow from cells.
911    for (unsigned r = 0; r < totalRows; r++) {
912        for (unsigned c = 0; c < nEffCols; c++) {
913            CellStruct& cs = cellAt(r, c);
914            RenderTableCell* cell = cs.primaryCell();
915            if (!cell || cs.inColSpan)
916                continue;
917            if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
918                continue;
919            addOverflowFromChild(cell);
920#ifndef NDEBUG
921            hasOverflowingCell |= cell->hasVisualOverflow();
922#endif
923            if (cell->hasVisualOverflow() && !m_forceSlowPaintPathWithOverflowingCell) {
924                m_overflowingCells.add(cell);
925                if (m_overflowingCells.size() > maxAllowedOverflowingCellsCount) {
926                    // We need to set m_forcesSlowPaintPath only if there is a least one overflowing cells as the hit testing code rely on this information.
927                    m_forceSlowPaintPathWithOverflowingCell = true;
928                    // The slow path does not make any use of the overflowing cells info, don't hold on to the memory.
929                    m_overflowingCells.clear();
930                }
931            }
932        }
933    }
934
935    ASSERT(hasOverflowingCell == this->hasOverflowingCell());
936}
937
938int RenderTableSection::calcOuterBorderBefore() const
939{
940    unsigned totalCols = table()->numEffCols();
941    if (!m_grid.size() || !totalCols)
942        return 0;
943
944    unsigned borderWidth = 0;
945
946    const BorderValue& sb = style()->borderBefore();
947    if (sb.style() == BHIDDEN)
948        return -1;
949    if (sb.style() > BHIDDEN)
950        borderWidth = sb.width();
951
952    const BorderValue& rb = firstChild()->style()->borderBefore();
953    if (rb.style() == BHIDDEN)
954        return -1;
955    if (rb.style() > BHIDDEN && rb.width() > borderWidth)
956        borderWidth = rb.width();
957
958    bool allHidden = true;
959    for (unsigned c = 0; c < totalCols; c++) {
960        const CellStruct& current = cellAt(0, c);
961        if (current.inColSpan || !current.hasCells())
962            continue;
963        const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
964        // FIXME: Don't repeat for the same col group
965        RenderTableCol* colGroup = table()->colElement(c);
966        if (colGroup) {
967            const BorderValue& gb = colGroup->style()->borderBefore();
968            if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
969                continue;
970            allHidden = false;
971            if (gb.style() > BHIDDEN && gb.width() > borderWidth)
972                borderWidth = gb.width();
973            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
974                borderWidth = cb.width();
975        } else {
976            if (cb.style() == BHIDDEN)
977                continue;
978            allHidden = false;
979            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
980                borderWidth = cb.width();
981        }
982    }
983    if (allHidden)
984        return -1;
985
986    return borderWidth / 2;
987}
988
989int RenderTableSection::calcOuterBorderAfter() const
990{
991    unsigned totalCols = table()->numEffCols();
992    if (!m_grid.size() || !totalCols)
993        return 0;
994
995    unsigned borderWidth = 0;
996
997    const BorderValue& sb = style()->borderAfter();
998    if (sb.style() == BHIDDEN)
999        return -1;
1000    if (sb.style() > BHIDDEN)
1001        borderWidth = sb.width();
1002
1003    const BorderValue& rb = lastChild()->style()->borderAfter();
1004    if (rb.style() == BHIDDEN)
1005        return -1;
1006    if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1007        borderWidth = rb.width();
1008
1009    bool allHidden = true;
1010    for (unsigned c = 0; c < totalCols; c++) {
1011        const CellStruct& current = cellAt(m_grid.size() - 1, c);
1012        if (current.inColSpan || !current.hasCells())
1013            continue;
1014        const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
1015        // FIXME: Don't repeat for the same col group
1016        RenderTableCol* colGroup = table()->colElement(c);
1017        if (colGroup) {
1018            const BorderValue& gb = colGroup->style()->borderAfter();
1019            if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
1020                continue;
1021            allHidden = false;
1022            if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1023                borderWidth = gb.width();
1024            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1025                borderWidth = cb.width();
1026        } else {
1027            if (cb.style() == BHIDDEN)
1028                continue;
1029            allHidden = false;
1030            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1031                borderWidth = cb.width();
1032        }
1033    }
1034    if (allHidden)
1035        return -1;
1036
1037    return (borderWidth + 1) / 2;
1038}
1039
1040int RenderTableSection::calcOuterBorderStart() const
1041{
1042    unsigned totalCols = table()->numEffCols();
1043    if (!m_grid.size() || !totalCols)
1044        return 0;
1045
1046    unsigned borderWidth = 0;
1047
1048    const BorderValue& sb = style()->borderStart();
1049    if (sb.style() == BHIDDEN)
1050        return -1;
1051    if (sb.style() > BHIDDEN)
1052        borderWidth = sb.width();
1053
1054    if (RenderTableCol* colGroup = table()->colElement(0)) {
1055        const BorderValue& gb = colGroup->style()->borderStart();
1056        if (gb.style() == BHIDDEN)
1057            return -1;
1058        if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1059            borderWidth = gb.width();
1060    }
1061
1062    bool allHidden = true;
1063    for (unsigned r = 0; r < m_grid.size(); r++) {
1064        const CellStruct& current = cellAt(r, 0);
1065        if (!current.hasCells())
1066            continue;
1067        // FIXME: Don't repeat for the same cell
1068        const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
1069        const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart();
1070        if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
1071            continue;
1072        allHidden = false;
1073        if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1074            borderWidth = cb.width();
1075        if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1076            borderWidth = rb.width();
1077    }
1078    if (allHidden)
1079        return -1;
1080
1081    return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1082}
1083
1084int RenderTableSection::calcOuterBorderEnd() const
1085{
1086    unsigned totalCols = table()->numEffCols();
1087    if (!m_grid.size() || !totalCols)
1088        return 0;
1089
1090    unsigned borderWidth = 0;
1091
1092    const BorderValue& sb = style()->borderEnd();
1093    if (sb.style() == BHIDDEN)
1094        return -1;
1095    if (sb.style() > BHIDDEN)
1096        borderWidth = sb.width();
1097
1098    if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) {
1099        const BorderValue& gb = colGroup->style()->borderEnd();
1100        if (gb.style() == BHIDDEN)
1101            return -1;
1102        if (gb.style() > BHIDDEN && gb.width() > borderWidth)
1103            borderWidth = gb.width();
1104    }
1105
1106    bool allHidden = true;
1107    for (unsigned r = 0; r < m_grid.size(); r++) {
1108        const CellStruct& current = cellAt(r, totalCols - 1);
1109        if (!current.hasCells())
1110            continue;
1111        // FIXME: Don't repeat for the same cell
1112        const BorderValue& cb = current.primaryCell()->style()->borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
1113        const BorderValue& rb = current.primaryCell()->parent()->style()->borderEnd();
1114        if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
1115            continue;
1116        allHidden = false;
1117        if (cb.style() > BHIDDEN && cb.width() > borderWidth)
1118            borderWidth = cb.width();
1119        if (rb.style() > BHIDDEN && rb.width() > borderWidth)
1120            borderWidth = rb.width();
1121    }
1122    if (allHidden)
1123        return -1;
1124
1125    return (borderWidth + (table()->style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1126}
1127
1128void RenderTableSection::recalcOuterBorder()
1129{
1130    m_outerBorderBefore = calcOuterBorderBefore();
1131    m_outerBorderAfter = calcOuterBorderAfter();
1132    m_outerBorderStart = calcOuterBorderStart();
1133    m_outerBorderEnd = calcOuterBorderEnd();
1134}
1135
1136int RenderTableSection::firstLineBoxBaseline() const
1137{
1138    if (!m_grid.size())
1139        return -1;
1140
1141    int firstLineBaseline = m_grid[0].baseline;
1142    if (firstLineBaseline)
1143        return firstLineBaseline + m_rowPos[0];
1144
1145    firstLineBaseline = -1;
1146    const Row& firstRow = m_grid[0].row;
1147    for (size_t i = 0; i < firstRow.size(); ++i) {
1148        const CellStruct& cs = firstRow.at(i);
1149        const RenderTableCell* cell = cs.primaryCell();
1150        // Only cells with content have a baseline
1151        if (cell && cell->contentLogicalHeight())
1152            firstLineBaseline = max<int>(firstLineBaseline, cell->logicalTop() + cell->paddingBefore() + cell->borderBefore() + cell->contentLogicalHeight());
1153    }
1154
1155    return firstLineBaseline;
1156}
1157
1158void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1159{
1160    ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
1161
1162    // put this back in when all layout tests can handle it
1163    // ASSERT(!needsLayout());
1164    // avoid crashing on bugs that cause us to paint with dirty layout
1165    if (needsLayout())
1166        return;
1167
1168    unsigned totalRows = m_grid.size();
1169    unsigned totalCols = table()->columns().size();
1170
1171    if (!totalRows || !totalCols)
1172        return;
1173
1174    LayoutPoint adjustedPaintOffset = paintOffset + location();
1175
1176    PaintPhase phase = paintInfo.phase;
1177    bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
1178    paintObject(paintInfo, adjustedPaintOffset);
1179    if (pushedClip)
1180        popContentsClip(paintInfo, phase, adjustedPaintOffset);
1181
1182    if ((phase == PaintPhaseOutline || phase == PaintPhaseSelfOutline) && style()->visibility() == VISIBLE)
1183        paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
1184}
1185
1186static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
1187{
1188    return elem1->rowIndex() < elem2->rowIndex();
1189}
1190
1191// This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
1192// to sort both on rows and columns to properly repaint.
1193static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
1194{
1195    if (elem1->rowIndex() != elem2->rowIndex())
1196        return elem1->rowIndex() < elem2->rowIndex();
1197
1198    return elem1->col() < elem2->col();
1199}
1200
1201void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1202{
1203    LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1204    PaintPhase paintPhase = paintInfo.phase;
1205    RenderTableRow* row = toRenderTableRow(cell->parent());
1206
1207    if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) {
1208        // We need to handle painting a stack of backgrounds.  This stack (from bottom to top) consists of
1209        // the column group, column, row group, row, and then the cell.
1210        RenderTableCol* column = table()->colElement(cell->col());
1211        RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0;
1212
1213        // Column groups and columns first.
1214        // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
1215        // the stack, since we have already opened a transparency layer (potentially) for the table row group.
1216        // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
1217        // cell.
1218        cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
1219        cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
1220
1221        // Paint the row group next.
1222        cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
1223
1224        // Paint the row next, but only if it doesn't have a layer.  If a row has a layer, it will be responsible for
1225        // painting the row background for the cell.
1226        if (!row->hasSelfPaintingLayer())
1227            cell->paintBackgroundsBehindCell(paintInfo, cellPoint, row);
1228    }
1229    if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
1230        cell->paint(paintInfo, cellPoint);
1231}
1232
1233LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
1234{
1235    LayoutRect tableAlignedRect(rect);
1236
1237    flipForWritingMode(tableAlignedRect);
1238
1239    if (!style()->isHorizontalWritingMode())
1240        tableAlignedRect = tableAlignedRect.transposedRect();
1241
1242    const Vector<int>& columnPos = table()->columnPositions();
1243    // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1244    if (!style()->isLeftToRightDirection())
1245        tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
1246
1247    return tableAlignedRect;
1248}
1249
1250CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
1251{
1252    if (m_forceSlowPaintPathWithOverflowingCell)
1253        return fullTableRowSpan();
1254
1255    CellSpan coveredRows = spannedRows(damageRect);
1256
1257    // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
1258    if (coveredRows.start() >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
1259        --coveredRows.start();
1260
1261    if (!coveredRows.end() && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY())
1262        ++coveredRows.end();
1263
1264    return coveredRows;
1265}
1266
1267CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
1268{
1269    if (m_forceSlowPaintPathWithOverflowingCell)
1270        return fullTableColumnSpan();
1271
1272    CellSpan coveredColumns = spannedColumns(damageRect);
1273
1274    const Vector<int>& columnPos = table()->columnPositions();
1275    // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
1276    if (coveredColumns.start() >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
1277        --coveredColumns.start();
1278
1279    if (!coveredColumns.end() && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX())
1280        ++coveredColumns.end();
1281
1282    return coveredColumns;
1283}
1284
1285CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect) const
1286{
1287    // Find the first row that starts after rect top.
1288    unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
1289
1290    if (nextRow == m_rowPos.size())
1291        return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
1292
1293    unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
1294
1295    // Find the first row that starts after rect bottom.
1296    unsigned endRow;
1297    if (m_rowPos[nextRow] >= flippedRect.maxY())
1298        endRow = nextRow;
1299    else {
1300        endRow = std::upper_bound(m_rowPos.begin() + nextRow, m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
1301        if (endRow == m_rowPos.size())
1302            endRow = m_rowPos.size() - 1;
1303    }
1304
1305    return CellSpan(startRow, endRow);
1306}
1307
1308CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect) const
1309{
1310    const Vector<int>& columnPos = table()->columnPositions();
1311
1312    // Find the first column that starts after rect left.
1313    // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the
1314    // cell on the logical top/left.
1315    // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
1316    // matches the behavior of other browsers.
1317    unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
1318
1319    if (nextColumn == columnPos.size())
1320        return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
1321
1322    unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
1323
1324    // Find the first column that starts after rect right.
1325    unsigned endColumn;
1326    if (columnPos[nextColumn] >= flippedRect.maxX())
1327        endColumn = nextColumn;
1328    else {
1329        endColumn = std::upper_bound(columnPos.begin() + nextColumn, columnPos.end(), flippedRect.maxX()) - columnPos.begin();
1330        if (endColumn == columnPos.size())
1331            endColumn = columnPos.size() - 1;
1332    }
1333
1334    return CellSpan(startColumn, endColumn);
1335}
1336
1337
1338void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1339{
1340    PaintPhase paintPhase = paintInfo.phase;
1341
1342    LayoutRect localRepaintRect = paintInfo.rect;
1343    localRepaintRect.moveBy(-paintOffset);
1344    localRepaintRect.inflate(maximalOutlineSize(paintPhase));
1345
1346    LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
1347
1348    CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
1349    CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
1350
1351    if (dirtiedColumns.start() < dirtiedColumns.end()) {
1352        if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) {
1353            if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1354                // Collapsed borders are painted from the bottom right to the top left so that precedence
1355                // due to cell position is respected.
1356                for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) {
1357                    unsigned row = r - 1;
1358                    for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) {
1359                        unsigned col = c - 1;
1360                        CellStruct& current = cellAt(row, col);
1361                        RenderTableCell* cell = current.primaryCell();
1362                        if (!cell || (row > dirtiedRows.start() && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && primaryCellAt(row, col - 1) == cell))
1363                            continue;
1364                        LayoutPoint cellPoint = flipForWritingModeForChild(cell, paintOffset);
1365                        cell->paintCollapsedBorders(paintInfo, cellPoint);
1366                    }
1367                }
1368            } else {
1369                // Draw the dirty cells in the order that they appear.
1370                for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1371                    RenderTableRow* row = m_grid[r].rowRenderer;
1372                    if (row && !row->hasSelfPaintingLayer())
1373                        row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1374                    for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1375                        CellStruct& current = cellAt(r, c);
1376                        RenderTableCell* cell = current.primaryCell();
1377                        if (!cell || (r > dirtiedRows.start() && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && primaryCellAt(r, c - 1) == cell))
1378                            continue;
1379                        paintCell(cell, paintInfo, paintOffset);
1380                    }
1381                }
1382            }
1383        } else {
1384            // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
1385#ifndef NDEBUG
1386            unsigned totalRows = m_grid.size();
1387            unsigned totalCols = table()->columns().size();
1388            ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
1389#endif
1390
1391            // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
1392            Vector<RenderTableCell*> cells;
1393            copyToVector(m_overflowingCells, cells);
1394
1395            HashSet<RenderTableCell*> spanningCells;
1396
1397            for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) {
1398                RenderTableRow* row = m_grid[r].rowRenderer;
1399                if (row && !row->hasSelfPaintingLayer())
1400                    row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1401                for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) {
1402                    CellStruct& current = cellAt(r, c);
1403                    if (!current.hasCells())
1404                        continue;
1405                    for (unsigned i = 0; i < current.cells.size(); ++i) {
1406                        if (m_overflowingCells.contains(current.cells[i]))
1407                            continue;
1408
1409                        if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
1410                            if (!spanningCells.add(current.cells[i]).isNewEntry)
1411                                continue;
1412                        }
1413
1414                        cells.append(current.cells[i]);
1415                    }
1416                }
1417            }
1418
1419            // Sort the dirty cells by paint order.
1420            if (!m_overflowingCells.size())
1421                std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
1422            else
1423                std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
1424
1425            if (paintInfo.phase == PaintPhaseCollapsedTableBorders) {
1426                for (unsigned i = cells.size(); i > 0; --i) {
1427                    LayoutPoint cellPoint = flipForWritingModeForChild(cells[i - 1], paintOffset);
1428                    cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
1429                }
1430            } else {
1431                for (unsigned i = 0; i < cells.size(); ++i)
1432                    paintCell(cells[i], paintInfo, paintOffset);
1433            }
1434        }
1435    }
1436}
1437
1438void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
1439{
1440    // FIXME: Examine cells and repaint only the rect the image paints in.
1441    repaint();
1442}
1443
1444void RenderTableSection::recalcCells()
1445{
1446    ASSERT(m_needsCellRecalc);
1447    // We reset the flag here to ensure that |addCell| works. This is safe to do as
1448    // fillRowsWithDefaultStartingAtPosition makes sure we match the table's columns
1449    // representation.
1450    m_needsCellRecalc = false;
1451
1452    m_cCol = 0;
1453    m_cRow = 0;
1454    m_grid.clear();
1455
1456    for (RenderObject* row = firstChild(); row; row = row->nextSibling()) {
1457        if (row->isTableRow()) {
1458            unsigned insertionRow = m_cRow;
1459            m_cRow++;
1460            m_cCol = 0;
1461            ensureRows(m_cRow);
1462
1463            RenderTableRow* tableRow = toRenderTableRow(row);
1464            m_grid[insertionRow].rowRenderer = tableRow;
1465            tableRow->setRowIndex(insertionRow);
1466            setRowLogicalHeightToRowStyleLogicalHeight(m_grid[insertionRow]);
1467
1468            for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
1469                if (!cell->isTableCell())
1470                    continue;
1471
1472                RenderTableCell* tableCell = toRenderTableCell(cell);
1473                addCell(tableCell, tableRow);
1474            }
1475        }
1476    }
1477
1478    m_grid.shrinkToFit();
1479    setNeedsLayout();
1480}
1481
1482// FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
1483void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
1484{
1485    if (needsCellRecalc())
1486        return;
1487
1488    setRowLogicalHeightToRowStyleLogicalHeight(m_grid[rowIndex]);
1489
1490    for (RenderObject* cell = m_grid[rowIndex].rowRenderer->firstChild(); cell; cell = cell->nextSibling()) {
1491        if (!cell->isTableCell())
1492            continue;
1493
1494        updateLogicalHeightForCell(m_grid[rowIndex], toRenderTableCell(cell));
1495    }
1496}
1497
1498void RenderTableSection::setNeedsCellRecalc()
1499{
1500    m_needsCellRecalc = true;
1501    if (RenderTable* t = table())
1502        t->setNeedsSectionRecalc();
1503}
1504
1505unsigned RenderTableSection::numColumns() const
1506{
1507    unsigned result = 0;
1508
1509    for (unsigned r = 0; r < m_grid.size(); ++r) {
1510        for (unsigned c = result; c < table()->numEffCols(); ++c) {
1511            const CellStruct& cell = cellAt(r, c);
1512            if (cell.hasCells() || cell.inColSpan)
1513                result = c;
1514        }
1515    }
1516
1517    return result + 1;
1518}
1519
1520const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell* cell) const
1521{
1522    ASSERT(cell->isFirstOrLastCellInRow());
1523    return hasSameDirectionAs(cell) ? style()->borderStart() : style()->borderEnd();
1524}
1525
1526const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell* cell) const
1527{
1528    ASSERT(cell->isFirstOrLastCellInRow());
1529    return hasSameDirectionAs(cell) ? style()->borderEnd() : style()->borderStart();
1530}
1531
1532const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
1533{
1534    unsigned adjoiningStartCellColumnIndex = hasSameDirectionAs(table()) ? 0 : table()->lastColumnIndex();
1535    return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
1536}
1537
1538const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
1539{
1540    unsigned adjoiningEndCellColumnIndex = hasSameDirectionAs(table()) ? table()->lastColumnIndex() : 0;
1541    return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
1542}
1543
1544void RenderTableSection::appendColumn(unsigned pos)
1545{
1546    ASSERT(!m_needsCellRecalc);
1547
1548    for (unsigned row = 0; row < m_grid.size(); ++row)
1549        m_grid[row].row.resize(pos + 1);
1550}
1551
1552void RenderTableSection::splitColumn(unsigned pos, unsigned first)
1553{
1554    ASSERT(!m_needsCellRecalc);
1555
1556    if (m_cCol > pos)
1557        m_cCol++;
1558    for (unsigned row = 0; row < m_grid.size(); ++row) {
1559        Row& r = m_grid[row].row;
1560        r.insert(pos + 1, CellStruct());
1561        if (r[pos].hasCells()) {
1562            r[pos + 1].cells.append(r[pos].cells);
1563            RenderTableCell* cell = r[pos].primaryCell();
1564            ASSERT(cell);
1565            ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0));
1566            unsigned colleft = cell->colSpan() - r[pos].inColSpan;
1567            if (first > colleft)
1568              r[pos + 1].inColSpan = 0;
1569            else
1570              r[pos + 1].inColSpan = first + r[pos].inColSpan;
1571        } else {
1572            r[pos + 1].inColSpan = 0;
1573        }
1574    }
1575}
1576
1577// Hit Testing
1578bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1579{
1580    // If we have no children then we have nothing to do.
1581    if (!firstChild())
1582        return false;
1583
1584    // Table sections cannot ever be hit tested.  Effectively they do not exist.
1585    // Just forward to our children always.
1586    LayoutPoint adjustedLocation = accumulatedOffset + location();
1587
1588    if (hasOverflowClip() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region())))
1589        return false;
1590
1591    if (hasOverflowingCell()) {
1592        for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1593            // FIXME: We have to skip over inline flows, since they can show up inside table rows
1594            // at the moment (a demoted inline <form> for example). If we ever implement a
1595            // table-specific hit-test method (which we should do for performance reasons anyway),
1596            // then we can remove this check.
1597            if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer()) {
1598                LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1599                if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1600                    updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1601                    return true;
1602                }
1603            }
1604        }
1605        return false;
1606    }
1607
1608    recalcCellsIfNeeded();
1609
1610    LayoutRect hitTestRect = locationInContainer.boundingBox();
1611    hitTestRect.moveBy(-adjustedLocation);
1612
1613    LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
1614    CellSpan rowSpan = spannedRows(tableAlignedRect);
1615    CellSpan columnSpan = spannedColumns(tableAlignedRect);
1616
1617    // Now iterate over the spanned rows and columns.
1618    for (unsigned hitRow = rowSpan.start(); hitRow < rowSpan.end(); ++hitRow) {
1619        for (unsigned hitColumn = columnSpan.start(); hitColumn < columnSpan.end(); ++hitColumn) {
1620            CellStruct& current = cellAt(hitRow, hitColumn);
1621
1622            // If the cell is empty, there's nothing to do
1623            if (!current.hasCells())
1624                continue;
1625
1626            for (unsigned i = current.cells.size() ; i; ) {
1627                --i;
1628                RenderTableCell* cell = current.cells[i];
1629                LayoutPoint cellPoint = flipForWritingModeForChild(cell, adjustedLocation);
1630                if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
1631                    updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
1632                    return true;
1633                }
1634            }
1635            if (!result.isRectBasedTest())
1636                break;
1637        }
1638        if (!result.isRectBasedTest())
1639            break;
1640    }
1641
1642    return false;
1643}
1644
1645void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell* cell)
1646{
1647    if (!table()->collapseBorders())
1648        return;
1649
1650    for (int side = CBSBefore; side <= CBSEnd; ++side)
1651        m_cellsCollapsedBorders.remove(make_pair(cell, side));
1652}
1653
1654void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side, CollapsedBorderValue border)
1655{
1656    ASSERT(table()->collapseBorders());
1657    m_cellsCollapsedBorders.set(make_pair(cell, side), border);
1658}
1659
1660CollapsedBorderValue& RenderTableSection::cachedCollapsedBorder(const RenderTableCell* cell, CollapsedBorderSide side)
1661{
1662    ASSERT(table()->collapseBorders());
1663    HashMap<pair<const RenderTableCell*, int>, CollapsedBorderValue>::iterator it = m_cellsCollapsedBorders.find(make_pair(cell, side));
1664    ASSERT(it != m_cellsCollapsedBorders.end());
1665    return it->value;
1666}
1667
1668RenderTableSection* RenderTableSection::createAnonymousWithParentRenderer(const RenderObject* parent)
1669{
1670    RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE_ROW_GROUP);
1671    RenderTableSection* newSection = new RenderTableSection(0);
1672    newSection->setDocumentForAnonymous(parent->document());
1673    newSection->setStyle(newStyle.release());
1674    return newSection;
1675}
1676
1677void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const
1678{
1679    LayoutPoint oldCellLocation = cell->location();
1680
1681    LayoutPoint cellLocation(0, m_rowPos[cell->rowIndex()]);
1682    int horizontalBorderSpacing = table()->hBorderSpacing();
1683
1684    // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1685    if (!style()->isLeftToRightDirection())
1686        cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing);
1687    else
1688        cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing);
1689
1690    cell->setLogicalLocation(cellLocation);
1691    view()->addLayoutDelta(oldCellLocation - cell->location());
1692}
1693
1694} // namespace WebCore
1695