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