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