RenderTableSection.cpp 45.5 KB
Newer Older
darin@apple.com's avatar
darin@apple.com committed
1
/*
2
3
4
5
6
 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
 *           (C) 1997 Torben Weis (weis@kde.org)
 *           (C) 1998 Waldo Bastian (bastian@kde.org)
 *           (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
7
 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
ap's avatar
ap committed
8
 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
ddkilzer's avatar
ddkilzer committed
22
23
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
24
25
26
27
 */

#include "config.h"
#include "RenderTableSection.h"
28
#include "CachedImage.h"
darin's avatar
darin committed
29
#include "Document.h"
30
#include "HitTestResult.h"
darin's avatar
darin committed
31
#include "HTMLNames.h"
32
#include "PaintInfo.h"
33
34
35
#include "RenderTableCell.h"
#include "RenderTableCol.h"
#include "RenderTableRow.h"
weinig's avatar
weinig committed
36
#include "RenderView.h"
37
#include <limits>
38
#include <wtf/HashSet.h>
ddkilzer's avatar
ddkilzer committed
39
#include <wtf/Vector.h>
40

darin's avatar
darin committed
41
42
using namespace std;

43
44
45
46
namespace WebCore {

using namespace HTMLNames;

47
static inline void setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct* row)
48
49
{
    ASSERT(row && row->rowRenderer);
50
51
52
    row->logicalHeight = row->rowRenderer->style()->logicalHeight();
    if (row->logicalHeight.isRelative())
        row->logicalHeight = Length();
53
54
}

darin's avatar
darin committed
55
RenderTableSection::RenderTableSection(Node* node)
56
    : RenderBox(node)
ddkilzer's avatar
ddkilzer committed
57
58
59
    , m_gridRows(0)
    , m_cCol(0)
    , m_cRow(-1)
60
61
62
63
    , m_outerBorderStart(0)
    , m_outerBorderEnd(0)
    , m_outerBorderBefore(0)
    , m_outerBorderAfter(0)
64
    , m_needsCellRecalc(false)
adele's avatar
adele committed
65
    , m_hasOverflowingCell(false)
66
    , m_hasMultipleCellLevels(false)
67
68
{
    // init RenderObject attributes
69
    setInline(false); // our object is not Inline
70
71
72
73
74
75
76
77
78
}

RenderTableSection::~RenderTableSection()
{
    clearGrid();
}

void RenderTableSection::destroy()
{
antti's avatar
antti committed
79
80
    RenderTable* recalcTable = table();
    
81
    RenderBox::destroy();
antti's avatar
antti committed
82
    
83
84
    // recalc cell info because RenderTable has unguarded pointers
    // stored that point to this RenderTableSection.
antti's avatar
antti committed
85
86
    if (recalcTable)
        recalcTable->setNeedsSectionRecalc();
87
88
}

darin's avatar
darin committed
89
void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild)
90
{
hyatt's avatar
hyatt committed
91
92
93
94
    // Make sure we don't append things after :after-generated content if we have it.
    if (!beforeChild && isAfterContent(lastChild()))
        beforeChild = lastChild();

95
    if (!child->isTableRow()) {
darin's avatar
darin committed
96
97
98
99
        RenderObject* last = beforeChild;
        if (!last)
            last = lastChild();
        if (last && last->isAnonymous()) {
100
101
102
            if (beforeChild == last)
                beforeChild = last->firstChild();
            last->addChild(child, beforeChild);
darin's avatar
darin committed
103
            return;
104
        }
darin's avatar
darin committed
105

106
107
        // If beforeChild is inside an anonymous cell/row, insert into the cell or into
        // the anonymous row containing it, if there is one.
darin's avatar
darin committed
108
109
110
111
112
113
114
115
        RenderObject* lastBox = last;
        while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow())
            lastBox = lastBox->parent();
        if (lastBox && lastBox->isAnonymous()) {
            lastBox->addChild(child, beforeChild);
            return;
        }

116
        RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table row */);
117
        RefPtr<RenderStyle> newStyle = RenderStyle::create();
darin's avatar
darin committed
118
119
        newStyle->inheritFrom(style());
        newStyle->setDisplay(TABLE_ROW);
120
        row->setStyle(newStyle.release());
darin's avatar
darin committed
121
        addChild(row, beforeChild);
122
123
124
125
126
        row->addChild(child);
        return;
    }

    if (beforeChild)
ddkilzer's avatar
ddkilzer committed
127
        setNeedsCellRecalc();
128

ddkilzer's avatar
ddkilzer committed
129
130
    ++m_cRow;
    m_cCol = 0;
131

132
133
134
135
    // make sure we have enough rows
    if (!ensureRows(m_cRow + 1))
        return;

