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.
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.
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.
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.
23 #include "AutoTableLayout.h"
25 #include "RenderTable.h"
26 #include "RenderTableCell.h"
27 #include "RenderTableCol.h"
28 #include "RenderTableSection.h"
34 AutoTableLayout::AutoTableLayout(RenderTable* table)
37 , m_effectiveLogicalWidthDirty(true)
41 AutoTableLayout::~AutoTableLayout()
45 void AutoTableLayout::recalcColumn(unsigned effCol)
47 Layout& columnLayout = m_layoutStruct[effCol];
49 RenderTableCell* fixedContributor = 0;
50 RenderTableCell* maxContributor = 0;
52 for (RenderObject* child = m_table->firstChild(); child; child = child->nextSibling()) {
53 if (child->isRenderTableCol())
54 toRenderTableCol(child)->computePreferredLogicalWidths();
55 else if (child->isTableSection()) {
56 RenderTableSection* section = toRenderTableSection(child);
57 unsigned numRows = section->numRows();
58 for (unsigned i = 0; i < numRows; i++) {
59 RenderTableSection::CellStruct current = section->cellAt(i, effCol);
60 RenderTableCell* cell = current.primaryCell();
62 if (current.inColSpan || !cell)
65 bool cellHasContent = cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding();
67 columnLayout.emptyCellsOnly = false;
69 // A cell originates in this column. Ensure we have
70 // a min/max width of at least 1px for this column now.
71 columnLayout.minLogicalWidth = max<int>(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0);
72 columnLayout.maxLogicalWidth = max<int>(columnLayout.maxLogicalWidth, 1);
74 if (cell->colSpan() == 1) {
75 if (cell->preferredLogicalWidthsDirty())
76 cell->computePreferredLogicalWidths();
77 columnLayout.minLogicalWidth = max<int>(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth);
78 if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) {
79 columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth();
80 maxContributor = cell;
83 // All browsers implement a size limit on the cell's max width.
84 // Our limit is based on KHTML's representation that used 16 bits widths.
85 // FIXME: Other browsers have a lower limit for the cell's max width.
86 const int cCellMaxWidth = 32760;
87 Length cellLogicalWidth = cell->styleOrColLogicalWidth();
88 if (cellLogicalWidth.value() > cCellMaxWidth)
89 cellLogicalWidth.setValue(cCellMaxWidth);
90 if (cellLogicalWidth.isNegative())
91 cellLogicalWidth.setValue(0);
92 switch (cellLogicalWidth.type()) {
95 if (cellLogicalWidth.isPositive() && !columnLayout.logicalWidth.isPercent()) {
96 int logicalWidth = cell->computeBorderBoxLogicalWidth(cellLogicalWidth.value());
97 if (columnLayout.logicalWidth.isFixed()) {
99 if ((logicalWidth > columnLayout.logicalWidth.value())
100 || ((columnLayout.logicalWidth.value() == logicalWidth) && (maxContributor == cell))) {
101 columnLayout.logicalWidth.setValue(Fixed, logicalWidth);
102 fixedContributor = cell;
105 columnLayout.logicalWidth.setValue(Fixed, logicalWidth);
106 fixedContributor = cell;
112 if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercent() || cellLogicalWidth.value() > columnLayout.logicalWidth.value()))
113 columnLayout.logicalWidth = cellLogicalWidth;
116 // FIXME: Need to understand this case and whether it makes sense to compare values
117 // which are not necessarily of the same type.
118 if (cellLogicalWidth.value() > columnLayout.logicalWidth.value())
119 columnLayout.logicalWidth = cellLogicalWidth;
123 } else if (!effCol || section->primaryCellAt(i, effCol - 1) != cell) {
124 // This spanning cell originates in this column. Insert the cell into spanning cells list.
125 insertSpanCell(cell);
132 if (columnLayout.logicalWidth.isFixed()) {
133 if (m_table->document()->inQuirksMode() && columnLayout.maxLogicalWidth > columnLayout.logicalWidth.value() && fixedContributor != maxContributor) {
134 columnLayout.logicalWidth = Length();
135 fixedContributor = 0;
139 columnLayout.maxLogicalWidth = max(columnLayout.maxLogicalWidth, columnLayout.minLogicalWidth);
142 void AutoTableLayout::fullRecalc()
144 m_hasPercent = false;
145 m_effectiveLogicalWidthDirty = true;
147 unsigned nEffCols = m_table->numEffCols();
148 m_layoutStruct.resize(nEffCols);
149 m_layoutStruct.fill(Layout());
152 Length groupLogicalWidth;
153 unsigned currentColumn = 0;
154 for (RenderTableCol* column = m_table->firstColumn(); column; column = column->nextColumn()) {
155 if (column->isTableColumnGroupWithColumnChildren())
156 groupLogicalWidth = column->style()->logicalWidth();
158 Length colLogicalWidth = column->style()->logicalWidth();
159 if (colLogicalWidth.isAuto())
160 colLogicalWidth = groupLogicalWidth;
161 if ((colLogicalWidth.isFixed() || colLogicalWidth.isPercent()) && colLogicalWidth.isZero())
162 colLogicalWidth = Length();
163 unsigned effCol = m_table->colToEffCol(currentColumn);
164 unsigned span = column->span();
165 if (!colLogicalWidth.isAuto() && span == 1 && effCol < nEffCols && m_table->spanOfEffCol(effCol) == 1) {
166 m_layoutStruct[effCol].logicalWidth = colLogicalWidth;
167 if (colLogicalWidth.isFixed() && m_layoutStruct[effCol].maxLogicalWidth < colLogicalWidth.value())
168 m_layoutStruct[effCol].maxLogicalWidth = colLogicalWidth.value();
170 currentColumn += span;
173 // For the last column in a column-group, we invalidate our group logical width.
174 if (column->isTableColumn() && !column->nextSibling())
175 groupLogicalWidth = Length();
178 for (unsigned i = 0; i < nEffCols; i++)
182 // FIXME: This needs to be adapted for vertical writing modes.
183 static bool shouldScaleColumns(RenderTable* table)
185 // A special case. If this table is not fixed width and contained inside
186 // a cell, then don't bloat the maxwidth by examining percentage growth.
189 Length tw = table->style()->width();
190 if ((tw.isAuto() || tw.isPercent()) && !table->isOutOfFlowPositioned()) {
191 RenderBlock* cb = table->containingBlock();
192 while (cb && !cb->isRenderView() && !cb->isTableCell() &&
193 cb->style()->width().isAuto() && !cb->isOutOfFlowPositioned())
194 cb = cb->containingBlock();
197 if (cb && cb->isTableCell() &&
198 (cb->style()->width().isAuto() || cb->style()->width().isPercent())) {
202 RenderTableCell* cell = toRenderTableCell(cb);
203 if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto())
206 table = cell->table();
216 void AutoTableLayout::computePreferredLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth)
220 int spanMaxLogicalWidth = calcEffectiveLogicalWidth();
223 float maxPercent = 0;
224 float maxNonPercent = 0;
225 bool scaleColumns = shouldScaleColumns(m_table);
227 // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero.
228 // FIXME: Handle the 0% cases properly.
229 const float epsilon = 1 / 128.0f;
231 float remainingPercent = 100;
232 for (size_t i = 0; i < m_layoutStruct.size(); ++i) {
233 minWidth += m_layoutStruct[i].effectiveMinLogicalWidth;
234 maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth;
236 if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) {
237 float percent = min(static_cast<float>(m_layoutStruct[i].effectiveLogicalWidth.percent()), remainingPercent);
238 float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 / max(percent, epsilon);
239 maxPercent = max(logicalWidth, maxPercent);
240 remainingPercent -= percent;
242 maxNonPercent += m_layoutStruct[i].effectiveMaxLogicalWidth;
247 maxNonPercent = maxNonPercent * 100 / max(remainingPercent, epsilon);
248 maxWidth = max<int>(maxWidth, static_cast<int>(min(maxNonPercent, static_cast<float>(tableMaxWidth))));
249 maxWidth = max<int>(maxWidth, static_cast<int>(min(maxPercent, static_cast<float>(tableMaxWidth))));
252 maxWidth = max<int>(maxWidth, spanMaxLogicalWidth);
254 int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection();
255 minWidth += bordersPaddingAndSpacing;
256 maxWidth += bordersPaddingAndSpacing;
258 Length tableLogicalWidth = m_table->style()->logicalWidth();
259 if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) {
260 minWidth = max<int>(minWidth, tableLogicalWidth.value());
262 } else if (!remainingPercent && maxNonPercent) {
263 // if there was no remaining percent, maxWidth is invalid
264 maxWidth = tableMaxWidth;
267 Length tableLogicalMinWidth = m_table->style()->logicalMinWidth();
268 if (tableLogicalMinWidth.isFixed() && tableLogicalMinWidth.isPositive()) {
269 minWidth = max<int>(minWidth, tableLogicalMinWidth.value());
270 maxWidth = max<int>(minWidth, maxWidth);
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.
278 int AutoTableLayout::calcEffectiveLogicalWidth()
280 float maxLogicalWidth = 0;
282 size_t nEffCols = m_layoutStruct.size();
283 int spacingInRowDirection = m_table->hBorderSpacing();
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;
291 for (size_t i = 0; i < m_spanCells.size(); ++i) {
292 RenderTableCell* cell = m_spanCells[i];
296 unsigned span = cell->colSpan();
298 Length cellLogicalWidth = cell->styleOrColLogicalWidth();
299 if (!cellLogicalWidth.isRelative() && cellLogicalWidth.isZero())
300 cellLogicalWidth = Length(); // make it Auto
302 unsigned 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;
314 while (lastCol < nEffCols && span > 0) {
315 Layout& columnLayout = m_layoutStruct[lastCol];
316 switch (columnLayout.logicalWidth.type()) {
318 totalPercent += columnLayout.logicalWidth.percent();
319 allColsAreFixed = false;
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.
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.
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>
341 if (!columnLayout.effectiveLogicalWidth.isPercent()) {
342 columnLayout.effectiveLogicalWidth = Length();
343 allColsArePercent = false;
345 totalPercent += columnLayout.effectiveLogicalWidth.percent();
346 allColsAreFixed = false;
348 if (!columnLayout.emptyCellsOnly)
349 spanHasEmptyCellsOnly = false;
350 span -= m_table->spanOfEffCol(lastCol);
351 spanMinLogicalWidth += columnLayout.effectiveMinLogicalWidth;
352 spanMaxLogicalWidth += columnLayout.effectiveMaxLogicalWidth;
354 cellMinLogicalWidth -= spacingInRowDirection;
355 cellMaxLogicalWidth -= spacingInRowDirection;
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();
364 maxLogicalWidth = max(maxLogicalWidth, static_cast<float>(max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 / cellLogicalWidth.percent()));
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;
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;
380 m_layoutStruct[pos].effectiveLogicalWidth.setValue(Percent, percent);
382 m_layoutStruct[pos].effectiveLogicalWidth = Length();
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, static_cast<int>(cellMinLogicalWidth * m_layoutStruct[pos].logicalWidth.value() / fixedWidth));
393 fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
394 cellMinLogicalWidth -= cellLogicalWidth;
395 m_layoutStruct[pos].effectiveMinLogicalWidth = cellLogicalWidth;
397 } else if (allColsArePercent) {
398 // In this case, we just split the colspan's min amd max widths following the percentage.
399 int allocatedMinLogicalWidth = 0;
400 float allocatedMaxLogicalWidth = 0;
401 for (unsigned pos = effCol; pos < lastCol; ++pos) {
402 ASSERT(m_layoutStruct[pos].logicalWidth.isPercent() || m_layoutStruct[pos].effectiveLogicalWidth.isPercent());
403 // |allColsArePercent| means that either the logicalWidth *or* the effectiveLogicalWidth are percents, handle both of them here.
404 float percent = m_layoutStruct[pos].logicalWidth.isPercent() ? m_layoutStruct[pos].logicalWidth.percent() : m_layoutStruct[pos].effectiveLogicalWidth.percent();
405 int columnMinLogicalWidth = static_cast<int>(percent * cellMinLogicalWidth / totalPercent);
406 float columnMaxLogicalWidth = percent * cellMaxLogicalWidth / totalPercent;
407 m_layoutStruct[pos].effectiveMinLogicalWidth = max(m_layoutStruct[pos].effectiveMinLogicalWidth, columnMinLogicalWidth);
408 m_layoutStruct[pos].effectiveMaxLogicalWidth = columnMaxLogicalWidth;
409 allocatedMinLogicalWidth += columnMinLogicalWidth;
410 allocatedMaxLogicalWidth += columnMaxLogicalWidth;
412 ASSERT(allocatedMinLogicalWidth <= cellMinLogicalWidth);
413 ASSERT(allocatedMaxLogicalWidth <= cellMaxLogicalWidth);
414 cellMinLogicalWidth -= allocatedMinLogicalWidth;
415 cellMaxLogicalWidth -= allocatedMaxLogicalWidth;
417 float remainingMaxLogicalWidth = spanMaxLogicalWidth;
418 int remainingMinLogicalWidth = spanMinLogicalWidth;
420 // Give min to variable first, to fixed second, and to others third.
421 for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
422 if (m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth) {
423 int colMinLogicalWidth = max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, m_layoutStruct[pos].logicalWidth.value());
424 fixedWidth -= m_layoutStruct[pos].logicalWidth.value();
425 remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
426 remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
427 cellMinLogicalWidth -= colMinLogicalWidth;
428 m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
432 for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol && remainingMinLogicalWidth < cellMinLogicalWidth; ++pos) {
433 if (!(m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth)) {
434 int colMinLogicalWidth = max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / remainingMaxLogicalWidth : cellMinLogicalWidth));
435 colMinLogicalWidth = min<int>(m_layoutStruct[pos].effectiveMinLogicalWidth + (cellMinLogicalWidth - remainingMinLogicalWidth), colMinLogicalWidth);
436 remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
437 remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth;
438 cellMinLogicalWidth -= colMinLogicalWidth;
439 m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth;
444 if (!cellLogicalWidth.isPercent()) {
445 if (cellMaxLogicalWidth > spanMaxLogicalWidth) {
446 for (unsigned pos = effCol; spanMaxLogicalWidth >= 0 && pos < lastCol; ++pos) {
447 int colMaxLogicalWidth = max(m_layoutStruct[pos].effectiveMaxLogicalWidth, static_cast<int>(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / spanMaxLogicalWidth : cellMaxLogicalWidth));
448 spanMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth;
449 cellMaxLogicalWidth -= colMaxLogicalWidth;
450 m_layoutStruct[pos].effectiveMaxLogicalWidth = colMaxLogicalWidth;
454 for (unsigned pos = effCol; pos < lastCol; ++pos)
455 m_layoutStruct[pos].maxLogicalWidth = max(m_layoutStruct[pos].maxLogicalWidth, m_layoutStruct[pos].minLogicalWidth);
457 // treat span ranges consisting of empty cells only as if they had content
458 if (spanHasEmptyCellsOnly) {
459 for (unsigned pos = effCol; pos < lastCol; ++pos)
460 m_layoutStruct[pos].emptyCellsOnly = false;
463 m_effectiveLogicalWidthDirty = false;
465 return static_cast<int>(min(maxLogicalWidth, INT_MAX / 2.0f));
468 /* gets all cells that originate in a column and have a cellspan > 1
469 Sorts them by increasing cellspan
471 void AutoTableLayout::insertSpanCell(RenderTableCell *cell)
473 ASSERT_ARG(cell, cell && cell->colSpan() != 1);
474 if (!cell || cell->colSpan() == 1)
477 unsigned size = m_spanCells.size();
478 if (!size || m_spanCells[size-1] != 0) {
479 m_spanCells.grow(size + 10);
480 for (unsigned i = 0; i < 10; i++)
481 m_spanCells[size+i] = 0;
485 // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
487 unsigned span = cell->colSpan();
488 while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan())
490 memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *));
491 m_spanCells[pos] = cell;
495 void AutoTableLayout::layout()
497 // table layout based on the values collected in the layout structure.
498 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
499 int available = tableLogicalWidth;
500 size_t nEffCols = m_table->numEffCols();
502 // FIXME: It is possible to be called without having properly updated our internal representation.
503 // This means that our preferred logical widths were not recomputed as expected.
504 if (nEffCols != m_layoutStruct.size()) {
506 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
507 nEffCols = m_table->numEffCols();
510 if (m_effectiveLogicalWidthDirty)
511 calcEffectiveLogicalWidth();
513 bool havePercent = false;
514 int totalRelative = 0;
518 float totalFixed = 0;
519 float totalPercent = 0;
521 unsigned numAutoEmptyCellsOnly = 0;
523 // fill up every cell with its minWidth
524 for (size_t i = 0; i < nEffCols; ++i) {
525 int cellLogicalWidth = m_layoutStruct[i].effectiveMinLogicalWidth;
526 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
527 available -= cellLogicalWidth;
528 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
529 switch (logicalWidth.type()) {
532 totalPercent += logicalWidth.percent();
535 totalRelative += logicalWidth.value();
539 totalFixed += m_layoutStruct[i].effectiveMaxLogicalWidth;
543 if (m_layoutStruct[i].emptyCellsOnly)
544 numAutoEmptyCellsOnly++;
547 totalAuto += m_layoutStruct[i].effectiveMaxLogicalWidth;
548 allocAuto += cellLogicalWidth;
556 // allocate width to percent cols
557 if (available > 0 && havePercent) {
558 for (size_t i = 0; i < nEffCols; ++i) {
559 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
560 if (logicalWidth.isPercent()) {
561 int cellLogicalWidth = max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, minimumValueForLength(logicalWidth, tableLogicalWidth));
562 available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
563 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
566 if (totalPercent > 100) {
567 // remove overallocated space from the last columns
568 int excess = tableLogicalWidth * (totalPercent - 100) / 100;
569 for (unsigned i = nEffCols; i; ) {
571 if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) {
572 int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth;
573 int reduction = min(cellLogicalWidth, excess);
574 // the lines below might look inconsistent, but that's the way it's handled in mozilla
576 int newLogicalWidth = max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, cellLogicalWidth - reduction);
577 available += cellLogicalWidth - newLogicalWidth;
578 m_layoutStruct[i].computedLogicalWidth = newLogicalWidth;
584 // then allocate width to fixed cols
586 for (size_t i = 0; i < nEffCols; ++i) {
587 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
588 if (logicalWidth.isFixed() && logicalWidth.value() > m_layoutStruct[i].computedLogicalWidth) {
589 available += m_layoutStruct[i].computedLogicalWidth - logicalWidth.value();
590 m_layoutStruct[i].computedLogicalWidth = logicalWidth.value();
595 // now satisfy relative
597 for (size_t i = 0; i < nEffCols; ++i) {
598 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
599 if (logicalWidth.isRelative() && logicalWidth.value() != 0) {
600 // width=0* gets effMinWidth.
601 int cellLogicalWidth = logicalWidth.value() * tableLogicalWidth / totalRelative;
602 available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth;
603 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
608 // now satisfy variable
609 if (available > 0 && numAuto) {
610 available += allocAuto; // this gets redistributed
611 for (size_t i = 0; i < nEffCols; ++i) {
612 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
613 if (logicalWidth.isAuto() && totalAuto && !m_layoutStruct[i].emptyCellsOnly) {
614 int cellLogicalWidth = max<int>(m_layoutStruct[i].computedLogicalWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalAuto));
615 available -= cellLogicalWidth;
616 totalAuto -= m_layoutStruct[i].effectiveMaxLogicalWidth;
617 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth;
622 // spread over fixed columns
623 if (available > 0 && numFixed) {
624 for (size_t i = 0; i < nEffCols; ++i) {
625 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
626 if (logicalWidth.isFixed()) {
627 int cellLogicalWidth = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalFixed);
628 available -= cellLogicalWidth;
629 totalFixed -= m_layoutStruct[i].effectiveMaxLogicalWidth;
630 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
635 // spread over percent colums
636 if (available > 0 && m_hasPercent && totalPercent < 100) {
637 for (size_t i = 0; i < nEffCols; ++i) {
638 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
639 if (logicalWidth.isPercent()) {
640 int cellLogicalWidth = available * logicalWidth.percent() / totalPercent;
641 available -= cellLogicalWidth;
642 totalPercent -= logicalWidth.percent();
643 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
644 if (!available || !totalPercent)
650 // spread over the rest
651 if (available > 0 && nEffCols > numAutoEmptyCellsOnly) {
652 unsigned total = nEffCols - numAutoEmptyCellsOnly;
653 // still have some width to spread
654 for (unsigned i = nEffCols; i; ) {
656 // variable columns with empty cells only don't get any width
657 if (m_layoutStruct[i].effectiveLogicalWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly)
659 int cellLogicalWidth = available / total;
660 available -= cellLogicalWidth;
662 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth;
666 // If we have overallocated, reduce every cell according to the difference between desired width and minwidth
667 // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing.
669 // Need to reduce cells with the following prioritization:
674 // This is basically the reverse of how we grew the cells.
676 int logicalWidthBeyondMin = 0;
677 for (unsigned i = nEffCols; i; ) {
679 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
680 if (logicalWidth.isAuto())
681 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
684 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
686 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
687 if (logicalWidth.isAuto()) {
688 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
689 int reduce = available * minMaxDiff / logicalWidthBeyondMin;
690 m_layoutStruct[i].computedLogicalWidth += reduce;
692 logicalWidthBeyondMin -= minMaxDiff;
700 int logicalWidthBeyondMin = 0;
701 for (unsigned i = nEffCols; i; ) {
703 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
704 if (logicalWidth.isRelative())
705 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
708 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
710 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
711 if (logicalWidth.isRelative()) {
712 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
713 int reduce = available * minMaxDiff / logicalWidthBeyondMin;
714 m_layoutStruct[i].computedLogicalWidth += reduce;
716 logicalWidthBeyondMin -= minMaxDiff;
724 int logicalWidthBeyondMin = 0;
725 for (unsigned i = nEffCols; i; ) {
727 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
728 if (logicalWidth.isFixed())
729 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
732 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
734 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
735 if (logicalWidth.isFixed()) {
736 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
737 int reduce = available * minMaxDiff / logicalWidthBeyondMin;
738 m_layoutStruct[i].computedLogicalWidth += reduce;
740 logicalWidthBeyondMin -= minMaxDiff;
748 int logicalWidthBeyondMin = 0;
749 for (unsigned i = nEffCols; i; ) {
751 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
752 if (logicalWidth.isPercent())
753 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
756 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) {
758 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth;
759 if (logicalWidth.isPercent()) {
760 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth;
761 int reduce = available * minMaxDiff / logicalWidthBeyondMin;
762 m_layoutStruct[i].computedLogicalWidth += reduce;
764 logicalWidthBeyondMin -= minMaxDiff;
773 for (size_t i = 0; i < nEffCols; ++i) {
774 m_table->columnPositions()[i] = pos;
775 pos += m_layoutStruct[i].computedLogicalWidth + m_table->hBorderSpacing();
777 m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos;