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 "core/layout/TableLayoutAlgorithmAuto.h"
25 #include "core/layout/LayoutTable.h"
26 #include "core/layout/LayoutTableCell.h"
27 #include "core/layout/LayoutTableCol.h"
28 #include "core/layout/LayoutTableSection.h"
29 #include "core/layout/TextAutosizer.h"
33 TableLayoutAlgorithmAuto::TableLayoutAlgorithmAuto(LayoutTable
* table
)
34 : TableLayoutAlgorithm(table
)
36 , m_effectiveLogicalWidthDirty(true)
40 TableLayoutAlgorithmAuto::~TableLayoutAlgorithmAuto()
44 void TableLayoutAlgorithmAuto::recalcColumn(unsigned effCol
)
46 Layout
& columnLayout
= m_layoutStruct
[effCol
];
48 LayoutTableCell
* fixedContributor
= nullptr;
49 LayoutTableCell
* maxContributor
= nullptr;
51 for (LayoutObject
* child
= m_table
->children()->firstChild(); child
; child
= child
->nextSibling()) {
52 if (child
->isLayoutTableCol()) {
53 // LayoutTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits
54 // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's
55 // ancestors as dirty.
56 toLayoutTableCol(child
)->clearPreferredLogicalWidthsDirtyBits();
57 } else if (child
->isTableSection()) {
58 LayoutTableSection
* section
= toLayoutTableSection(child
);
59 unsigned numRows
= section
->numRows();
60 for (unsigned i
= 0; i
< numRows
; i
++) {
61 LayoutTableSection::CellStruct current
= section
->cellAt(i
, effCol
);
62 LayoutTableCell
* cell
= current
.primaryCell();
64 if (current
.inColSpan
|| !cell
)
66 columnLayout
.columnHasNoCells
= false;
68 if (cell
->maxPreferredLogicalWidth())
69 columnLayout
.emptyCellsOnly
= false;
71 if (cell
->colSpan() == 1) {
72 columnLayout
.minLogicalWidth
= std::max
<int>(cell
->minPreferredLogicalWidth(), columnLayout
.minLogicalWidth
);
73 if (cell
->maxPreferredLogicalWidth() > columnLayout
.maxLogicalWidth
) {
74 columnLayout
.maxLogicalWidth
= cell
->maxPreferredLogicalWidth();
75 maxContributor
= cell
;
78 // All browsers implement a size limit on the cell's max width.
79 // Our limit is based on KHTML's representation that used 16 bits widths.
80 // FIXME: Other browsers have a lower limit for the cell's max width.
81 const int cCellMaxWidth
= 32760;
82 Length cellLogicalWidth
= cell
->styleOrColLogicalWidth();
83 // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725
84 if (cellLogicalWidth
.isCalculated())
85 cellLogicalWidth
= Length(); // Make it Auto
86 if (cellLogicalWidth
.value() > cCellMaxWidth
)
87 cellLogicalWidth
.setValue(cCellMaxWidth
);
88 if (cellLogicalWidth
.isNegative())
89 cellLogicalWidth
.setValue(0);
90 switch (cellLogicalWidth
.type()) {
93 if (cellLogicalWidth
.isPositive() && !columnLayout
.logicalWidth
.hasPercent()) {
94 int logicalWidth
= cell
->adjustBorderBoxLogicalWidthForBoxSizing(cellLogicalWidth
.value());
95 if (columnLayout
.logicalWidth
.isFixed()) {
97 if ((logicalWidth
> columnLayout
.logicalWidth
.value())
98 || ((columnLayout
.logicalWidth
.value() == logicalWidth
) && (maxContributor
== cell
))) {
99 columnLayout
.logicalWidth
.setValue(Fixed
, logicalWidth
);
100 fixedContributor
= cell
;
103 columnLayout
.logicalWidth
.setValue(Fixed
, logicalWidth
);
104 fixedContributor
= cell
;
110 // TODO(alancutter): Make this work correctly for calc lengths.
111 if (cellLogicalWidth
.isPositive() && (!columnLayout
.logicalWidth
.hasPercent() || cellLogicalWidth
.value() > columnLayout
.logicalWidth
.value()))
112 columnLayout
.logicalWidth
= cellLogicalWidth
;
117 } else if (!effCol
|| section
->primaryCellAt(i
, effCol
- 1) != cell
) {
118 // If a cell originates in this spanning column ensure we have a min/max width of at least 1px for it.
119 columnLayout
.minLogicalWidth
= std::max
<int>(columnLayout
.minLogicalWidth
, cell
->maxPreferredLogicalWidth() ? 1 : 0);
121 // This spanning cell originates in this column. Insert the cell into spanning cells list.
122 insertSpanCell(cell
);
129 if (columnLayout
.logicalWidth
.isFixed()) {
130 if (m_table
->document().inQuirksMode() && columnLayout
.maxLogicalWidth
> columnLayout
.logicalWidth
.value() && fixedContributor
!= maxContributor
) {
131 columnLayout
.logicalWidth
= Length();
132 fixedContributor
= nullptr;
136 columnLayout
.maxLogicalWidth
= std::max(columnLayout
.maxLogicalWidth
, columnLayout
.minLogicalWidth
);
139 void TableLayoutAlgorithmAuto::fullRecalc()
141 m_hasPercent
= false;
142 m_effectiveLogicalWidthDirty
= true;
144 unsigned nEffCols
= m_table
->numEffCols();
145 m_layoutStruct
.resize(nEffCols
);
146 m_layoutStruct
.fill(Layout());
149 Length groupLogicalWidth
;
150 unsigned currentColumn
= 0;
151 for (LayoutTableCol
* column
= m_table
->firstColumn(); column
; column
= column
->nextColumn()) {
152 if (column
->isTableColumnGroupWithColumnChildren()) {
153 groupLogicalWidth
= column
->style()->logicalWidth();
155 Length colLogicalWidth
= column
->style()->logicalWidth();
156 // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725
157 if (colLogicalWidth
.isCalculated() || colLogicalWidth
.isAuto())
158 colLogicalWidth
= groupLogicalWidth
;
159 // TODO(alancutter): Make this work correctly for calc lengths.
160 if ((colLogicalWidth
.isFixed() || colLogicalWidth
.hasPercent()) && colLogicalWidth
.isZero())
161 colLogicalWidth
= Length();
162 unsigned effCol
= m_table
->colToEffCol(currentColumn
);
163 unsigned span
= column
->span();
164 if (!colLogicalWidth
.isAuto() && span
== 1 && effCol
< nEffCols
&& m_table
->spanOfEffCol(effCol
) == 1) {
165 m_layoutStruct
[effCol
].logicalWidth
= colLogicalWidth
;
166 if (colLogicalWidth
.isFixed() && m_layoutStruct
[effCol
].maxLogicalWidth
< colLogicalWidth
.value())
167 m_layoutStruct
[effCol
].maxLogicalWidth
= colLogicalWidth
.value();
169 currentColumn
+= span
;
172 // For the last column in a column-group, we invalidate our group logical width.
173 if (column
->isTableColumn() && !column
->nextSibling())
174 groupLogicalWidth
= Length();
177 for (unsigned i
= 0; i
< nEffCols
; i
++)
181 // FIXME: This needs to be adapted for vertical writing modes.
182 static bool shouldScaleColumns(LayoutTable
* table
)
184 // A special case. If this table is not fixed width and contained inside
185 // a cell, then don't bloat the maxwidth by examining percentage growth.
188 Length tw
= table
->style()->width();
189 if ((tw
.isAuto() || tw
.hasPercent()) && !table
->isOutOfFlowPositioned()) {
190 LayoutBlock
* cb
= table
->containingBlock();
191 while (cb
&& !cb
->isLayoutView() && !cb
->isTableCell()
192 && cb
->style()->width().isAuto() && !cb
->isOutOfFlowPositioned())
193 cb
= cb
->containingBlock();
196 if (cb
&& cb
->isTableCell()
197 && (cb
->style()->width().isAuto() || cb
->style()->width().hasPercent())) {
198 LayoutTableCell
* cell
= toLayoutTableCell(cb
);
199 if (cell
->colSpan() > 1 || cell
->table()->style()->width().isAuto())
202 table
= cell
->table();
211 void TableLayoutAlgorithmAuto::computeIntrinsicLogicalWidths(LayoutUnit
& minWidth
, LayoutUnit
& maxWidth
)
213 TextAutosizer::TableLayoutScope
textAutosizerTableLayoutScope(m_table
);
217 int spanMaxLogicalWidth
= calcEffectiveLogicalWidth();
220 float maxPercent
= 0;
221 float maxNonPercent
= 0;
222 bool scaleColumns
= shouldScaleColumns(m_table
);
224 // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero.
225 // FIXME: Handle the 0% cases properly.
226 const float epsilon
= 1 / 128.0f
;
228 float remainingPercent
= 100;
229 for (size_t i
= 0; i
< m_layoutStruct
.size(); ++i
) {
230 minWidth
+= m_layoutStruct
[i
].effectiveMinLogicalWidth
;
231 maxWidth
+= m_layoutStruct
[i
].effectiveMaxLogicalWidth
;
233 if (m_layoutStruct
[i
].effectiveLogicalWidth
.hasPercent()) {
234 float percent
= std::min(static_cast<float>(m_layoutStruct
[i
].effectiveLogicalWidth
.percent()), remainingPercent
);
235 float logicalWidth
= static_cast<float>(m_layoutStruct
[i
].effectiveMaxLogicalWidth
) * 100 / std::max(percent
, epsilon
);
236 maxPercent
= std::max(logicalWidth
, maxPercent
);
237 remainingPercent
-= percent
;
239 maxNonPercent
+= m_layoutStruct
[i
].effectiveMaxLogicalWidth
;
245 maxNonPercent
= maxNonPercent
* 100 / std::max(remainingPercent
, epsilon
);
246 maxWidth
= std::max
<int>(maxWidth
, static_cast<int>(std::min(maxNonPercent
, static_cast<float>(tableMaxWidth
))));
247 maxWidth
= std::max
<int>(maxWidth
, static_cast<int>(std::min(maxPercent
, static_cast<float>(tableMaxWidth
))));
250 maxWidth
= std::max
<int>(maxWidth
, spanMaxLogicalWidth
);
253 void TableLayoutAlgorithmAuto::applyPreferredLogicalWidthQuirks(LayoutUnit
& minWidth
, LayoutUnit
& maxWidth
) const
255 Length tableLogicalWidth
= m_table
->style()->logicalWidth();
256 if (tableLogicalWidth
.isFixed() && tableLogicalWidth
.isPositive()) {
257 // |minWidth| is the result of measuring the intrinsic content's size. Keep it to
258 // make sure we are *never* smaller than the actual content.
259 LayoutUnit minContentWidth
= minWidth
;
260 // FIXME: This line looks REALLY suspicious as it could allow the minimum
261 // preferred logical width to be smaller than the table content. This has
262 // to be cross-checked against other browsers.
263 minWidth
= maxWidth
= std::max
<int>(minWidth
, tableLogicalWidth
.value());
265 const Length
& styleMaxLogicalWidth
= m_table
->style()->logicalMaxWidth();
266 if (styleMaxLogicalWidth
.isFixed() && !styleMaxLogicalWidth
.isNegative()) {
267 minWidth
= std::min
<int>(minWidth
, styleMaxLogicalWidth
.value());
268 minWidth
= std::max(minWidth
, minContentWidth
);
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 TableLayoutAlgorithmAuto::calcEffectiveLogicalWidth()
280 int 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 LayoutTableCell
* cell
= m_spanCells
[i
];
296 unsigned span
= cell
->colSpan();
298 Length cellLogicalWidth
= cell
->styleOrColLogicalWidth();
299 // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725
300 if (cellLogicalWidth
.isZero() || cellLogicalWidth
.isCalculated())
301 cellLogicalWidth
= Length(); // Make it Auto
303 unsigned effCol
= m_table
->colToEffCol(cell
->col());
304 size_t lastCol
= effCol
;
305 int cellMinLogicalWidth
= cell
->minPreferredLogicalWidth() + spacingInRowDirection
;
306 int cellMaxLogicalWidth
= cell
->maxPreferredLogicalWidth() + spacingInRowDirection
;
307 float totalPercent
= 0;
308 int spanMinLogicalWidth
= 0;
309 int spanMaxLogicalWidth
= 0;
310 bool allColsArePercent
= true;
311 bool allColsAreFixed
= true;
312 bool haveAuto
= false;
313 bool spanHasEmptyCellsOnly
= true;
315 while (lastCol
< nEffCols
&& span
> 0) {
316 Layout
& columnLayout
= m_layoutStruct
[lastCol
];
317 switch (columnLayout
.logicalWidth
.type()) {
319 totalPercent
+= columnLayout
.logicalWidth
.percent();
320 allColsAreFixed
= false;
323 if (columnLayout
.logicalWidth
.value() > 0) {
324 fixedWidth
+= columnLayout
.logicalWidth
.value();
325 allColsArePercent
= false;
326 // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad
327 // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither.
335 // If the column is a percentage width, do not let the spanning cell overwrite the
336 // width value. This caused a mis-layout on amazon.com.
338 // <table border=2 width=100%><
339 // <tr><td>1</td><td colspan=2>2-3</tr>
340 // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr>
342 // TODO(alancutter): Make this work correctly for calc lengths.
343 if (!columnLayout
.effectiveLogicalWidth
.hasPercent()) {
344 columnLayout
.effectiveLogicalWidth
= Length();
345 allColsArePercent
= false;
347 totalPercent
+= columnLayout
.effectiveLogicalWidth
.percent();
349 allColsAreFixed
= false;
351 if (!columnLayout
.emptyCellsOnly
)
352 spanHasEmptyCellsOnly
= false;
353 span
-= m_table
->spanOfEffCol(lastCol
);
354 spanMinLogicalWidth
+= columnLayout
.effectiveMinLogicalWidth
;
355 spanMaxLogicalWidth
+= columnLayout
.effectiveMaxLogicalWidth
;
357 cellMinLogicalWidth
-= spacingInRowDirection
;
358 cellMaxLogicalWidth
-= spacingInRowDirection
;
361 // adjust table max width if needed
362 if (cellLogicalWidth
.hasPercent()) {
363 if (totalPercent
> cellLogicalWidth
.percent() || allColsArePercent
) {
364 // can't satify this condition, treat as variable
365 cellLogicalWidth
= Length();
367 maxLogicalWidth
= std::max(maxLogicalWidth
, static_cast<int>(std::max(spanMaxLogicalWidth
, cellMaxLogicalWidth
) * 100 / cellLogicalWidth
.percent()));
369 // all non percent columns in the span get percent values to sum up correctly.
370 float percentMissing
= cellLogicalWidth
.percent() - totalPercent
;
372 for (unsigned pos
= effCol
; pos
< lastCol
; ++pos
) {
373 if (!m_layoutStruct
[pos
].effectiveLogicalWidth
.hasPercent())
374 totalWidth
+= m_layoutStruct
[pos
].clampedEffectiveMaxLogicalWidth();
377 for (unsigned pos
= effCol
; pos
< lastCol
&& totalWidth
> 0; ++pos
) {
378 if (!m_layoutStruct
[pos
].effectiveLogicalWidth
.hasPercent()) {
379 float percent
= percentMissing
* static_cast<float>(m_layoutStruct
[pos
].effectiveMaxLogicalWidth
) / totalWidth
;
380 totalWidth
-= m_layoutStruct
[pos
].clampedEffectiveMaxLogicalWidth();
381 percentMissing
-= percent
;
383 m_layoutStruct
[pos
].effectiveLogicalWidth
.setValue(Percent
, percent
);
385 m_layoutStruct
[pos
].effectiveLogicalWidth
= Length();
391 // make sure minWidth and maxWidth of the spanning cell are honoured
392 if (cellMinLogicalWidth
> spanMinLogicalWidth
) {
393 if (allColsAreFixed
) {
394 for (unsigned pos
= effCol
; fixedWidth
> 0 && pos
< lastCol
; ++pos
) {
395 int cellLogicalWidth
= std::max(m_layoutStruct
[pos
].effectiveMinLogicalWidth
, static_cast<int>(cellMinLogicalWidth
* m_layoutStruct
[pos
].logicalWidth
.value() / fixedWidth
));
396 fixedWidth
-= m_layoutStruct
[pos
].logicalWidth
.value();
397 cellMinLogicalWidth
-= cellLogicalWidth
;
398 m_layoutStruct
[pos
].effectiveMinLogicalWidth
= cellLogicalWidth
;
400 } else if (allColsArePercent
) {
401 // In this case, we just split the colspan's min amd max widths following the percentage.
402 int allocatedMinLogicalWidth
= 0;
403 int allocatedMaxLogicalWidth
= 0;
404 for (unsigned pos
= effCol
; pos
< lastCol
; ++pos
) {
405 // TODO(alancutter): Make this work correctly for calc lengths.
406 ASSERT(m_layoutStruct
[pos
].logicalWidth
.hasPercent() || m_layoutStruct
[pos
].effectiveLogicalWidth
.hasPercent());
407 // |allColsArePercent| means that either the logicalWidth *or* the effectiveLogicalWidth are percents, handle both of them here.
408 float percent
= m_layoutStruct
[pos
].logicalWidth
.hasPercent() ? m_layoutStruct
[pos
].logicalWidth
.percent() : m_layoutStruct
[pos
].effectiveLogicalWidth
.percent();
409 int columnMinLogicalWidth
= static_cast<int>(percent
* cellMinLogicalWidth
/ totalPercent
);
410 int columnMaxLogicalWidth
= static_cast<int>(percent
* cellMaxLogicalWidth
/ totalPercent
);
411 m_layoutStruct
[pos
].effectiveMinLogicalWidth
= std::max(m_layoutStruct
[pos
].effectiveMinLogicalWidth
, columnMinLogicalWidth
);
412 m_layoutStruct
[pos
].effectiveMaxLogicalWidth
= columnMaxLogicalWidth
;
413 allocatedMinLogicalWidth
+= columnMinLogicalWidth
;
414 allocatedMaxLogicalWidth
+= columnMaxLogicalWidth
;
416 ASSERT(allocatedMinLogicalWidth
<= cellMinLogicalWidth
);
417 ASSERT(allocatedMaxLogicalWidth
<= cellMaxLogicalWidth
);
418 cellMinLogicalWidth
-= allocatedMinLogicalWidth
;
419 cellMaxLogicalWidth
-= allocatedMaxLogicalWidth
;
421 int remainingMaxLogicalWidth
= spanMaxLogicalWidth
;
422 int remainingMinLogicalWidth
= spanMinLogicalWidth
;
424 // Give min to variable first, to fixed second, and to others third.
425 for (unsigned pos
= effCol
; remainingMaxLogicalWidth
>= 0 && pos
< lastCol
; ++pos
) {
426 if (m_layoutStruct
[pos
].logicalWidth
.isFixed() && haveAuto
&& fixedWidth
<= cellMinLogicalWidth
) {
427 int colMinLogicalWidth
= std::max
<int>(m_layoutStruct
[pos
].effectiveMinLogicalWidth
, m_layoutStruct
[pos
].logicalWidth
.value());
428 fixedWidth
-= m_layoutStruct
[pos
].logicalWidth
.value();
429 remainingMinLogicalWidth
-= m_layoutStruct
[pos
].effectiveMinLogicalWidth
;
430 remainingMaxLogicalWidth
-= m_layoutStruct
[pos
].effectiveMaxLogicalWidth
;
431 cellMinLogicalWidth
-= colMinLogicalWidth
;
432 m_layoutStruct
[pos
].effectiveMinLogicalWidth
= colMinLogicalWidth
;
436 for (unsigned pos
= effCol
; remainingMaxLogicalWidth
>= 0 && pos
< lastCol
&& remainingMinLogicalWidth
< cellMinLogicalWidth
; ++pos
) {
437 if (!(m_layoutStruct
[pos
].logicalWidth
.isFixed() && haveAuto
&& fixedWidth
<= cellMinLogicalWidth
)) {
438 int colMinLogicalWidth
= std::max
<int>(m_layoutStruct
[pos
].effectiveMinLogicalWidth
, static_cast<int>(remainingMaxLogicalWidth
? cellMinLogicalWidth
* static_cast<float>(m_layoutStruct
[pos
].effectiveMaxLogicalWidth
) / remainingMaxLogicalWidth
: cellMinLogicalWidth
));
439 colMinLogicalWidth
= std::min
<int>(m_layoutStruct
[pos
].effectiveMinLogicalWidth
+ (cellMinLogicalWidth
- remainingMinLogicalWidth
), colMinLogicalWidth
);
440 remainingMaxLogicalWidth
-= m_layoutStruct
[pos
].effectiveMaxLogicalWidth
;
441 remainingMinLogicalWidth
-= m_layoutStruct
[pos
].effectiveMinLogicalWidth
;
442 cellMinLogicalWidth
-= colMinLogicalWidth
;
443 m_layoutStruct
[pos
].effectiveMinLogicalWidth
= colMinLogicalWidth
;
448 if (!cellLogicalWidth
.hasPercent()) {
449 if (cellMaxLogicalWidth
> spanMaxLogicalWidth
) {
450 for (unsigned pos
= effCol
; spanMaxLogicalWidth
>= 0 && pos
< lastCol
; ++pos
) {
451 int colMaxLogicalWidth
= std::max(m_layoutStruct
[pos
].effectiveMaxLogicalWidth
, static_cast<int>(spanMaxLogicalWidth
? cellMaxLogicalWidth
* static_cast<float>(m_layoutStruct
[pos
].effectiveMaxLogicalWidth
) / spanMaxLogicalWidth
: cellMaxLogicalWidth
));
452 spanMaxLogicalWidth
-= m_layoutStruct
[pos
].effectiveMaxLogicalWidth
;
453 cellMaxLogicalWidth
-= colMaxLogicalWidth
;
454 m_layoutStruct
[pos
].effectiveMaxLogicalWidth
= colMaxLogicalWidth
;
458 for (unsigned pos
= effCol
; pos
< lastCol
; ++pos
)
459 m_layoutStruct
[pos
].maxLogicalWidth
= std::max(m_layoutStruct
[pos
].maxLogicalWidth
, m_layoutStruct
[pos
].minLogicalWidth
);
461 // treat span ranges consisting of empty cells only as if they had content
462 if (spanHasEmptyCellsOnly
) {
463 for (unsigned pos
= effCol
; pos
< lastCol
; ++pos
)
464 m_layoutStruct
[pos
].emptyCellsOnly
= false;
467 m_effectiveLogicalWidthDirty
= false;
469 return std::min(maxLogicalWidth
, INT_MAX
/ 2);
472 /* gets all cells that originate in a column and have a cellspan > 1
473 Sorts them by increasing cellspan
475 void TableLayoutAlgorithmAuto::insertSpanCell(LayoutTableCell
*cell
)
477 ASSERT_ARG(cell
, cell
&& cell
->colSpan() != 1);
478 if (!cell
|| cell
->colSpan() == 1)
481 unsigned size
= m_spanCells
.size();
482 if (!size
|| m_spanCells
[size
-1] != 0) {
483 m_spanCells
.grow(size
+ 10);
484 for (unsigned i
= 0; i
< 10; i
++)
485 m_spanCells
[size
+i
] = 0;
489 // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better
491 unsigned span
= cell
->colSpan();
492 while (pos
< m_spanCells
.size() && m_spanCells
[pos
] && span
> m_spanCells
[pos
]->colSpan())
494 memmove(m_spanCells
.data()+pos
+1, m_spanCells
.data()+pos
, (size
-pos
-1)*sizeof(LayoutTableCell
*));
495 m_spanCells
[pos
] = cell
;
499 void TableLayoutAlgorithmAuto::layout()
501 // table layout based on the values collected in the layout structure.
502 int tableLogicalWidth
= m_table
->logicalWidth() - m_table
->bordersPaddingAndSpacingInRowDirection();
503 int available
= tableLogicalWidth
;
504 size_t nEffCols
= m_table
->numEffCols();
506 // FIXME: It is possible to be called without having properly updated our internal representation.
507 // This means that our preferred logical widths were not recomputed as expected.
508 if (nEffCols
!= m_layoutStruct
.size()) {
510 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
511 nEffCols
= m_table
->numEffCols();
514 if (m_effectiveLogicalWidthDirty
)
515 calcEffectiveLogicalWidth();
517 bool havePercent
= false;
521 float totalFixed
= 0;
522 float totalPercent
= 0;
524 unsigned numAutoEmptyCellsOnly
= 0;
526 // fill up every cell with its minWidth
527 for (size_t i
= 0; i
< nEffCols
; ++i
) {
528 int cellLogicalWidth
= m_layoutStruct
[i
].effectiveMinLogicalWidth
;
529 m_layoutStruct
[i
].computedLogicalWidth
= cellLogicalWidth
;
530 available
-= cellLogicalWidth
;
531 Length
& logicalWidth
= m_layoutStruct
[i
].effectiveLogicalWidth
;
532 switch (logicalWidth
.type()) {
535 totalPercent
+= logicalWidth
.percent();
539 totalFixed
+= m_layoutStruct
[i
].clampedEffectiveMaxLogicalWidth();
543 if (m_layoutStruct
[i
].emptyCellsOnly
) {
544 numAutoEmptyCellsOnly
++;
547 totalAuto
+= m_layoutStruct
[i
].clampedEffectiveMaxLogicalWidth();
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
.hasPercent()) {
561 int cellLogicalWidth
= std::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
.hasPercent()) {
572 int cellLogicalWidth
= m_layoutStruct
[i
].computedLogicalWidth
;
573 int reduction
= std::min(cellLogicalWidth
, excess
);
574 // the lines below might look inconsistent, but that's the way it's handled in mozilla
576 int newLogicalWidth
= std::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 // Give each auto width column its share of the available width, non-empty columns then empty columns.
596 if (available
> 0 && numAuto
) {
597 available
+= allocAuto
;
598 distributeWidthToColumns
<float, Auto
, NonEmptyCells
, InitialWidth
, StartToEnd
>(available
, totalAuto
);
600 if (available
> 0 && numAutoEmptyCellsOnly
) {
601 unsigned total
= numAutoEmptyCellsOnly
;
602 distributeWidthToColumns
<unsigned, Auto
, EmptyCells
, InitialWidth
, StartToEnd
>(available
, total
);
605 // Any remaining available width expands fixed width, percent width, and non-empty auto width columns, in that order.
606 if (available
> 0 && numFixed
)
607 distributeWidthToColumns
<float, Fixed
, AllCells
, ExtraWidth
, StartToEnd
>(available
, totalFixed
);
609 if (available
> 0 && m_hasPercent
&& totalPercent
< 100)
610 distributeWidthToColumns
<float, Percent
, AllCells
, ExtraWidth
, StartToEnd
>(available
, totalPercent
);
612 if (available
> 0 && nEffCols
> numAutoEmptyCellsOnly
) {
613 unsigned total
= nEffCols
- numAutoEmptyCellsOnly
;
614 // Starting from the last cell is for compatibility with FF/IE - it isn't specified anywhere.
615 distributeWidthToColumns
<unsigned, Auto
, NonEmptyCells
, LeftoverWidth
, EndToStart
>(available
, total
);
618 // If we have overallocated, reduce every cell according to the difference between desired width and minwidth
619 // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing.
620 // This is basically the reverse of how we grew the cells.
622 shrinkColumnWidth(Auto
, available
);
624 shrinkColumnWidth(Fixed
, available
);
626 shrinkColumnWidth(Percent
, available
);
629 for (size_t i
= 0; i
< nEffCols
; ++i
) {
630 m_table
->setColumnPosition(i
, pos
);
631 pos
+= m_layoutStruct
[i
].computedLogicalWidth
+ m_table
->hBorderSpacing();
633 m_table
->setColumnPosition(m_table
->columnPositions().size() - 1, pos
);
636 template<typename Total
, LengthType lengthType
, CellsToProcess cellsToProcess
, DistributionMode distributionMode
, DistributionDirection distributionDirection
>
637 void TableLayoutAlgorithmAuto::distributeWidthToColumns(int& available
, Total total
)
639 // TODO(alancutter): Make this work correctly for calc lengths.
640 int nEffCols
= static_cast<int>(m_table
->numEffCols());
641 bool startToEnd
= distributionDirection
== StartToEnd
;
642 for (int i
= startToEnd
? 0 : nEffCols
- 1; startToEnd
? i
< nEffCols
: i
> -1; startToEnd
? ++i
: --i
) {
643 const Length
& logicalWidth
= m_layoutStruct
[i
].effectiveLogicalWidth
;
644 if (cellsToProcess
== NonEmptyCells
&& logicalWidth
.isAuto() && m_layoutStruct
[i
].emptyCellsOnly
)
646 // When allocating width to columns with nothing but empty cells we avoid
647 // columns that exist only to flesh out a colspan and have no actual cells.
648 if (cellsToProcess
== EmptyCells
&& logicalWidth
.isAuto() && (!m_layoutStruct
[i
].emptyCellsOnly
|| m_layoutStruct
[i
].columnHasNoCells
))
650 if (distributionMode
!= LeftoverWidth
&& logicalWidth
.type() != lengthType
)
654 if (distributionMode
!= LeftoverWidth
) {
655 if (lengthType
== Percent
)
656 factor
= logicalWidth
.percent();
657 else if (lengthType
== Auto
|| lengthType
== Fixed
)
658 factor
= m_layoutStruct
[i
].clampedEffectiveMaxLogicalWidth();
661 int newWidth
= available
* factor
/ total
;
662 int cellLogicalWidth
= (distributionMode
== InitialWidth
) ? max
<int>(m_layoutStruct
[i
].computedLogicalWidth
, newWidth
) : newWidth
;
663 available
-= cellLogicalWidth
;
665 m_layoutStruct
[i
].computedLogicalWidth
= (distributionMode
== InitialWidth
) ? cellLogicalWidth
: m_layoutStruct
[i
].computedLogicalWidth
+ cellLogicalWidth
;
667 // If we have run out of width to allocate we're done.
668 // TODO(rhogan): Extend this to Fixed as well.
669 if (lengthType
== Percent
&& (!available
|| !total
))
671 if (lengthType
== Auto
&& !total
)
676 void TableLayoutAlgorithmAuto::shrinkColumnWidth(const LengthType
& lengthType
, int& available
)
678 size_t nEffCols
= m_table
->numEffCols();
679 int logicalWidthBeyondMin
= 0;
680 for (unsigned i
= nEffCols
; i
; ) {
682 Length
& logicalWidth
= m_layoutStruct
[i
].effectiveLogicalWidth
;
683 if (logicalWidth
.type() == lengthType
)
684 logicalWidthBeyondMin
+= m_layoutStruct
[i
].computedLogicalWidth
- m_layoutStruct
[i
].effectiveMinLogicalWidth
;
687 for (unsigned i
= nEffCols
; i
&& logicalWidthBeyondMin
> 0; ) {
689 Length
& logicalWidth
= m_layoutStruct
[i
].effectiveLogicalWidth
;
690 if (logicalWidth
.type() == lengthType
) {
691 int minMaxDiff
= m_layoutStruct
[i
].computedLogicalWidth
- m_layoutStruct
[i
].effectiveMinLogicalWidth
;
692 int reduce
= available
* minMaxDiff
/ logicalWidthBeyondMin
;
693 m_layoutStruct
[i
].computedLogicalWidth
+= reduce
;
695 logicalWidthBeyondMin
-= minMaxDiff
;