darin@apple.com's avatar
darin@apple.com committed
136
    m_grid[m_cRow].rowRenderer = toRenderTableRow(child);
137

138
    if (!beforeChild)
139
        setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(&m_grid[m_cRow]);
140

ddkilzer's avatar
ddkilzer committed
141
    // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that.
mitz@apple.com's avatar
mitz@apple.com committed
142
    while (beforeChild && beforeChild->parent() != this)
mjs's avatar
mjs committed
143
144
        beforeChild = beforeChild->parent();

bdakin@apple.com's avatar
bdakin@apple.com committed
145
    ASSERT(!beforeChild || beforeChild->isTableRow());
146
    RenderBox::addChild(child, beforeChild);
147
148
}

149
150
151
void RenderTableSection::removeChild(RenderObject* oldChild)
{
    setNeedsCellRecalc();
152
    RenderBox::removeChild(oldChild);
153
154
}

155
156
bool RenderTableSection::ensureRows(int numRows)
{
ddkilzer's avatar
ddkilzer committed
157
    int nRows = m_gridRows;
158
    if (numRows > nRows) {
ddkilzer's avatar
ddkilzer committed
159
        if (numRows > static_cast<int>(m_grid.size())) {
160
161
            size_t maxSize = numeric_limits<size_t>::max() / sizeof(RowStruct);
            if (static_cast<size_t>(numRows) > maxSize)
162
                return false;
darin@apple.com's avatar
darin@apple.com committed
163
            m_grid.grow(numRows);
164
        }
ddkilzer's avatar
ddkilzer committed
165
        m_gridRows = numRows;
bdakin@apple.com's avatar
bdakin@apple.com committed
166
        int nCols = max(1, table()->numEffCols());
ap's avatar
ap committed
167
        for (int r = nRows; r < numRows; r++) {
ddkilzer's avatar
ddkilzer committed
168
169
            m_grid[r].row = new Row(nCols);
            m_grid[r].rowRenderer = 0;
170
            m_grid[r].baseline = 0;
171
            m_grid[r].logicalHeight = Length();
ap's avatar
ap committed
172
        }
173
174
175
176
177
    }

    return true;
}

