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