1/*
2 * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
3 *           (C) 2002 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2003, 2006, 2008, 2010 Apple Inc. All rights reserved.
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB.  If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23#include "AutoTableLayout.h"
24
25#include "RenderTable.h"
26#include "RenderTableCell.h"
27#include "RenderTableCol.h"
28#include "RenderTableSection.h"
29
30namespace WebCore {
31
32AutoTableLayout::AutoTableLayout(RenderTable* table)
33    : TableLayout(table)
34    , m_hasPercent(false)
35    , m_effectiveLogicalWidthDirty(true)
36{
37}
38
39AutoTableLayout::~AutoTableLayout()
40{
41}
42
43void AutoTableLayout::recalcColumn(unsigned effCol)
44{
45    Layout& columnLayout = m_layoutStruct[effCol];
46
47    RenderTableCell* fixedContributor = 0;
48    RenderTableCell* maxContributor = 0;
49
50    for (RenderObject* child = m_table->firstChild(); child; child = child->nextSibling()) {
51        if (child->isRenderTableCol()){
52            // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits
53            // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's
54            // ancestors as dirty.
55            toRenderTableCol(child)->clearPreferredLogicalWidthsDirtyBits();
56        } else if (child->isTableSection()) {
57            RenderTableSection* section = toRenderTableSection(child);
58            unsigned numRows = section->numRows();
59            for (unsigned i = 0; i < numRows; i++) {
60                RenderTableSection::CellStruct current = section->cellAt(i, effCol);
61                RenderTableCell* cell = current.primaryCell();
62
63                if (current.inColSpan || !cell)
64                    continue;
65
66                bool cellHasContent = cell->firstChild() || cell->style().hasBorder() || cell->style().hasPadding() || cell->style().hasBackground();
67                if (cellHasContent)
68                    columnLayout.emptyCellsOnly = false;
69
70                // A cell originates in this column. Ensure we have
71                // a min/max width of at least 1px for this column now.
72                columnLayout.minLogicalWidth = std::max<int>(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0);
73                columnLayout.maxLogicalWidth = std::max<int>(columnLayout.maxLogicalWidth, 1);
74
75                if (cell->colSpan() == 1) {
76                    columnLayout.minLogicalWidth = std::max<int>(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth);
77                    if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) {
78                        columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth();
79                        maxContributor = cell;
80                    }
81
82                    // All browsers implement a size limit on the cell's max width.
83                    // Our limit is based on KHTML's representation that used 16 bits widths.
84                    // FIXME: Other browsers have a lower limit for the cell's max width.
85                    const int cCellMaxWidth = 32760;
86                    Length cellLogicalWidth = cell->styleOrColLogicalWidth();
87                    if (cellLogicalWidth.value() > cCellMaxWidth)
88                        cellLogicalWidth.setValue(Fixed, cCellMaxWidth);
89                    if (cellLogicalWidth.isNegative())
90                        cellLogicalWidth.setValue(Fixed, 0);
91                    switch (cellLogicalWidth.type()) {
92                    case Fixed:
93                        // ignore width=0
94                        if (cellLogicalWidth.isPositive() && !columnLayout.logicalWidth.isPercent()) {
95                            int logicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(cellLogicalWidth.value());
96                            if (columnLayout.logicalWidth.isFixed()) {
97                                // Nav/IE weirdness
98                                if ((logicalWidth > columnLayout.logicalWidth.value())
99                                    || ((columnLayout.logicalWidth.value() == logicalWidth) && (maxContributor == cell))) {
100                                    columnLayout.logicalWidth.setValue(Fixed, logicalWidth);
101                                    fixedContributor = cell;
102                                }
103                            } else {
104                                columnLayout.logicalWidth.setValue(Fixed, logicalWidth);
105                                fixedContributor = cell;
106                            }
107                        }
108                        break;
109                    case Percent:
110                        m_hasPercent = true;
111                        if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercentNotCalculated() || cellLogicalWidth.percent() > columnLayout.logicalWidth.percent()))
112                            columnLayout.logicalWidth = cellLogicalWidth;
113                        break;
114                    case Relative:
115                        // FIXME: Need to understand this case and whether it makes sense to compare values
116                        // which are not necessarily of the same type.
117                        if (cellLogicalWidth.value() > columnLayout.logicalWidth.value())
118                            columnLayout.logicalWidth = cellLogicalWidth;
119                        break;
120                    default:
121                        break;
122                    }
123                } else if (!effCol || section->primaryCellAt(i, effCol - 1) != cell) {
124                    // This spanning cell originates in this column. Insert the cell into spanning cells list.
125                    insertSpanCell(cell);
126                }
127            }
128        }
129    }
130
131    // Nav/IE weirdness
132    if (columnLayout.logicalWidth.isFixed()) {
133        if (m_table->document().inQuirksMode() && columnLayout.maxLogicalWidth > columnLayout.logicalWidth.value() && fixedContributor != maxContributor) {
134            columnLayout.logicalWidth = Length();
135            fixedContributor = 0;
136        }
137    }
138
139    columnLayout.maxLogicalWidth = std::max(columnLayout.maxLogicalWidth, columnLayout.minLogicalWidth);
140}
141
142void AutoTableLayout::fullRecalc()
143{
144    m_hasPercent = false;
145    m_effectiveLogicalWidthDirty = true;
146
147    unsigned nEffCols = m_table->numEffCols();
148    m_layoutStruct.resize(nEffCols);
149    m_layoutStruct.fill(Layout());
150    m_spanCells.fill(0);
151
152    Length groupLogicalWidth;
153    unsigned currentColumn = 0;
154    for (RenderTableCol* column = m_table->firstColumn(); column; column = column->nextColumn()) {
155        if (column->isTableColumnGroupWithColumnChildren())
156            groupLogicalWidth = column->style().logicalWidth();
157        else {
158            Length colLogicalWidth = column->style().logicalWidth();
159            if (colLogicalWidth.isAuto())
160                colLogicalWidth = groupLogicalWidth;
161            if ((colLogicalWidth.isFixed() || colLogicalWidth.isPercent()) && colLogicalWidth.isZero())
162                colLogicalWidth = Length();
163            unsigned effCol = m_table->colToEffCol(currentColumn);
164            unsigned span = column->span();
165            if (!colLogicalWidth.isAuto() && span == 1 && effCol < nEffCols && m_table->spanOfEffCol(effCol) == 1) {
166                m_layoutStruct[effCol].logicalWidth = colLogicalWidth;
167                if (colLogicalWidth.isFixed() && m_layoutStruct[effCol].maxLogicalWidth < colLogicalWidth.value())
168                    m_layoutStruct[effCol].maxLogicalWidth = colLogicalWidth.value();
169            }
170            currentColumn += span;
171        }
172
173        // For the last column in a column-group, we invalidate our group logical width.
174        if (column->isTableColumn() && !column->nextSibling())
175            groupLogicalWidth = Length();
176    }
177
178    for (unsigned i = 0; i < nEffCols; i++)
179        recalcColumn(i);
180}
181
182// FIXME: This needs to be adapted for vertical writing modes.
183static bool shouldScaleColumns(RenderTable* table)
184{
185    // A special case.  If this table is not fixed width and contained inside
186    // a cell, then don't bloat the maxwidth by examining percentage growth.
187    bool scale = true;
188    while (table) {
189        Length tw = table->style().width();
190        if ((tw.isAuto() || tw.isPercent()) && !table->isOutOfFlowPositioned()) {
191            RenderBlock* cb = table->containingBlock();
192            while (cb && !cb->isRenderView() && !cb->isTableCell() &&
193                cb->style().width().isAuto() && !cb->isOutOfFlowPositioned())
194                cb = cb->containingBlock();
195
196            table = 0;
197            if (cb && cb->isTableCell() &&
198                (cb->style().width().isAuto() || cb->style().width().isPercent())) {
199                RenderTableCell* cell = toRenderTableCell(cb);
200                if (cell->colSpan() > 1 || cell->table()->style().width().isAuto())
201                    scale = false;
202                else
203                    table = cell->table();
204            }
205        }
206        else
207            table = 0;
208    }
209    return scale;
210}
211
212void AutoTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth)
213{
214    fullRecalc();
215
216    int spanMaxLogicalWidth = calcEffectiveLogicalWidth();
217    minWidth = 0;
218    maxWidth = 0;
219    float maxPercent = 0;
220    float maxNonPercent = 0;
221    bool scaleColumns = shouldScaleColumns(m_table);
222
223    // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero.
224    // FIXME: Handle the 0% cases properly.
225    const float epsilon = 1 / 128.0f;
226
227    float remainingPercent = 100;
228    for (size_t i = 0; i < m_layoutStruct.size(); ++i) {
229        minWidth += m_layoutStruct[i].effectiveMinLogicalWidth;
230        maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth;
231        if (scaleColumns) {
232            if (m_layoutStruct[i].effectiveLogicalWidth.isPercentNotCalculated()) {
233                float percent = std::min(m_layoutStruct[i].effectiveLogicalWidth.percent(), remainingPercent);
234                float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 / std::max(percent, epsilon);
235                maxPercent = std::max(logicalWidth,  maxPercent);
236                remainingPercent -= percent;
237            } else
238                maxNonPercent += m_layoutStruct[i].effectiveMaxLogicalWidth;
239        }
240    }
241
242    if (scaleColumns) {
243        maxNonPercent = maxNonPercent * 100 / std::max(remainingPercent, epsilon);
244        maxWidth = std::max<int>(maxWidth, static_cast<int>(std::min(maxNonPercent, static_cast<float>(tableMaxWidth))));
245        maxWidth = std::max<int>(maxWidth, static_cast<int>(std::min(maxPercent, static_cast<float>(tableMaxWidth))));
246    }
247
248    maxWidth = std::max<int>(maxWidth, spanMaxLogicalWidth);
249}
250
251void AutoTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
252{
253    Length tableLogicalWidth = m_table->style().logicalWidth();
254    if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive())
255        minWidth = maxWidth = std::max<int>(minWidth, tableLogicalWidth.value());
256}
257
258/*
259  This method takes care of colspans.
260  effWidth is the same as width for cells without colspans. If we have colspans, they get modified.
261 */
262int AutoTableLayout::calcEffectiveLogicalWidth()
263{
264    int maxLogicalWidth = 0;
265
266    size_t nEffCols = m_layoutStruct.size();
267    int spacingInRowDirection = m_table->hBorderSpacing();
268
269    for (size_t i = 0; i < nEffCols; ++i) {
270        m_layoutStruct[i].effectiveLogicalWidth = m_layoutStruct[i].logicalWidth;
271        m_layoutStruct[i].effectiveMinLogicalWidth = m_layoutStruct[i].minLogicalWidth;
272        m_layoutStruct[i].effectiveMaxLogicalWidth = m_layoutStruct[i].maxLogicalWidth;
273    }
274
275    for (size_t i = 0; i < m_spanCells.size(); ++i) {
276        RenderTableCell* cell = m_spanCells[i];
277        if (!cell)
278            break;
279
280        unsigned span = cell->colSpan();
281
282        Length cellLogicalWidth = cell->styleOrColLogicalWidth();
283        if (!cellLogicalWidth.isRelative() && cellLogicalWidth.isZero())
284            cellLogicalWidth = Length(); // make it Auto
285
286        unsigned effCol = m_table->colToEffCol(cell->col());
287        size_t lastCol = effCol;
288        int cellMinLogicalWidth = cell->minPreferredLogicalWidth() + spacingInRowDirection;
289        int cellMaxLogicalWidth = cell->maxPreferredLogicalWidth() + spacingInRowDirection;
290        float totalPercent = 0;
291        int spanMinLogicalWidth = 0;
292        int spanMaxLogicalWidth = 0;
293        bool allColsArePercent = true;
294        bool allColsAreFixed = true;
295        bool haveAuto = false;
296        bool spanHasEmptyCellsOnly = true;
297        int fixedWidth = 0;
298        while (lastCol < nEffCols && span > 0) {
299            Layout& columnLayout = m_layoutStruct[lastCol];
300            switch (columnLayout.logicalWidth.type()) {
301            case Percent:
302                totalPercent += columnLayout.logicalWidth.percent();
303                allColsAreFixed = false;
304                break;
305            case Fixed:
306                if (columnLayout.logicalWidth.value() > 0) {
307                    fixedWidth += columnLayout.logicalWidth.value();
308                    allColsArePercent = false;
309                    // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad
310                    // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
311                    break;
312                }
313                FALLTHROUGH;
314            case Auto:
315                haveAuto = true;
316                FALLTHROUGH;
317            default:
318                // If the column is a percentage width, do not let the spanning cell overwrite the
319                // width value.  This caused a mis-rendering on amazon.com.
320                // Sample snippet:
321                // <table border=2 width=100%><
322                //   <tr><td>1</td><td colspan=2>2-3</tr>
323                //   <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
324                // </table>
325                if (!columnLayout.effectiveLogicalWidth.isPercentNotCalculated()) {
326                    columnLayout.effectiveLogicalWidth = Length();
327                    allColsArePercent = false;
328                } else
329                    totalPercent += columnLayout.effectiveLogicalWidth.percent();
330                allColsAreFixed = false;
331            }
332            if (!columnLayout.emptyCellsOnly)
333                spanHasEmptyCellsOnly = false;
334            span -= m_table->spanOfEffCol(lastCol);
335            spanMinLogicalWidth += columnLayout.effectiveMinLogicalWidth;
336            spanMaxLogicalWidth += columnLayout.effectiveMaxLogicalWidth;
337            lastCol++;
338            cellMinLogicalWidth -= spacingInRowDirection;
339            cellMaxLogicalWidth -= spacingInRowDirection;
340        }
341
342        // adjust table max width if needed
343        if (cellLogicalWidth.isPercentNotCalculated()) {
344            if (totalPercent > cellLogicalWidth.percent() || allColsArePercent) {
345                // can't satify this condition, treat as variable
346                cellLogicalWidth = Length();
347            } else {
348                maxLogicalWidth = std::max(maxLogicalWidth, static_cast<int>(std::max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100  / cellLogicalWidth.percent()));
349
350                // all non percent columns in the span get percent values to sum up correctly.
351                float percentMissing = cellLogicalWidth.percent() - totalPercent;
352                int totalWidth = 0;
353                for (unsigned pos = effCol; pos < lastCol; ++pos) {
354                    if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent())
355                        totalWidth += m_layoutStruct[pos].effectiveMaxLogicalWidth;
356                }
357
358                for (unsigned pos = effCol; pos < lastCol && totalWidth > 0; ++pos) {
359                    if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) {
360                        float percent = percentMissing * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth;
361                        totalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
362                        percentMissing -= percent;
363                        if (percent > 0)
364                            m_layoutStruct[pos].effectiveLogicalWidth.setValue(Percent, percent);
365                        else
366                            m_layoutStruct[pos].effectiveLogicalWidth = Length();
367                    }
368                }
369            }
370        }
371
372        // make sure minWidth and maxWidth of the spanning cell are honoured
373        if (cellMinLogicalWidth > spanMinLogicalWidth) {
374            if (allColsAreFixed) {
375                for (unsigned pos = effCol; fixedWidth > 0 && pos < lastCol; ++pos) {
376                    int cellLogicalWidth = std::max(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(cellMinLogicalWidth * m_layoutStruct[pos].logicalWidth.value() / fixedWidth));
377                    fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
378                    cellMinLogicalWidth -= cellLogicalWidth;
379                    m_layoutStruct[pos].effectiveMinLogicalWidth = cellLogicalWidth;
380                }
381            } else if (allColsArePercent) {
382                // In this case, we just split the colspan's min amd max widths following the percentage.
383                int allocatedMinLogicalWidth = 0;
384                int allocatedMaxLogicalWidth = 0;
385                for (unsigned pos = effCol; pos < lastCol; ++pos) {
386                    ASSERT(m_layoutStruct[pos].logicalWidth.isPercentNotCalculated() || m_layoutStruct[pos].effectiveLogicalWidth.isPercentNotCalculated());
387                    // |allColsArePercent| means that either the logicalWidth *or* the effectiveLogicalWidth are percents, handle both of them here.
388                    float percent = m_layoutStruct[pos].logicalWidth.isPercentNotCalculated() ? m_layoutStruct[pos].logicalWidth.percent() : m_layoutStruct[pos].effectiveLogicalWidth.percent();
389                    int columnMinLogicalWidth = static_cast<int>(percent * cellMinLogicalWidth / totalPercent);
390                    int columnMaxLogicalWidth = static_cast<int>(percent * cellMaxLogicalWidth / totalPercent);
391                    m_layoutStruct[pos].effectiveMinLogicalWidth = std::max(m_layoutStruct[pos].effectiveMinLogicalWidth, columnMinLogicalWidth);
392                    m_layoutStruct[pos].effectiveMaxLogicalWidth = columnMaxLogicalWidth;
393                    allocatedMinLogicalWidth += columnMinLogicalWidth;
394                    allocatedMaxLogicalWidth += columnMaxLogicalWidth;
395                }
396                ASSERT(allocatedMinLogicalWidth <= cellMinLogicalWidth);
397                ASSERT(allocatedMaxLogicalWidth <= cellMaxLogicalWidth);
398                cellMinLogicalWidth -= allocatedMinLogicalWidth;
399                cellMaxLogicalWidth -= allocatedMaxLogicalWidth;
400            } else {
401                int remainingMaxLogicalWidth = spanMaxLogicalWidth;
402                int remainingMinLogicalWidth = spanMinLogicalWidth;
403
404                // Give min to variable first, to fixed second, and to others third.
405                for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
406                    if (m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth) {
407                        int colMinLogicalWidth = std::max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, m_layoutStruct[pos].logicalWidth.value());
408                        fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
409                        remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
410                        remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
411                        cellMinLogicalWidth -= colMinLogicalWidth;
412                        m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
413                    }
414                }
415
416                for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol && remainingMinLogicalWidth < cellMinLogicalWidth; ++pos) {
417                    if (!(m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth)) {
418                        int colMinLogicalWidth = std::max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / remainingMaxLogicalWidth : cellMinLogicalWidth));
419                        colMinLogicalWidth = std::min<int>(m_layoutStruct[pos].effectiveMinLogicalWidth + (cellMinLogicalWidth - remainingMinLogicalWidth), colMinLogicalWidth);
420                        remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
421                        remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
422                        cellMinLogicalWidth -= colMinLogicalWidth;
423                        m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
424                    }
425                }
426            }
427        }
428        if (!cellLogicalWidth.isPercent()) {
429            if (cellMaxLogicalWidth > spanMaxLogicalWidth) {
430                for (unsigned pos = effCol; spanMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
431                    int colMaxLogicalWidth = std::max(m_layoutStruct[pos].effectiveMaxLogicalWidth, static_cast<int>(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / spanMaxLogicalWidth : cellMaxLogicalWidth));
432                    spanMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
433                    cellMaxLogicalWidth -= colMaxLogicalWidth;
434                    m_layoutStruct[pos].effectiveMaxLogicalWidth = colMaxLogicalWidth;
435                }
436            }
437        } else {
438            for (unsigned pos = effCol; pos < lastCol; ++pos)
439                m_layoutStruct[pos].maxLogicalWidth = std::max(m_layoutStruct[pos].maxLogicalWidth, m_layoutStruct[pos].minLogicalWidth);
440        }
441        // treat span ranges consisting of empty cells only as if they had content
442        if (spanHasEmptyCellsOnly) {
443            for (unsigned pos = effCol; pos < lastCol; ++pos)
444                m_layoutStruct[pos].emptyCellsOnly = false;
445        }
446    }
447    m_effectiveLogicalWidthDirty = false;
448
449    return std::min(maxLogicalWidth, INT_MAX / 2);
450}
451
452/* gets all cells that originate in a column and have a cellspan > 1
453   Sorts them by increasing cellspan
454*/
455void AutoTableLayout::insertSpanCell(RenderTableCell *cell)
456{
457    ASSERT_ARG(cell, cell && cell->colSpan() != 1);
458    if (!cell || cell->colSpan() == 1)
459        return;
460
461    unsigned size = m_spanCells.size();
462    if (!size || m_spanCells[size-1] != 0) {
463        m_spanCells.grow(size + 10);
464        for (unsigned i = 0; i < 10; i++)
465            m_spanCells[size+i] = 0;
466        size += 10;
467    }
468
469    // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
470    unsigned pos = 0;
471    unsigned span = cell->colSpan();
472    while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan())
473        pos++;
474    memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *));
475    m_spanCells[pos] = cell;
476}
477
478
479void AutoTableLayout::layout()
480{
481    // table layout based on the values collected in the layout structure.
482    int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
483    int available = tableLogicalWidth;
484    size_t nEffCols = m_table->numEffCols();
485
486    // FIXME: It is possible to be called without having properly updated our internal representation.
487    // This means that our preferred logical widths were not recomputed as expected.
488    if (nEffCols != m_layoutStruct.size()) {
489        fullRecalc();
490        // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
491        nEffCols = m_table->numEffCols();
492    }
493
494    if (m_effectiveLogicalWidthDirty)
495        calcEffectiveLogicalWidth();
496
497    bool havePercent = false;
498    int totalRelative = 0;
499    int numAuto = 0;
500    int numFixed = 0;
501    float totalAuto = 0;
502    float totalFixed = 0;
503    float totalPercent = 0;
504    int allocAuto = 0;
505    unsigned numAutoEmptyCellsOnly = 0;
506
507    // fill up every cell with its minWidth
508    for (size_t i = 0; i < nEffCols; ++i) {
509        int cellLogicalWidth = m_layoutStruct[i].effectiveMinLogicalWidth;
510        m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
511        available -= cellLogicalWidth;
512        Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
513        switch (logicalWidth.type()) {
514        case Percent:
515            havePercent = true;
516            totalPercent += logicalWidth.percent();
517            break;
518        case Relative:
519            totalRelative += logicalWidth.value();
520            break;
521        case Fixed:
522            numFixed++;
523            totalFixed += m_layoutStruct[i].effectiveMaxLogicalWidth;
524            break;
525        case Auto:
526            if (m_layoutStruct[i].emptyCellsOnly)
527                numAutoEmptyCellsOnly++;
528            else {
529                numAuto++;
530                totalAuto += m_layoutStruct[i].effectiveMaxLogicalWidth;
531                allocAuto += cellLogicalWidth;
532            }
533            break;
534        default:
535            break;
536        }
537    }
538
539    // allocate width to percent cols
540    if (available > 0 && havePercent) {
541        for (size_t i = 0; i < nEffCols; ++i) {
542            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
543            if (logicalWidth.isPercent()) {
544                int cellLogicalWidth = std::max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, minimumValueForLength(logicalWidth, tableLogicalWidth));
545                available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
546                m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
547            }
548        }
549        if (totalPercent > 100) {
550            // remove overallocated space from the last columns
551            int excess = tableLogicalWidth * (totalPercent - 100) / 100;
552            for (unsigned i = nEffCols; i; ) {
553                --i;
554                if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) {
555                    int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth;
556                    int reduction = std::min(cellLogicalWidth,  excess);
557                    // the lines below might look inconsistent, but that's the way it's handled in mozilla
558                    excess -= reduction;
559                    int newLogicalWidth = std::max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, cellLogicalWidth - reduction);
560                    available += cellLogicalWidth - newLogicalWidth;
561                    m_layoutStruct[i].computedLogicalWidth = newLogicalWidth;
562                }
563            }
564        }
565    }
566
567    // then allocate width to fixed cols
568    if (available > 0) {
569        for (size_t i = 0; i < nEffCols; ++i) {
570            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
571            if (logicalWidth.isFixed() && logicalWidth.value() > m_layoutStruct[i].computedLogicalWidth) {
572                available += m_layoutStruct[i].computedLogicalWidth - logicalWidth.value();
573                m_layoutStruct[i].computedLogicalWidth = logicalWidth.value();
574            }
575        }
576    }
577
578    // now satisfy relative
579    if (available > 0) {
580        for (size_t i = 0; i < nEffCols; ++i) {
581            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
582            if (logicalWidth.isRelative() && logicalWidth.value() != 0) {
583                // width=0* gets effMinWidth.
584                int cellLogicalWidth = logicalWidth.value() * tableLogicalWidth / totalRelative;
585                available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
586                m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
587            }
588        }
589    }
590
591    // now satisfy variable
592    if (available > 0 && numAuto) {
593        available += allocAuto; // this gets redistributed
594        for (size_t i = 0; i < nEffCols; ++i) {
595            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
596            if (logicalWidth.isAuto() && totalAuto && !m_layoutStruct[i].emptyCellsOnly) {
597                int cellLogicalWidth = std::max<int>(m_layoutStruct[i].computedLogicalWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalAuto));
598                available -= cellLogicalWidth;
599                totalAuto -= m_layoutStruct[i].effectiveMaxLogicalWidth;
600                m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
601            }
602        }
603    }
604
605    // spread over fixed columns
606    if (available > 0 && numFixed) {
607        for (size_t i = 0; i < nEffCols; ++i) {
608            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
609            if (logicalWidth.isFixed()) {
610                int cellLogicalWidth = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalFixed);
611                available -= cellLogicalWidth;
612                totalFixed -= m_layoutStruct[i].effectiveMaxLogicalWidth;
613                m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
614            }
615        }
616    }
617
618    // spread over percent colums
619    if (available > 0 && m_hasPercent && totalPercent < 100) {
620        for (size_t i = 0; i < nEffCols; ++i) {
621            Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
622            if (logicalWidth.isPercentNotCalculated()) {
623                int cellLogicalWidth = available * logicalWidth.percent() / totalPercent;
624                available -= cellLogicalWidth;
625                totalPercent -= logicalWidth.percent();
626                m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
627                if (!available || !totalPercent)
628                    break;
629            }
630        }
631    }
632
633    // spread over the rest
634    if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
635        unsigned total = nEffCols - numAutoEmptyCellsOnly;
636        // still have some width to spread
637        for (unsigned i = nEffCols; i; ) {
638            --i;
639            // variable columns with empty cells only don't get any width
640            if (m_layoutStruct[i].effectiveLogicalWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly)
641                continue;
642            int cellLogicalWidth = available / total;
643            available -= cellLogicalWidth;
644            total--;
645            m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
646        }
647    }
648
649    // If we have overallocated, reduce every cell according to the difference between desired width and minwidth
650    // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing.
651    if (available < 0) {
652        // Need to reduce cells with the following prioritization:
653        // (1) Auto
654        // (2) Relative
655        // (3) Fixed
656        // (4) Percent
657        // This is basically the reverse of how we grew the cells.
658        if (available < 0) {
659            int logicalWidthBeyondMin = 0;
660            for (unsigned i = nEffCols; i; ) {
661                --i;
662                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
663                if (logicalWidth.isAuto())
664                    logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
665            }
666
667            for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
668                --i;
669                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
670                if (logicalWidth.isAuto()) {
671                    int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
672                    int reduce = available * minMaxDiff / logicalWidthBeyondMin;
673                    m_layoutStruct[i].computedLogicalWidth += reduce;
674                    available -= reduce;
675                    logicalWidthBeyondMin -= minMaxDiff;
676                    if (available >= 0)
677                        break;
678                }
679            }
680        }
681
682        if (available < 0) {
683            int logicalWidthBeyondMin = 0;
684            for (unsigned i = nEffCols; i; ) {
685                --i;
686                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
687                if (logicalWidth.isRelative())
688                    logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
689            }
690
691            for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
692                --i;
693                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
694                if (logicalWidth.isRelative()) {
695                    int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
696                    int reduce = available * minMaxDiff / logicalWidthBeyondMin;
697                    m_layoutStruct[i].computedLogicalWidth += reduce;
698                    available -= reduce;
699                    logicalWidthBeyondMin -= minMaxDiff;
700                    if (available >= 0)
701                        break;
702                }
703            }
704        }
705
706        if (available < 0) {
707            int logicalWidthBeyondMin = 0;
708            for (unsigned i = nEffCols; i; ) {
709                --i;
710                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
711                if (logicalWidth.isFixed())
712                    logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
713            }
714
715            for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
716                --i;
717                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
718                if (logicalWidth.isFixed()) {
719                    int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
720                    int reduce = available * minMaxDiff / logicalWidthBeyondMin;
721                    m_layoutStruct[i].computedLogicalWidth += reduce;
722                    available -= reduce;
723                    logicalWidthBeyondMin -= minMaxDiff;
724                    if (available >= 0)
725                        break;
726                }
727            }
728        }
729
730        if (available < 0) {
731            int logicalWidthBeyondMin = 0;
732            for (unsigned i = nEffCols; i; ) {
733                --i;
734                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
735                if (logicalWidth.isPercent())
736                    logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
737            }
738
739            for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
740                --i;
741                Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
742                if (logicalWidth.isPercent()) {
743                    int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
744                    int reduce = available * minMaxDiff / logicalWidthBeyondMin;
745                    m_layoutStruct[i].computedLogicalWidth += reduce;
746                    available -= reduce;
747                    logicalWidthBeyondMin -= minMaxDiff;
748                    if (available >= 0)
749                        break;
750                }
751            }
752        }
753    }
754
755    int pos = 0;
756    for (size_t i = 0; i < nEffCols; ++i) {
757        m_table->setColumnPosition(i, pos);
758        pos += m_layoutStruct[i].computedLogicalWidth + m_table->hBorderSpacing();
759    }
760    m_table->setColumnPosition(m_table->columnPositions().size() - 1, pos);
761}
762
763}
764