hyatt@apple.com's avatar
hyatt@apple.com committed
178
void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
179
180
181
{
    int rSpan = cell->rowSpan();
    int cSpan = cell->colSpan();
ddkilzer's avatar
ddkilzer committed
182
    Vector<RenderTable::ColumnStruct>& columns = table()->columns();
183
184
185
186
187
188
189
190
    int nCols = columns.size();

    // ### mozilla still seems to do the old HTML way, even for strict DTD
    // (see the annotation on table cell layouting in the CSS specs and the testcase below:
    // <TABLE border>
    // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
    // <TR><TD colspan="2">5
    // </TABLE>
191
    while (m_cCol < nCols && (cellAt(m_cRow, m_cCol).hasCells() || cellAt(m_cRow, m_cCol).inColSpan))
ddkilzer's avatar
ddkilzer committed
192
        m_cCol++;
193

194
    if (rSpan == 1) {
ap's avatar
ap committed
195
        // we ignore height settings on rowspan cells
196
197
198
199
        Length logicalHeight = cell->style()->logicalHeight();
        if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) {
            Length cRowLogicalHeight = m_grid[m_cRow].logicalHeight;
            switch (logicalHeight.type()) {
200
                case Percent:
201
202
203
                    if (!(cRowLogicalHeight.isPercent()) ||
                        (cRowLogicalHeight.isPercent() && cRowLogicalHeight.rawValue() < logicalHeight.rawValue()))
                        m_grid[m_cRow].logicalHeight = logicalHeight;
204
205
                        break;
                case Fixed:
206
207
208
                    if (cRowLogicalHeight.type() < Percent ||
                        (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
                        m_grid[m_cRow].logicalHeight = logicalHeight;
209
210
211
212
                    break;
                case Relative:
                default:
                    break;
ap's avatar
ap committed
213
214
            }
        }
215
216
217
    }

    // make sure we have enough rows
ddkilzer's avatar
ddkilzer committed
218
    if (!ensureRows(m_cRow + rSpan))
219
220
        return;

ddkilzer's avatar
ddkilzer committed
221
    m_grid[m_cRow].rowRenderer = row;
222

ddkilzer's avatar
ddkilzer committed
223
    int col = m_cCol;
224
    // tell the cell where it is
225
    bool inColSpan = false;
226
    while (cSpan) {
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
        int currentSpan;
        if (m_cCol >= nCols) {
            table()->appendColumn(cSpan);
            currentSpan = cSpan;
        } else {
            if (cSpan < (int)columns[m_cCol].span)
                table()->splitColumn(m_cCol, cSpan);
            currentSpan = columns[m_cCol].span;
        }
        for (int r = 0; r < rSpan; r++) {
            CellStruct& c = cellAt(m_cRow + r, m_cCol);
            ASSERT(cell);
            c.cells.append(cell);
            // If cells overlap then we take the slow path for painting.
            if (c.cells.size() > 1)
                m_hasMultipleCellLevels = true;
            if (inColSpan)
                c.inColSpan = true;
        }
        m_cCol++;
        cSpan -= currentSpan;
        inColSpan = true;
249
    }
eric@webkit.org's avatar
eric@webkit.org committed
250
251
    cell->setRow(m_cRow);
    cell->setCol(table()->effColToCol(col));
252
253
}

254
void RenderTableSection::setCellLogicalWidths()
255
{
ddkilzer's avatar
ddkilzer committed
256
    Vector<int>& columnPos = table()->columnPositions();
257

258
259
    LayoutStateMaintainer statePusher(view());
    
ddkilzer's avatar
ddkilzer committed
260
261
    for (int i = 0; i < m_gridRows; i++) {
        Row& row = *m_grid[i].row;
ap's avatar
ap committed
262
263
        int cols = row.size();
        for (int j = 0; j < cols; j++) {
264
265
            CellStruct& current = row[j];
            RenderTableCell* cell = current.primaryCell();
266
            if (!cell || current.inColSpan)
267
              continue;
ap's avatar
ap committed
268
269
270
            int endCol = j;
            int cspan = cell->colSpan();
            while (cspan && endCol < cols) {
271
                ASSERT(endCol < (int)table()->columns().size());
ddkilzer's avatar
ddkilzer committed
272
                cspan -= table()->columns()[endCol].span;
ap's avatar
ap committed
273
274
275
                endCol++;
            }
            int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing();
276
277
            int oldLogicalWidth = cell->logicalWidth();
            if (w != oldLogicalWidth) {
ap's avatar
ap committed
278
                cell->setNeedsLayout(true);
weinig's avatar
weinig committed
279
                if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) {
280
                    if (!statePusher.didPush()) {
weinig's avatar
weinig committed
281
282
                        // Technically, we should also push state for the row, but since
                        // rows don't push a coordinate transform, that's not necessary.
283
                        statePusher.push(this, IntSize(x(), y()));
weinig's avatar
weinig committed
284
                    }
hyatt's avatar
hyatt committed
285
                    cell->repaint();
weinig's avatar
weinig committed
286
                }
287
                cell->updateLogicalWidth(w);
ap's avatar
ap committed
288
289
            }
        }
290
    }
weinig's avatar
weinig committed
291
    
292
    statePusher.pop(); // only pops if we pushed
293
294
}

