Home | History | Annotate | Download | only in rendering
      1 /*
      2  * Copyright (C) 2002 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2002 Dirk Mueller (mueller (at) 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 
     31 namespace blink {
     32 
     33 AutoTableLayout::AutoTableLayout(RenderTable* table)
     34     : TableLayout(table)
     35     , m_hasPercent(false)
     36     , m_effectiveLogicalWidthDirty(true)
     37 {
     38 }
     39 
     40 AutoTableLayout::~AutoTableLayout()
     41 {
     42 }
     43 
     44 void 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 
    140 void 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.
    181 static 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 
    210 void 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 
    251 void 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  */
    276 int 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 */
    470 void 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 
    494 void 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