• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
3  *           (C) 2002 Dirk Mueller (mueller@kde.org)
4  * Copyright (C) 2003, 2006, 2008, 2010 Apple Inc. All rights reserved.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 #include "config.h"
23 #include "AutoTableLayout.h"
24 
25 #include "RenderTable.h"
26 #include "RenderTableCell.h"
27 #include "RenderTableCol.h"
28 #include "RenderTableSection.h"
29 
30 using namespace std;
31 
32 namespace WebCore {
33 
AutoTableLayout(RenderTable * table)34 AutoTableLayout::AutoTableLayout(RenderTable* table)
35     : TableLayout(table)
36     , m_hasPercent(false)
37     , m_effectiveLogicalWidthDirty(true)
38 {
39 }
40 
~AutoTableLayout()41 AutoTableLayout::~AutoTableLayout()
42 {
43 }
44 
recalcColumn(int effCol)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 
fullRecalc()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.
shouldScaleColumns(RenderTable * table)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 
computePreferredLogicalWidths(int & minWidth,int & maxWidth)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  */
calcEffectiveLogicalWidth()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 */
insertSpanCell(RenderTableCell * cell)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 
layout()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