295
int RenderTableSection::calcRowLogicalHeight()
296
{
297
298
299
300
301
302
#ifndef NDEBUG
    setNeedsLayoutIsForbidden(true);
#endif

    ASSERT(!needsLayout());

ddkilzer's avatar
ddkilzer committed
303
    RenderTableCell* cell;
304
305

    int spacing = table()->vBorderSpacing();
306
307

    LayoutStateMaintainer statePusher(view());
308

ddkilzer's avatar
ddkilzer committed
309
310
    m_rowPos.resize(m_gridRows + 1);
    m_rowPos[0] = spacing;
311

ddkilzer's avatar
ddkilzer committed
312
313
    for (int r = 0; r < m_gridRows; r++) {
        m_rowPos[r + 1] = 0;
314
        m_grid[r].baseline = 0;
ap's avatar
ap committed
315
316
        int baseline = 0;
        int bdesc = 0;
317
        int ch = m_grid[r].logicalHeight.calcMinValue(0);
ddkilzer's avatar
ddkilzer committed
318
        int pos = m_rowPos[r] + ch + (m_grid[r].rowRenderer ? spacing : 0);
319

ddkilzer's avatar
ddkilzer committed
320
        m_rowPos[r + 1] = max(m_rowPos[r + 1], pos);
321

ddkilzer's avatar
ddkilzer committed
322
        Row* row = m_grid[r].row;
ap's avatar
ap committed
323
        int totalCols = row->size();
324

ap's avatar
ap committed
325
        for (int c = 0; c < totalCols; c++) {
326
327
            CellStruct& current = cellAt(r, c);
            cell = current.primaryCell();
328
329

            if (!cell || current.inColSpan)
ap's avatar
ap committed
330
                continue;
331
332

            if ((cell->row() + cell->rowSpan() - 1) > r)
ap's avatar
ap committed
333
                continue;
334

ddkilzer's avatar
ddkilzer committed
335
            int indx = max(r - cell->rowSpan() + 1, 0);
336
337

            if (cell->overrideSize() != -1) {
338
                if (!statePusher.didPush()) {
weinig's avatar
weinig committed
339
340
                    // Technically, we should also push state for the row, but since
                    // rows don't push a coordinate transform, that's not necessary.
341
                    statePusher.push(this, IntSize(x(), y()));
weinig's avatar
weinig committed
342
                }
343
344
345
346
                cell->setOverrideSize(-1);
                cell->setChildNeedsLayout(true, false);
                cell->layoutIfNeeded();
            }
347
348
349
350
351

            int adjustedPaddingBefore = cell->paddingBefore() - cell->intrinsicPaddingBefore();
            int adjustedPaddingAfter = cell->paddingAfter() - cell->intrinsicPaddingAfter();
            int adjustedLogicalHeight = cell->logicalHeight() - (cell->intrinsicPaddingBefore() + cell->intrinsicPaddingAfter());

352
353
            // Explicit heights use the border box in quirks mode.  In strict mode do the right
            // thing and actually add in the border and padding.
354
355
356
357
            ch = cell->style()->logicalHeight().calcValue(0) + 
                (document()->inQuirksMode() ? 0 : (adjustedPaddingBefore + adjustedPaddingAfter +
                                                   cell->borderBefore() + cell->borderAfter()));
            ch = max(ch, adjustedLogicalHeight);
358

ddkilzer's avatar
ddkilzer committed
359
            pos = m_rowPos[indx] + ch + (m_grid[r].rowRenderer ? spacing : 0);
360

ddkilzer's avatar
ddkilzer committed
361
            m_rowPos[r + 1] = max(m_rowPos[r + 1], pos);
362

ap's avatar
ap committed
363
364
            // find out the baseline
            EVerticalAlign va = cell->style()->verticalAlign();
ddkilzer's avatar
ddkilzer committed
365
            if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) {
ap's avatar
ap committed
366
                int b = cell->baselinePosition();
367
                if (b > cell->borderBefore() + cell->paddingBefore()) {
368
369
                    baseline = max(baseline, b - cell->intrinsicPaddingBefore());
                    bdesc = max(bdesc, m_rowPos[indx] + ch - (b - cell->intrinsicPaddingBefore()));
370
                }
ap's avatar
ap committed
371
372
            }
        }
373

374
        // do we have baseline aligned elements?
ap's avatar
ap committed
375
376
        if (baseline) {
            // increase rowheight if baseline requires
ddkilzer's avatar
ddkilzer committed
377
            m_rowPos[r + 1] = max(m_rowPos[r + 1], baseline + bdesc + (m_grid[r].rowRenderer ? spacing : 0));
378
            m_grid[r].baseline = baseline;
ap's avatar
ap committed
379
        }
380

ddkilzer's avatar
ddkilzer committed
381
        m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]);
382
    }
weinig's avatar
weinig committed
383

384
385
386
387
388
389
#ifndef NDEBUG
    setNeedsLayoutIsForbidden(false);
#endif

    ASSERT(!needsLayout());

390
    statePusher.pop();
mitz@apple.com's avatar
mitz@apple.com committed
391
392

    return m_rowPos[m_gridRows];
393
394
}

395
396
397
398
void RenderTableSection::layout()
{
    ASSERT(needsLayout());

399
    LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
400
401
402
403
404
405
406
407
408
409
    for (RenderObject* child = children()->firstChild(); child; child = child->nextSibling()) {
        if (child->isTableRow()) {
            child->layoutIfNeeded();
            ASSERT(!child->needsLayout());
        }
    }
    statePusher.pop();
    setNeedsLayout(false);
}

