1 /*
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 Apple Inc.
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 "FixedTableLayout.h"
24
25 #include "RenderTable.h"
26 #include "RenderTableCell.h"
27 #include "RenderTableCol.h"
28 #include "RenderTableSection.h"
29
30 /*
31 The text below is from the CSS 2.1 specs.
32
33 Fixed table layout
34
35 With this (fast) algorithm, the horizontal layout of the table does
36 not depend on the contents of the cells; it only depends on the
37 table's width, the width of the columns, and borders or cell
38 spacing.
39
40 The table's width may be specified explicitly with the 'width'
41 property. A value of 'auto' (for both 'display: table' and 'display:
42 inline-table') means use the automatic table layout algorithm.
43
44 In the fixed table layout algorithm, the width of each column is
45 determined as follows:
46
47 1. A column element with a value other than 'auto' for the 'width'
48 property sets the width for that column.
49
50 2. Otherwise, a cell in the first row with a value other than
51 'auto' for the 'width' property sets the width for that column. If
52 the cell spans more than one column, the width is divided over the
53 columns.
54
55 3. Any remaining columns equally divide the remaining horizontal
56 table space (minus borders or cell spacing).
57
58 The width of the table is then the greater of the value of the
59 'width' property for the table element and the sum of the column
60 widths (plus cell spacing or borders). If the table is wider than
61 the columns, the extra space should be distributed over the columns.
62
63
64 In this manner, the user agent can begin to lay out the table once
65 the entire first row has been received. Cells in subsequent rows do
66 not affect column widths. Any cell that has content that overflows
67 uses the 'overflow' property to determine whether to clip the
68 overflow content.
69 */
70
71 using namespace std;
72
73 namespace WebCore {
74
FixedTableLayout(RenderTable * table)75 FixedTableLayout::FixedTableLayout(RenderTable* table)
76 : TableLayout(table)
77 {
78 }
79
calcWidthArray(int)80 int FixedTableLayout::calcWidthArray(int)
81 {
82 int usedWidth = 0;
83
84 // iterate over all <col> elements
85 RenderObject* child = m_table->firstChild();
86 int nEffCols = m_table->numEffCols();
87 m_width.resize(nEffCols);
88 m_width.fill(Length(Auto));
89
90 int currentEffectiveColumn = 0;
91 Length grpWidth;
92 while (child && child->isTableCol()) {
93 RenderTableCol* col = toRenderTableCol(child);
94 if (col->firstChild())
95 grpWidth = col->style()->logicalWidth();
96 else {
97 Length w = col->style()->logicalWidth();
98 if (w.isAuto())
99 w = grpWidth;
100 int effWidth = 0;
101 if (w.isFixed() && w.value() > 0)
102 effWidth = w.value();
103
104 int span = col->span();
105 while (span) {
106 int spanInCurrentEffectiveColumn;
107 if (currentEffectiveColumn >= nEffCols) {
108 m_table->appendColumn(span);
109 nEffCols++;
110 m_width.append(Length());
111 spanInCurrentEffectiveColumn = span;
112 } else {
113 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) {
114 m_table->splitColumn(currentEffectiveColumn, span);
115 nEffCols++;
116 m_width.append(Length());
117 }
118 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn);
119 }
120 if ((w.isFixed() || w.isPercent()) && w.isPositive()) {
121 m_width[currentEffectiveColumn] = w;
122 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn;
123 usedWidth += effWidth * spanInCurrentEffectiveColumn;
124 }
125 span -= spanInCurrentEffectiveColumn;
126 currentEffectiveColumn++;
127 }
128 }
129 col->computePreferredLogicalWidths();
130
131 RenderObject* next = child->firstChild();
132 if (!next)
133 next = child->nextSibling();
134 if (!next && child->parent()->isTableCol()) {
135 next = child->parent()->nextSibling();
136 grpWidth = Length();
137 }
138 child = next;
139 }
140
141 // Iterate over the first row in case some are unspecified.
142 RenderTableSection* section = m_table->header();
143 if (!section)
144 section = m_table->firstBody();
145 if (!section)
146 section = m_table->footer();
147 if (section && !section->numRows())
148 section = m_table->sectionBelow(section, true);
149 if (section) {
150 int cCol = 0;
151 RenderObject* firstRow = section->firstChild();
152 child = firstRow->firstChild();
153 while (child) {
154 if (child->isTableCell()) {
155 RenderTableCell* cell = toRenderTableCell(child);
156 if (cell->preferredLogicalWidthsDirty())
157 cell->computePreferredLogicalWidths();
158
159 Length w = cell->styleOrColLogicalWidth();
160 int span = cell->colSpan();
161 int effWidth = 0;
162 if (w.isFixed() && w.isPositive())
163 effWidth = w.value();
164
165 int usedSpan = 0;
166 int i = 0;
167 while (usedSpan < span && cCol + i < nEffCols) {
168 float eSpan = m_table->spanOfEffCol(cCol + i);
169 // Only set if no col element has already set it.
170 if (m_width[cCol + i].isAuto() && w.type() != Auto) {
171 m_width[cCol + i] = w;
172 m_width[cCol + i] *= eSpan / span;
173 usedWidth += effWidth * eSpan / span;
174 }
175 usedSpan += eSpan;
176 i++;
177 }
178 cCol += i;
179 }
180 child = child->nextSibling();
181 }
182 }
183
184 return usedWidth;
185 }
186
187 // Use a very large value (in effect infinite). But not too large!
188 // numeric_limits<int>::max() will too easily overflow widths.
189 // Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp
190 #define TABLE_MAX_WIDTH 15000
191
computePreferredLogicalWidths(int & minWidth,int & maxWidth)192 void FixedTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth)
193 {
194 // FIXME: This entire calculation is incorrect for both minwidth and maxwidth.
195
196 // we might want to wait until we have all of the first row before
197 // layouting for the first time.
198
199 // only need to calculate the minimum width as the sum of the
200 // cols/cells with a fixed width.
201 //
202 // The maximum width is max(minWidth, tableWidth).
203 int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection();
204
205 int tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0;
206 int mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing;
207
208 minWidth = max(mw, tableLogicalWidth);
209 maxWidth = minWidth;
210
211 // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths().
212 // Here's the example for this one:
213 /*
214 <table style="width:100%; background-color:red"><tr><td>
215 <table style="background-color:blue"><tr><td>
216 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td>
217 Content
218 </td></tr></table>
219 </td></tr></table>
220 </td></tr></table>
221 */
222 // In this example, the two inner tables should be as large as the outer table.
223 // We can achieve this effect by making the maxwidth of fixed tables with percentage
224 // widths be infinite.
225 if (m_table->document()->inQuirksMode() && m_table->style()->logicalWidth().isPercent() && maxWidth < TABLE_MAX_WIDTH)
226 maxWidth = TABLE_MAX_WIDTH;
227 }
228
layout()229 void FixedTableLayout::layout()
230 {
231 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection();
232 int nEffCols = m_table->numEffCols();
233 Vector<int> calcWidth(nEffCols, 0);
234
235 int numAuto = 0;
236 int autoSpan = 0;
237 int totalFixedWidth = 0;
238 int totalPercentWidth = 0;
239 float totalPercent = 0;
240
241 // Compute requirements and try to satisfy fixed and percent widths.
242 // Percentages are of the table's width, so for example
243 // for a table width of 100px with columns (40px, 10%), the 10% compute
244 // to 10px here, and will scale up to 20px in the final (80px, 20px).
245 for (int i = 0; i < nEffCols; i++) {
246 if (m_width[i].isFixed()) {
247 calcWidth[i] = m_width[i].value();
248 totalFixedWidth += calcWidth[i];
249 } else if (m_width[i].isPercent()) {
250 calcWidth[i] = m_width[i].calcValue(tableLogicalWidth);
251 totalPercentWidth += calcWidth[i];
252 totalPercent += m_width[i].percent();
253 } else if (m_width[i].isAuto()) {
254 numAuto++;
255 autoSpan += m_table->spanOfEffCol(i);
256 }
257 }
258
259 int hspacing = m_table->hBorderSpacing();
260 int totalWidth = totalFixedWidth + totalPercentWidth;
261 if (!numAuto || totalWidth > tableLogicalWidth) {
262 // If there are no auto columns, or if the total is too wide, take
263 // what we have and scale it to fit as necessary.
264 if (totalWidth != tableLogicalWidth) {
265 // Fixed widths only scale up
266 if (totalFixedWidth && totalWidth < tableLogicalWidth) {
267 totalFixedWidth = 0;
268 for (int i = 0; i < nEffCols; i++) {
269 if (m_width[i].isFixed()) {
270 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth;
271 totalFixedWidth += calcWidth[i];
272 }
273 }
274 }
275 if (totalPercent) {
276 totalPercentWidth = 0;
277 for (int i = 0; i < nEffCols; i++) {
278 if (m_width[i].isPercent()) {
279 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent;
280 totalPercentWidth += calcWidth[i];
281 }
282 }
283 }
284 totalWidth = totalFixedWidth + totalPercentWidth;
285 }
286 } else {
287 // Divide the remaining width among the auto columns.
288 int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto);
289 int lastAuto = 0;
290 for (int i = 0; i < nEffCols; i++) {
291 if (m_width[i].isAuto()) {
292 int span = m_table->spanOfEffCol(i);
293 int w = remainingWidth * span / autoSpan;
294 calcWidth[i] = w + hspacing * (span - 1);
295 remainingWidth -= w;
296 if (!remainingWidth)
297 break;
298 lastAuto = i;
299 numAuto--;
300 autoSpan -= span;
301 }
302 }
303 // Last one gets the remainder.
304 if (remainingWidth)
305 calcWidth[lastAuto] += remainingWidth;
306 totalWidth = tableLogicalWidth;
307 }
308
309 if (totalWidth < tableLogicalWidth) {
310 // Spread extra space over columns.
311 int remainingWidth = tableLogicalWidth - totalWidth;
312 int total = nEffCols;
313 while (total) {
314 int w = remainingWidth / total;
315 remainingWidth -= w;
316 calcWidth[--total] += w;
317 }
318 if (nEffCols > 0)
319 calcWidth[nEffCols - 1] += remainingWidth;
320 }
321
322 int pos = 0;
323 for (int i = 0; i < nEffCols; i++) {
324 m_table->columnPositions()[i] = pos;
325 pos += calcWidth[i] + hspacing;
326 }
327 int colPositionsSize = m_table->columnPositions().size();
328 if (colPositionsSize > 0)
329 m_table->columnPositions()[colPositionsSize - 1] = pos;
330 }
331
332 } // namespace WebCore
333