2 * Copyright (C) 2002 Lars Knoll (knoll@kde.org)
3 * (C) 2002 Dirk Mueller (mueller@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc.
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/TableLayoutAlgorithmFixed.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 "platform/LayoutUnit.h"
32 The text below is from the CSS 2.1 specs.
36 With this (fast) algorithm, the horizontal layout of the table does
37 not depend on the contents of the cells; it only depends on the
38 table's width, the width of the columns, and borders or cell
41 The table's width may be specified explicitly with the 'width'
42 property. A value of 'auto' (for both 'display: table' and 'display:
43 inline-table') means use the automatic table layout algorithm.
45 In the fixed table layout algorithm, the width of each column is
46 determined as follows:
48 1. A column element with a value other than 'auto' for the 'width'
49 property sets the width for that column.
51 2. Otherwise, a cell in the first row with a value other than
52 'auto' for the 'width' property sets the width for that column. If
53 the cell spans more than one column, the width is divided over the
56 3. Any remaining columns equally divide the remaining horizontal
57 table space (minus borders or cell spacing).
59 The width of the table is then the greater of the value of the
60 'width' property for the table element and the sum of the column
61 widths (plus cell spacing or borders). If the table is wider than
62 the columns, the extra space should be distributed over the columns.
65 In this manner, the user agent can begin to lay out the table once
66 the entire first row has been received. Cells in subsequent rows do
67 not affect column widths. Any cell that has content that overflows
68 uses the 'overflow' property to determine whether to clip the
74 TableLayoutAlgorithmFixed::TableLayoutAlgorithmFixed(LayoutTable
* table
)
75 : TableLayoutAlgorithm(table
)
79 int TableLayoutAlgorithmFixed::calcWidthArray()
81 // FIXME: We might want to wait until we have all of the first row before computing for the first time.
84 // iterate over all <col> elements
85 unsigned nEffCols
= m_table
->numEffCols();
86 m_width
.resize(nEffCols
);
87 m_width
.fill(Length(Auto
));
89 unsigned currentEffectiveColumn
= 0;
90 for (LayoutTableCol
* col
= m_table
->firstColumn(); col
; col
= col
->nextColumn()) {
91 // LayoutTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits
92 // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's
93 // ancestors as dirty.
94 col
->clearPreferredLogicalWidthsDirtyBits();
96 // Width specified by column-groups that have column child does not affect column width in fixed layout tables
97 if (col
->isTableColumnGroupWithColumnChildren())
100 Length colStyleLogicalWidth
= col
->style()->logicalWidth();
101 int effectiveColWidth
= 0;
102 if (colStyleLogicalWidth
.isFixed() && colStyleLogicalWidth
.value() > 0)
103 effectiveColWidth
= colStyleLogicalWidth
.value();
105 unsigned span
= col
->span();
107 unsigned spanInCurrentEffectiveColumn
;
108 if (currentEffectiveColumn
>= nEffCols
) {
109 m_table
->appendColumn(span
);
111 m_width
.append(Length());
112 spanInCurrentEffectiveColumn
= span
;
114 if (span
< m_table
->spanOfEffCol(currentEffectiveColumn
)) {
115 m_table
->splitColumn(currentEffectiveColumn
, span
);
117 m_width
.append(Length());
119 spanInCurrentEffectiveColumn
= m_table
->spanOfEffCol(currentEffectiveColumn
);
121 // TODO(alancutter): Make this work correctly for calc lengths.
122 if ((colStyleLogicalWidth
.isFixed() || colStyleLogicalWidth
.hasPercent()) && colStyleLogicalWidth
.isPositive()) {
123 m_width
[currentEffectiveColumn
] = colStyleLogicalWidth
;
124 m_width
[currentEffectiveColumn
] *= spanInCurrentEffectiveColumn
;
125 usedWidth
+= effectiveColWidth
* spanInCurrentEffectiveColumn
;
127 span
-= spanInCurrentEffectiveColumn
;
128 currentEffectiveColumn
++;
132 // Iterate over the first row in case some are unspecified.
133 LayoutTableSection
* section
= m_table
->topNonEmptySection();
137 unsigned currentColumn
= 0;
139 LayoutTableRow
* firstRow
= section
->firstRow();
140 for (LayoutTableCell
* cell
= firstRow
->firstCell(); cell
; cell
= cell
->nextCell()) {
141 Length logicalWidth
= cell
->styleOrColLogicalWidth();
143 // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725
144 if (logicalWidth
.isCalculated())
145 logicalWidth
= Length(); // Make it Auto
147 unsigned span
= cell
->colSpan();
148 int fixedBorderBoxLogicalWidth
= 0;
149 // FIXME: Support other length types. If the width is non-auto, it should probably just use
150 // LayoutBox::computeLogicalWidthUsing to compute the width.
151 if (logicalWidth
.isFixed() && logicalWidth
.isPositive()) {
152 fixedBorderBoxLogicalWidth
= cell
->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth
.value());
153 logicalWidth
.setValue(fixedBorderBoxLogicalWidth
);
156 unsigned usedSpan
= 0;
157 while (usedSpan
< span
&& currentColumn
< nEffCols
) {
158 float eSpan
= m_table
->spanOfEffCol(currentColumn
);
159 // Only set if no col element has already set it.
160 if (m_width
[currentColumn
].isAuto() && logicalWidth
.type() != Auto
) {
161 m_width
[currentColumn
] = logicalWidth
;
162 m_width
[currentColumn
] *= eSpan
/ span
;
163 usedWidth
+= fixedBorderBoxLogicalWidth
* eSpan
/ span
;
169 // TableLayoutAlgorithmFixed doesn't use min/maxPreferredLogicalWidths, but we need to clear the
170 // dirty bit on the cell so that we'll correctly mark its ancestors dirty
171 // in case we later call setPreferredLogicalWidthsDirty() on it later.
172 if (cell
->preferredLogicalWidthsDirty())
173 cell
->clearPreferredLogicalWidthsDirty();
179 void TableLayoutAlgorithmFixed::computeIntrinsicLogicalWidths(LayoutUnit
& minWidth
, LayoutUnit
& maxWidth
)
181 minWidth
= maxWidth
= calcWidthArray();
184 void TableLayoutAlgorithmFixed::applyPreferredLogicalWidthQuirks(LayoutUnit
& minWidth
, LayoutUnit
& maxWidth
) const
186 Length tableLogicalWidth
= m_table
->style()->logicalWidth();
187 if (tableLogicalWidth
.isFixed() && tableLogicalWidth
.isPositive())
188 minWidth
= maxWidth
= max
<int>(minWidth
, tableLogicalWidth
.value() - m_table
->bordersPaddingAndSpacingInRowDirection());
191 <table style="width:100%; background-color:red"><tr><td>
192 <table style="background-color:blue"><tr><td>
193 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
199 // In this example, the two inner tables should be as large as the outer table.
200 // We can achieve this effect by making the maxwidth of fixed tables with percentage
201 // widths be infinite.
202 if (m_table
->style()->logicalWidth().hasPercent() && maxWidth
< tableMaxWidth
)
203 maxWidth
= tableMaxWidth
;
206 void TableLayoutAlgorithmFixed::layout()
208 int tableLogicalWidth
= m_table
->logicalWidth() - m_table
->bordersPaddingAndSpacingInRowDirection();
209 unsigned nEffCols
= m_table
->numEffCols();
211 // FIXME: It is possible to be called without having properly updated our internal representation.
212 // This means that our preferred logical widths were not recomputed as expected.
213 if (nEffCols
!= m_width
.size()) {
215 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups).
216 nEffCols
= m_table
->numEffCols();
219 Vector
<int> calcWidth(nEffCols
, 0);
221 unsigned numAuto
= 0;
222 unsigned autoSpan
= 0;
223 int totalFixedWidth
= 0;
224 int totalPercentWidth
= 0;
225 float totalPercent
= 0;
227 // Compute requirements and try to satisfy fixed and percent widths.
228 // Percentages are of the table's width, so for example
229 // for a table width of 100px with columns (40px, 10%), the 10% compute
230 // to 10px here, and will scale up to 20px in the final (80px, 20px).
231 for (unsigned i
= 0; i
< nEffCols
; i
++) {
232 if (m_width
[i
].isFixed()) {
233 calcWidth
[i
] = m_width
[i
].value();
234 totalFixedWidth
+= calcWidth
[i
];
235 } else if (m_width
[i
].hasPercent()) {
236 // TODO(alancutter): Make this work correctly for calc lengths.
237 calcWidth
[i
] = valueForLength(m_width
[i
], tableLogicalWidth
);
238 totalPercentWidth
+= calcWidth
[i
];
239 totalPercent
+= m_width
[i
].percent();
240 } else if (m_width
[i
].isAuto()) {
242 autoSpan
+= m_table
->spanOfEffCol(i
);
246 int hspacing
= m_table
->hBorderSpacing();
247 int totalWidth
= totalFixedWidth
+ totalPercentWidth
;
248 if (!numAuto
|| totalWidth
> tableLogicalWidth
) {
249 // If there are no auto columns, or if the total is too wide, take
250 // what we have and scale it to fit as necessary.
251 if (totalWidth
!= tableLogicalWidth
) {
252 // Fixed widths only scale up
253 if (totalFixedWidth
&& totalWidth
< tableLogicalWidth
) {
255 for (unsigned i
= 0; i
< nEffCols
; i
++) {
256 if (m_width
[i
].isFixed()) {
257 calcWidth
[i
] = calcWidth
[i
] * tableLogicalWidth
/ totalWidth
;
258 totalFixedWidth
+= calcWidth
[i
];
263 totalPercentWidth
= 0;
264 for (unsigned i
= 0; i
< nEffCols
; i
++) {
265 // TODO(alancutter): Make this work correctly for calc lengths.
266 if (m_width
[i
].hasPercent()) {
267 calcWidth
[i
] = m_width
[i
].percent() * (tableLogicalWidth
- totalFixedWidth
) / totalPercent
;
268 totalPercentWidth
+= calcWidth
[i
];
272 totalWidth
= totalFixedWidth
+ totalPercentWidth
;
275 // Divide the remaining width among the auto columns.
276 ASSERT(autoSpan
>= numAuto
);
277 int remainingWidth
= tableLogicalWidth
- totalFixedWidth
- totalPercentWidth
- hspacing
* (autoSpan
- numAuto
);
279 for (unsigned i
= 0; i
< nEffCols
; i
++) {
280 if (m_width
[i
].isAuto()) {
281 unsigned span
= m_table
->spanOfEffCol(i
);
282 int w
= remainingWidth
* span
/ autoSpan
;
283 calcWidth
[i
] = w
+ hspacing
* (span
- 1);
289 ASSERT(autoSpan
>= span
);
293 // Last one gets the remainder.
295 calcWidth
[lastAuto
] += remainingWidth
;
296 totalWidth
= tableLogicalWidth
;
299 if (totalWidth
< tableLogicalWidth
) {
300 // Spread extra space over columns.
301 int remainingWidth
= tableLogicalWidth
- totalWidth
;
302 int total
= nEffCols
;
304 int w
= remainingWidth
/ total
;
306 calcWidth
[--total
] += w
;
309 calcWidth
[nEffCols
- 1] += remainingWidth
;
313 for (unsigned i
= 0; i
< nEffCols
; i
++) {
314 m_table
->setColumnPosition(i
, pos
);
315 pos
+= calcWidth
[i
] + hspacing
;
317 int colPositionsSize
= m_table
->columnPositions().size();
318 if (colPositionsSize
> 0)
319 m_table
->setColumnPosition(colPositionsSize
- 1, pos
);
322 void TableLayoutAlgorithmFixed::willChangeTableLayout()
324 // When switching table layout algorithm, we need to dirty the preferred
325 // logical widths as we cleared the bits without computing them.
326 // (see calcWidthArray above.) This optimization is preferred to always
327 // computing the logical widths we never intended to use.
328 m_table
->recalcSectionsIfNeeded();
329 for (LayoutTableSection
* section
= m_table
->topNonEmptySection(); section
; section
= m_table
->sectionBelow(section
)) {
330 for (unsigned i
= 0; i
< section
->numRows(); i
++) {
331 LayoutTableRow
* row
= section
->rowLayoutObjectAt(i
);
334 for (LayoutTableCell
* cell
= row
->firstCell(); cell
; cell
= cell
->nextCell())
335 cell
->setPreferredLogicalWidthsDirty();