410
411
int RenderTableSection::layoutRows(int toAdd)
{
412
413
414
415
416
417
#ifndef NDEBUG
    setNeedsLayoutIsForbidden(true);
#endif

    ASSERT(!needsLayout());

418
419
    int rHeight;
    int rindx;
ddkilzer's avatar
ddkilzer committed
420
    int totalRows = m_gridRows;
421
    
422
    // Set the width of our section now.  The rows will also be this width.
423
    setLogicalWidth(table()->contentLogicalWidth());
424
    m_overflow.clear();
adele's avatar
adele committed
425
426
    m_hasOverflowingCell = false;

ddkilzer's avatar
ddkilzer committed
427
428
    if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) {
        int totalHeight = m_rowPos[totalRows] + toAdd;
429
430

        int dh = toAdd;
ap's avatar
ap committed
431
432
433
        int totalPercent = 0;
        int numAuto = 0;
        for (int r = 0; r < totalRows; r++) {
434
            if (m_grid[r].logicalHeight.isAuto())
ap's avatar
ap committed
435
                numAuto++;
436
437
            else if (m_grid[r].logicalHeight.isPercent())
                totalPercent += m_grid[r].logicalHeight.rawValue();
ap's avatar
ap committed
438
439
440
441
        }
        if (totalPercent) {
            // try to satisfy percent
            int add = 0;
ddkilzer's avatar
ddkilzer committed
442
443
            totalPercent = min(totalPercent, 100 * percentScaleFactor);
            int rh = m_rowPos[1] - m_rowPos[0];
ap's avatar
ap committed
444
            for (int r = 0; r < totalRows; r++) {
445
446
                if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) {
                    int toAdd = min(dh, (totalHeight * m_grid[r].logicalHeight.rawValue() / (100 * percentScaleFactor)) - rh);
447
448
                    // If toAdd is negative, then we don't want to shrink the row (this bug
                    // affected Outlook Web Access).
darin's avatar
darin committed
449
                    toAdd = max(0, toAdd);
ap's avatar
ap committed
450
451
                    add += toAdd;
                    dh -= toAdd;
452
                    totalPercent -= m_grid[r].logicalHeight.rawValue();
ap's avatar
ap committed
453
454
                }
                if (r < totalRows - 1)
ddkilzer's avatar
ddkilzer committed
455
456
                    rh = m_rowPos[r + 2] - m_rowPos[r + 1];
                m_rowPos[r + 1] += add;
ap's avatar
ap committed
457
458
459
460
461
462
            }
        }
        if (numAuto) {
            // distribute over variable cols
            int add = 0;
            for (int r = 0; r < totalRows; r++) {
463
                if (numAuto > 0 && m_grid[r].logicalHeight.isAuto()) {
ddkilzer's avatar
ddkilzer committed
464
                    int toAdd = dh / numAuto;
ap's avatar
ap committed
465
466
                    add += toAdd;
                    dh -= toAdd;
467
                    numAuto--;
ap's avatar
ap committed
468
                }
ddkilzer's avatar
ddkilzer committed
469
                m_rowPos[r + 1] += add;
ap's avatar
ap committed
470
471
            }
        }
ddkilzer's avatar
ddkilzer committed
472
        if (dh > 0 && m_rowPos[totalRows]) {
ap's avatar
ap committed
473
            // if some left overs, distribute equally.
ddkilzer's avatar
ddkilzer committed
474
            int tot = m_rowPos[totalRows];
475
            int add = 0;
ddkilzer's avatar
ddkilzer committed
476
            int prev = m_rowPos[0];
477
            for (int r = 0; r < totalRows; r++) {
478
                // weight with the original height
ddkilzer's avatar
ddkilzer committed
479
480
481
                add += dh * (m_rowPos[r + 1] - prev) / tot;
                prev = m_rowPos[r + 1];
                m_rowPos[r + 1] += add;
482
483
484
485
            }
        }
    }

ddkilzer's avatar
ddkilzer committed
486
487
    int hspacing = table()->hBorderSpacing();
    int vspacing = table()->vBorderSpacing();
488
    int nEffCols = table()->numEffCols();
ddkilzer's avatar
ddkilzer committed
489

490
    LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()), style()->isFlippedBlocksWritingMode());
weinig's avatar
weinig committed
491

492
    for (int r = 0; r < totalRows; r++) {
493
        // Set the row's x/y position and width/height.
hyatt@apple.com's avatar
hyatt@apple.com committed
494
495
        if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
            rowRenderer->setLocation(0, m_rowPos[r]);
496
497
            rowRenderer->setLogicalWidth(logicalWidth());
            rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
498
            rowRenderer->updateLayerTransform();
499
500
        }

501
        for (int c = 0; c < nEffCols; c++) {
502
503
            CellStruct& cs = cellAt(r, c);
            RenderTableCell* cell = cs.primaryCell();
504

505
            if (!cell || cs.inColSpan)
506
                continue;
507

508
509
            rindx = cell->row();
            rHeight = m_rowPos[rindx + cell->rowSpan()] - m_rowPos[rindx] - vspacing;
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
            
            // Force percent height children to lay themselves out again.
            // This will cause these children to grow to fill the cell.
            // FIXME: There is still more work to do here to fully match WinIE (should
            // it become necessary to do so).  In quirks mode, WinIE behaves like we
            // do, but it will clip the cells that spill out of the table section.  In
            // strict mode, Mozilla and WinIE both regrow the table to accommodate the
            // new height of the cell (thus letting the percentages cause growth one
            // time only).  We may also not be handling row-spanning cells correctly.
            //
            // Note also the oddity where replaced elements always flex, and yet blocks/tables do
            // not necessarily flex.  WinIE is crazy and inconsistent, and we can't hope to
            // match the behavior perfectly, but we'll continue to refine it as we discover new
            // bugs. :)
            bool cellChildrenFlex = false;
525
526
            bool flexAllChildren = cell->style()->logicalHeight().isFixed()
                || (!table()->style()->logicalHeight().isAuto() && rHeight != cell->logicalHeight());
527
528

            for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) {
529
                if (!o->isText() && o->style()->logicalHeight().isPercent() && (flexAllChildren || o->isReplaced() || (o->isBox() && toRenderBox(o)->scrollsOverflow()))) {
530
                    // Tables with no sections do not flex.
darin@apple.com's avatar
darin@apple.com committed
531
                    if (!o->isTable() || toRenderTable(o)->hasSections()) {
532
533
534
535
536
                        o->setNeedsLayout(true, false);
                        cellChildrenFlex = true;
                    }
                }
            }
