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