mitz@apple.com's avatar
mitz@apple.com committed
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557

            if (HashSet<RenderBox*>* percentHeightDescendants = cell->percentHeightDescendants()) {
                HashSet<RenderBox*>::iterator end = percentHeightDescendants->end();
                for (HashSet<RenderBox*>::iterator it = percentHeightDescendants->begin(); it != end; ++it) {
                    RenderBox* box = *it;
                    if (!box->isReplaced() && !box->scrollsOverflow() && !flexAllChildren)
                        continue;

                    while (box != cell) {
                        if (box->normalChildNeedsLayout())
                            break;
                        box->setChildNeedsLayout(true, false);
                        box = box->containingBlock();
                        ASSERT(box);
                        if (!box)
                            break;
                    }
                    cellChildrenFlex = true;
                }
            }

558
            if (cellChildrenFlex) {
mitz@apple.com's avatar
mitz@apple.com committed
559
                cell->setChildNeedsLayout(true, false);
560
561
                // Alignment within a cell is based off the calculated
                // height, which becomes irrelevant once the cell has
562
                // been resized based off its percentage.
563
                cell->setOverrideSize(max(0, 
564
565
                                           rHeight - cell->borderBefore() - cell->paddingBefore() - 
                                                     cell->borderAfter() - cell->paddingAfter()));
566
                cell->layoutIfNeeded();
567

568
569
570
571
                // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
                EVerticalAlign va = cell->style()->verticalAlign();
                if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) {
                    int b = cell->baselinePosition();
572
                    if (b > cell->borderBefore() + cell->paddingBefore())
573
574
                        m_grid[r].baseline = max(m_grid[r].baseline, b);
                }
bdakin's avatar
bdakin committed
575
            }
576
577
578
579
580
581

            int oldIntrinsicPaddingBefore = cell->intrinsicPaddingBefore();
            int oldIntrinsicPaddingAfter = cell->intrinsicPaddingAfter();
            int logicalHeightWithoutIntrinsicPadding = cell->logicalHeight() - oldIntrinsicPaddingBefore - oldIntrinsicPaddingAfter;

            int intrinsicPaddingBefore = 0;
bdakin's avatar
bdakin committed
582
583
584
585
586
            switch (cell->style()->verticalAlign()) {
                case SUB:
                case SUPER:
                case TEXT_TOP:
                case TEXT_BOTTOM:
hyatt@apple.com's avatar
hyatt@apple.com committed
587
                case BASELINE: {
588
                    int b = cell->baselinePosition();
589
590
                    if (b > cell->borderBefore() + cell->paddingBefore())
                        intrinsicPaddingBefore = getBaseline(r) - (b - oldIntrinsicPaddingBefore);
bdakin's avatar
bdakin committed
591
                    break;
hyatt@apple.com's avatar
hyatt@apple.com committed
592
                }
bdakin's avatar
bdakin committed
593
594
595
                case TOP:
                    break;
                case MIDDLE:
596
                    intrinsicPaddingBefore = (rHeight - logicalHeightWithoutIntrinsicPadding) / 2;
bdakin's avatar
bdakin committed
597
598
                    break;
                case BOTTOM:
599
                    intrinsicPaddingBefore = rHeight - logicalHeightWithoutIntrinsicPadding;
bdakin's avatar
bdakin committed
600
601
602
603
                    break;
                default:
                    break;
            }
604
            
605
606
607
608
            int intrinsicPaddingAfter = rHeight - logicalHeightWithoutIntrinsicPadding - intrinsicPaddingBefore;
            cell->setIntrinsicPaddingBefore(intrinsicPaddingBefore);
            cell->setIntrinsicPaddingAfter(intrinsicPaddingAfter);

609
            IntRect oldCellRect(cell->x(), cell->y() , cell->width(), cell->height());
610

611
            if (!style()->isLeftToRightDirection())
612
                cell->setLogicalLocation(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]);
613
            else
614
                cell->setLogicalLocation(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]);
615
            view()->addLayoutDelta(IntSize(oldCellRect.x() - cell->x(), oldCellRect.y() - cell->y()));
616

617
            if (intrinsicPaddingBefore != oldIntrinsicPaddingBefore || intrinsicPaddingAfter != oldIntrinsicPaddingAfter)
618
                cell->setNeedsLayout(true, false);
619

620
            if (!cell->needsLayout() && view()->layoutState()->pageLogicalHeight() && view()->layoutState()->pageLogicalOffset(cell->y()) != cell->pageLogicalOffset())
621
                cell->setChildNeedsLayout(true, false);
622

623
            cell->layoutIfNeeded();
624
625

            // FIXME: Make pagination work with vertical tables.
626
            if (style()->isHorizontalWritingMode() && view()->layoutState()->pageLogicalHeight() && cell->height() != rHeight)
627
                cell->setHeight(rHeight); // FIXME: Pagination might have made us change size.  For now just shrink or grow the cell to fit without doing a relayout.
628

629
630
631
632
633
634
635
636
637
638
            IntSize childOffset(cell->x() - oldCellRect.x(), cell->y() - oldCellRect.y());
            if (childOffset.width() || childOffset.height()) {
                view()->addLayoutDelta(childOffset);

                // If the child moved, we have to repaint it as well as any floating/positioned
                // descendants.  An exception is if we need a layout.  In this case, we know we're going to
                // repaint ourselves (and the child) anyway.
                if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
                    cell->repaintDuringLayoutIfMoved(oldCellRect);
            }
639
640
641
        }
    }

642
643
644
645
#ifndef NDEBUG
    setNeedsLayoutIsForbidden(false);
#endif

hyatt@apple.com's avatar
hyatt@apple.com committed
646
647
    ASSERT(!needsLayout());

648
    setLogicalHeight(m_rowPos[totalRows]);
649
650
651
652

    // Now that our height has been determined, add in overflow from cells.
    for (int r = 0; r < totalRows; r++) {
        for (int c = 0; c < nEffCols; c++) {
653
654
            CellStruct& cs = cellAt(r, c);
            RenderTableCell* cell = cs.primaryCell();
655
            if (!cell || cs.inColSpan)
656
                continue;
657
            if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
658
659
                continue;
            addOverflowFromChild(cell);
660
            m_hasOverflowingCell |= cell->hasVisualOverflow();
661
662
        }
    }
weinig's avatar
weinig committed
663

664
    statePusher.pop();
hyatt@apple.com's avatar
hyatt@apple.com committed
665
    return height();
666
667
}

668
int RenderTableSection::calcOuterBorderBefore() const
darin's avatar
darin committed
669
670
{
    int totalCols = table()->numEffCols();
ddkilzer's avatar
ddkilzer committed
671
    if (!m_gridRows || !totalCols)
darin's avatar
darin committed
672
673
674
675
        return 0;

    unsigned borderWidth = 0;

676
    const BorderValue& sb = style()->borderBefore();
darin's avatar
darin committed
677
678
679
    if (sb.style() == BHIDDEN)
        return -1;
    if (sb.style() > BHIDDEN)
680
        borderWidth = sb.width();
darin's avatar
darin committed
681

682
    const BorderValue& rb = firstChild()->style()->borderBefore();
darin's avatar
darin committed
683
684
    if (rb.style() == BHIDDEN)
        return -1;
685
686
    if (rb.style() > BHIDDEN && rb.width() > borderWidth)
        borderWidth = rb.width();
darin's avatar
darin committed
687
688
689
690

    bool allHidden = true;
    for (int c = 0; c < totalCols; c++) {
        const CellStruct& current = cellAt(0, c);
691
        if (current.inColSpan || !current.hasCells())
darin's avatar
darin committed
692
            continue;
693
        const BorderValue& cb = current.primaryCell()->style()->borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
darin's avatar
darin committed
694
695
696
        // FIXME: Don't repeat for the same col group
        RenderTableCol* colGroup = table()->colElement(c);
        if (colGroup) {
697
            const BorderValue& gb = colGroup->style()->borderBefore();
darin's avatar
darin committed
698
699
            if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
                continue;
700
            allHidden = false;
701
702
703
704
            if (gb.style() > BHIDDEN && gb.width() > borderWidth)
                borderWidth = gb.width();
            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
                borderWidth = cb.width();
darin's avatar
darin committed
705
706
707
        } else {
            if (cb.style() == BHIDDEN)
                continue;
708
            allHidden = false;
709
710
            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
                borderWidth = cb.width();
darin's avatar
darin committed
711
712
713
714
715
716
717
718
        }
    }
    if (allHidden)
        return -1;

    return borderWidth / 2;
}

719
int RenderTableSection::calcOuterBorderAfter() const
darin's avatar
darin committed
720
721
{
    int totalCols = table()->numEffCols();
ddkilzer's avatar
ddkilzer committed
722
    if (!m_gridRows || !totalCols)
darin's avatar
darin committed
723
724
725
726
        return 0;

    unsigned borderWidth = 0;

727
    const BorderValue& sb = style()->borderAfter();
darin's avatar
darin committed
728
729
730
    if (sb.style() == BHIDDEN)
        return -1;
    if (sb.style() > BHIDDEN)
731
        borderWidth = sb.width();
darin's avatar
darin committed
732

733
    const BorderValue& rb = lastChild()->style()->borderAfter();
darin's avatar
darin committed
734
735
    if (rb.style() == BHIDDEN)
        return -1;
736
737
    if (rb.style() > BHIDDEN && rb.width() > borderWidth)
        borderWidth = rb.width();
darin's avatar
darin committed
738
739
740

    bool allHidden = true;
    for (int c = 0; c < totalCols; c++) {
ddkilzer's avatar
ddkilzer committed
741
        const CellStruct& current = cellAt(m_gridRows - 1, c);
742
        if (current.inColSpan || !current.hasCells())
darin's avatar
darin committed
743
            continue;
744
        const BorderValue& cb = current.primaryCell()->style()->borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
darin's avatar
darin committed
745
746
747
        // FIXME: Don't repeat for the same col group
        RenderTableCol* colGroup = table()->colElement(c);
        if (colGroup) {
748
            const BorderValue& gb = colGroup->style()->borderAfter();
darin's avatar
darin committed
749
750
            if (gb.style() == BHIDDEN || cb.style() == BHIDDEN)
                continue;
751
            allHidden = false;
752
753
754
755
            if (gb.style() > BHIDDEN && gb.width() > borderWidth)
                borderWidth = gb.width();
            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
                borderWidth = cb.width();
darin's avatar
darin committed
756
757
758
        } else {
            if (cb.style() == BHIDDEN)
                continue;
759
            allHidden = false;
760
761
            if (cb.style() > BHIDDEN && cb.width() > borderWidth)
                borderWidth = cb.width();
darin's avatar
darin committed
762
763
764
765
766
767
768
769
        }
    }
    if (allHidden)
        return -1;

    return (borderWidth + 1) / 2;
}

770
int RenderTableSection::calcOuterBorderStart() const
darin's avatar
darin committed
771
772
{
    int totalCols = table()->numEffCols();
ddkilzer's avatar
ddkilzer committed
773
    if (!m_gridRows || !totalCols)
darin's avatar
darin committed
774
775
776
777
        return 0;

    unsigned borderWidth = 0;

778
    const BorderValue& sb = style()->borderStart();
darin's avatar
darin committed
779
780
781
    if (sb.style() == BHIDDEN)
        return -1;
    if (sb.style() > BHIDDEN)
782
        borderWidth = sb.width();
darin's avatar
darin committed
783

784
785
    if (RenderTableCol* colGroup = table()->colElement(0)) {
        const BorderValue& gb = colGroup->style()->borderStart();
darin's avatar
darin committed
786
787
        if (gb.style() == BHIDDEN)
            return -1;
788
789
        if (gb.style() > BHIDDEN && gb.width() > borderWidth)
            borderWidth = gb.width();
darin's avatar
darin committed
790
791
792
    }

    bool allHidden = true;
ddkilzer's avatar
ddkilzer committed
793
    for (int r = 0; r < m_gridRows; r++) {
794
        const CellStruct& current = cellAt(r, 0);
795
        if (!current.hasCells())
darin's avatar
darin committed
796
797
            continue;
        // FIXME: Don't repeat for the same cell
798
799
        const BorderValue& cb = current.primaryCell()->style()->borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
        const BorderValue& rb = current.primaryCell()->parent()->style()->borderStart();
darin's avatar
darin committed
800
801
        if (cb.style() == BHIDDEN || rb.style() == BHIDDEN)
            continue;
802
        allHidden = false;
803
804
805
806
        if (cb.style() > BHIDDEN && cb.width() > borderWidth)
            borderWidth = cb.width();
        if (rb.style() > BHIDDEN && rb.width() > borderWidth)
            borderWidth = rb.width();
darin's avatar
darin committed
807
808
809
810
    }
    if (allHidden)
        return -1;

811
    return (borderWidth + (table()->style()->isLeftToRightDirection() ? 0 : 1)) / 2;
darin's avatar
darin committed
812
813
}

814
int RenderTableSection::calcOuterBorderEnd() const
darin's avatar
darin committed
815
816
{
    int totalCols = table()->numEffCols();
ddkilzer's avatar
ddkilzer committed
817
    if (!m_gridRows || !totalCols)
darin's avatar
darin committed
818
819
820
821
        return 0;

    unsigned borderWidth = 0;

822
    const BorderValue& sb = style()->borderEnd();
darin's avatar