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 30namespace WebCore { 31 32AutoTableLayout::AutoTableLayout(RenderTable* table) 33 : TableLayout(table) 34 , m_hasPercent(false) 35 , m_effectiveLogicalWidthDirty(true) 36{ 37} 38 39AutoTableLayout::~AutoTableLayout() 40{ 41} 42 43void AutoTableLayout::recalcColumn(unsigned effCol) 44{ 45 Layout& columnLayout = m_layoutStruct[effCol]; 46 47 RenderTableCell* fixedContributor = 0; 48 RenderTableCell* maxContributor = 0; 49 50 for (RenderObject* child = m_table->firstChild(); child; child = child->nextSibling()) { 51 if (child->isRenderTableCol()){ 52 // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits 53 // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's 54 // ancestors as dirty. 55 toRenderTableCol(child)->clearPreferredLogicalWidthsDirtyBits(); 56 } else if (child->isTableSection()) { 57 RenderTableSection* section = toRenderTableSection(child); 58 unsigned numRows = section->numRows(); 59 for (unsigned i = 0; i < numRows; i++) { 60 RenderTableSection::CellStruct current = section->cellAt(i, effCol); 61 RenderTableCell* cell = current.primaryCell(); 62 63 if (current.inColSpan || !cell) 64 continue; 65 66 bool cellHasContent = cell->firstChild() || cell->style().hasBorder() || cell->style().hasPadding() || cell->style().hasBackground(); 67 if (cellHasContent) 68 columnLayout.emptyCellsOnly = false; 69 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 = std::max<int>(columnLayout.minLogicalWidth, cellHasContent ? 1 : 0); 73 columnLayout.maxLogicalWidth = std::max<int>(columnLayout.maxLogicalWidth, 1); 74 75 if (cell->colSpan() == 1) { 76 columnLayout.minLogicalWidth = std::max<int>(cell->minPreferredLogicalWidth(), columnLayout.minLogicalWidth); 77 if (cell->maxPreferredLogicalWidth() > columnLayout.maxLogicalWidth) { 78 columnLayout.maxLogicalWidth = cell->maxPreferredLogicalWidth(); 79 maxContributor = cell; 80 } 81 82 // All browsers implement a size limit on the cell's max width. 83 // Our limit is based on KHTML's representation that used 16 bits widths. 84 // FIXME: Other browsers have a lower limit for the cell's max width. 85 const int cCellMaxWidth = 32760; 86 Length cellLogicalWidth = cell->styleOrColLogicalWidth(); 87 if (cellLogicalWidth.value() > cCellMaxWidth) 88 cellLogicalWidth.setValue(Fixed, cCellMaxWidth); 89 if (cellLogicalWidth.isNegative()) 90 cellLogicalWidth.setValue(Fixed, 0); 91 switch (cellLogicalWidth.type()) { 92 case Fixed: 93 // ignore width=0 94 if (cellLogicalWidth.isPositive() && !columnLayout.logicalWidth.isPercent()) { 95 int logicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(cellLogicalWidth.value()); 96 if (columnLayout.logicalWidth.isFixed()) { 97 // Nav/IE weirdness 98 if ((logicalWidth > columnLayout.logicalWidth.value()) 99 || ((columnLayout.logicalWidth.value() == logicalWidth) && (maxContributor == cell))) { 100 columnLayout.logicalWidth.setValue(Fixed, logicalWidth); 101 fixedContributor = cell; 102 } 103 } else { 104 columnLayout.logicalWidth.setValue(Fixed, logicalWidth); 105 fixedContributor = cell; 106 } 107 } 108 break; 109 case Percent: 110 m_hasPercent = true; 111 if (cellLogicalWidth.isPositive() && (!columnLayout.logicalWidth.isPercentNotCalculated() || cellLogicalWidth.percent() > columnLayout.logicalWidth.percent())) 112 columnLayout.logicalWidth = cellLogicalWidth; 113 break; 114 case Relative: 115 // FIXME: Need to understand this case and whether it makes sense to compare values 116 // which are not necessarily of the same type. 117 if (cellLogicalWidth.value() > columnLayout.logicalWidth.value()) 118 columnLayout.logicalWidth = cellLogicalWidth; 119 break; 120 default: 121 break; 122 } 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); 126 } 127 } 128 } 129 } 130 131 // Nav/IE weirdness 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; 136 } 137 } 138 139 columnLayout.maxLogicalWidth = std::max(columnLayout.maxLogicalWidth, columnLayout.minLogicalWidth); 140} 141 142void AutoTableLayout::fullRecalc() 143{ 144 m_hasPercent = false; 145 m_effectiveLogicalWidthDirty = true; 146 147 unsigned nEffCols = m_table->numEffCols(); 148 m_layoutStruct.resize(nEffCols); 149 m_layoutStruct.fill(Layout()); 150 m_spanCells.fill(0); 151 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(); 157 else { 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(); 169 } 170 currentColumn += span; 171 } 172 173 // For the last column in a column-group, we invalidate our group logical width. 174 if (column->isTableColumn() && !column->nextSibling()) 175 groupLogicalWidth = Length(); 176 } 177 178 for (unsigned i = 0; i < nEffCols; i++) 179 recalcColumn(i); 180} 181 182// FIXME: This needs to be adapted for vertical writing modes. 183static bool shouldScaleColumns(RenderTable* table) 184{ 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. 187 bool scale = true; 188 while (table) { 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(); 195 196 table = 0; 197 if (cb && cb->isTableCell() && 198 (cb->style().width().isAuto() || cb->style().width().isPercent())) { 199 RenderTableCell* cell = toRenderTableCell(cb); 200 if (cell->colSpan() > 1 || cell->table()->style().width().isAuto()) 201 scale = false; 202 else 203 table = cell->table(); 204 } 205 } 206 else 207 table = 0; 208 } 209 return scale; 210} 211 212void AutoTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) 213{ 214 fullRecalc(); 215 216 int spanMaxLogicalWidth = calcEffectiveLogicalWidth(); 217 minWidth = 0; 218 maxWidth = 0; 219 float maxPercent = 0; 220 float maxNonPercent = 0; 221 bool scaleColumns = shouldScaleColumns(m_table); 222 223 // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero. 224 // FIXME: Handle the 0% cases properly. 225 const float epsilon = 1 / 128.0f; 226 227 float remainingPercent = 100; 228 for (size_t i = 0; i < m_layoutStruct.size(); ++i) { 229 minWidth += m_layoutStruct[i].effectiveMinLogicalWidth; 230 maxWidth += m_layoutStruct[i].effectiveMaxLogicalWidth; 231 if (scaleColumns) { 232 if (m_layoutStruct[i].effectiveLogicalWidth.isPercentNotCalculated()) { 233 float percent = std::min(m_layoutStruct[i].effectiveLogicalWidth.percent(), remainingPercent); 234 float logicalWidth = static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) * 100 / std::max(percent, epsilon); 235 maxPercent = std::max(logicalWidth, maxPercent); 236 remainingPercent -= percent; 237 } else 238 maxNonPercent += m_layoutStruct[i].effectiveMaxLogicalWidth; 239 } 240 } 241 242 if (scaleColumns) { 243 maxNonPercent = maxNonPercent * 100 / std::max(remainingPercent, epsilon); 244 maxWidth = std::max<int>(maxWidth, static_cast<int>(std::min(maxNonPercent, static_cast<float>(tableMaxWidth)))); 245 maxWidth = std::max<int>(maxWidth, static_cast<int>(std::min(maxPercent, static_cast<float>(tableMaxWidth)))); 246 } 247 248 maxWidth = std::max<int>(maxWidth, spanMaxLogicalWidth); 249} 250 251void AutoTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const 252{ 253 Length tableLogicalWidth = m_table->style().logicalWidth(); 254 if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) 255 minWidth = maxWidth = std::max<int>(minWidth, tableLogicalWidth.value()); 256} 257 258/* 259 This method takes care of colspans. 260 effWidth is the same as width for cells without colspans. If we have colspans, they get modified. 261 */ 262int AutoTableLayout::calcEffectiveLogicalWidth() 263{ 264 int maxLogicalWidth = 0; 265 266 size_t nEffCols = m_layoutStruct.size(); 267 int spacingInRowDirection = m_table->hBorderSpacing(); 268 269 for (size_t i = 0; i < nEffCols; ++i) { 270 m_layoutStruct[i].effectiveLogicalWidth = m_layoutStruct[i].logicalWidth; 271 m_layoutStruct[i].effectiveMinLogicalWidth = m_layoutStruct[i].minLogicalWidth; 272 m_layoutStruct[i].effectiveMaxLogicalWidth = m_layoutStruct[i].maxLogicalWidth; 273 } 274 275 for (size_t i = 0; i < m_spanCells.size(); ++i) { 276 RenderTableCell* cell = m_spanCells[i]; 277 if (!cell) 278 break; 279 280 unsigned span = cell->colSpan(); 281 282 Length cellLogicalWidth = cell->styleOrColLogicalWidth(); 283 if (!cellLogicalWidth.isRelative() && cellLogicalWidth.isZero()) 284 cellLogicalWidth = Length(); // make it Auto 285 286 unsigned effCol = m_table->colToEffCol(cell->col()); 287 size_t lastCol = effCol; 288 int cellMinLogicalWidth = cell->minPreferredLogicalWidth() + spacingInRowDirection; 289 int cellMaxLogicalWidth = cell->maxPreferredLogicalWidth() + spacingInRowDirection; 290 float totalPercent = 0; 291 int spanMinLogicalWidth = 0; 292 int spanMaxLogicalWidth = 0; 293 bool allColsArePercent = true; 294 bool allColsAreFixed = true; 295 bool haveAuto = false; 296 bool spanHasEmptyCellsOnly = true; 297 int fixedWidth = 0; 298 while (lastCol < nEffCols && span > 0) { 299 Layout& columnLayout = m_layoutStruct[lastCol]; 300 switch (columnLayout.logicalWidth.type()) { 301 case Percent: 302 totalPercent += columnLayout.logicalWidth.percent(); 303 allColsAreFixed = false; 304 break; 305 case Fixed: 306 if (columnLayout.logicalWidth.value() > 0) { 307 fixedWidth += columnLayout.logicalWidth.value(); 308 allColsArePercent = false; 309 // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad 310 // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither. 311 break; 312 } 313 FALLTHROUGH; 314 case Auto: 315 haveAuto = true; 316 FALLTHROUGH; 317 default: 318 // If the column is a percentage width, do not let the spanning cell overwrite the 319 // width value. This caused a mis-rendering on amazon.com. 320 // Sample snippet: 321 // <table border=2 width=100%>< 322 // <tr><td>1</td><td colspan=2>2-3</tr> 323 // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr> 324 // </table> 325 if (!columnLayout.effectiveLogicalWidth.isPercentNotCalculated()) { 326 columnLayout.effectiveLogicalWidth = Length(); 327 allColsArePercent = false; 328 } else 329 totalPercent += columnLayout.effectiveLogicalWidth.percent(); 330 allColsAreFixed = false; 331 } 332 if (!columnLayout.emptyCellsOnly) 333 spanHasEmptyCellsOnly = false; 334 span -= m_table->spanOfEffCol(lastCol); 335 spanMinLogicalWidth += columnLayout.effectiveMinLogicalWidth; 336 spanMaxLogicalWidth += columnLayout.effectiveMaxLogicalWidth; 337 lastCol++; 338 cellMinLogicalWidth -= spacingInRowDirection; 339 cellMaxLogicalWidth -= spacingInRowDirection; 340 } 341 342 // adjust table max width if needed 343 if (cellLogicalWidth.isPercentNotCalculated()) { 344 if (totalPercent > cellLogicalWidth.percent() || allColsArePercent) { 345 // can't satify this condition, treat as variable 346 cellLogicalWidth = Length(); 347 } else { 348 maxLogicalWidth = std::max(maxLogicalWidth, static_cast<int>(std::max(spanMaxLogicalWidth, cellMaxLogicalWidth) * 100 / cellLogicalWidth.percent())); 349 350 // all non percent columns in the span get percent values to sum up correctly. 351 float percentMissing = cellLogicalWidth.percent() - totalPercent; 352 int totalWidth = 0; 353 for (unsigned pos = effCol; pos < lastCol; ++pos) { 354 if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) 355 totalWidth += m_layoutStruct[pos].effectiveMaxLogicalWidth; 356 } 357 358 for (unsigned pos = effCol; pos < lastCol && totalWidth > 0; ++pos) { 359 if (!m_layoutStruct[pos].effectiveLogicalWidth.isPercent()) { 360 float percent = percentMissing * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / totalWidth; 361 totalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; 362 percentMissing -= percent; 363 if (percent > 0) 364 m_layoutStruct[pos].effectiveLogicalWidth.setValue(Percent, percent); 365 else 366 m_layoutStruct[pos].effectiveLogicalWidth = Length(); 367 } 368 } 369 } 370 } 371 372 // make sure minWidth and maxWidth of the spanning cell are honoured 373 if (cellMinLogicalWidth > spanMinLogicalWidth) { 374 if (allColsAreFixed) { 375 for (unsigned pos = effCol; fixedWidth > 0 && pos < lastCol; ++pos) { 376 int cellLogicalWidth = std::max(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(cellMinLogicalWidth * m_layoutStruct[pos].logicalWidth.value() / fixedWidth)); 377 fixedWidth -= m_layoutStruct[pos].logicalWidth.value(); 378 cellMinLogicalWidth -= cellLogicalWidth; 379 m_layoutStruct[pos].effectiveMinLogicalWidth = cellLogicalWidth; 380 } 381 } else if (allColsArePercent) { 382 // In this case, we just split the colspan's min amd max widths following the percentage. 383 int allocatedMinLogicalWidth = 0; 384 int allocatedMaxLogicalWidth = 0; 385 for (unsigned pos = effCol; pos < lastCol; ++pos) { 386 ASSERT(m_layoutStruct[pos].logicalWidth.isPercentNotCalculated() || m_layoutStruct[pos].effectiveLogicalWidth.isPercentNotCalculated()); 387 // |allColsArePercent| means that either the logicalWidth *or* the effectiveLogicalWidth are percents, handle both of them here. 388 float percent = m_layoutStruct[pos].logicalWidth.isPercentNotCalculated() ? m_layoutStruct[pos].logicalWidth.percent() : m_layoutStruct[pos].effectiveLogicalWidth.percent(); 389 int columnMinLogicalWidth = static_cast<int>(percent * cellMinLogicalWidth / totalPercent); 390 int columnMaxLogicalWidth = static_cast<int>(percent * cellMaxLogicalWidth / totalPercent); 391 m_layoutStruct[pos].effectiveMinLogicalWidth = std::max(m_layoutStruct[pos].effectiveMinLogicalWidth, columnMinLogicalWidth); 392 m_layoutStruct[pos].effectiveMaxLogicalWidth = columnMaxLogicalWidth; 393 allocatedMinLogicalWidth += columnMinLogicalWidth; 394 allocatedMaxLogicalWidth += columnMaxLogicalWidth; 395 } 396 ASSERT(allocatedMinLogicalWidth <= cellMinLogicalWidth); 397 ASSERT(allocatedMaxLogicalWidth <= cellMaxLogicalWidth); 398 cellMinLogicalWidth -= allocatedMinLogicalWidth; 399 cellMaxLogicalWidth -= allocatedMaxLogicalWidth; 400 } else { 401 int remainingMaxLogicalWidth = spanMaxLogicalWidth; 402 int remainingMinLogicalWidth = spanMinLogicalWidth; 403 404 // Give min to variable first, to fixed second, and to others third. 405 for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol; ++pos) { 406 if (m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth) { 407 int colMinLogicalWidth = std::max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, m_layoutStruct[pos].logicalWidth.value()); 408 fixedWidth -= m_layoutStruct[pos].logicalWidth.value(); 409 remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth; 410 remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; 411 cellMinLogicalWidth -= colMinLogicalWidth; 412 m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth; 413 } 414 } 415 416 for (unsigned pos = effCol; remainingMaxLogicalWidth >= 0 && pos < lastCol && remainingMinLogicalWidth < cellMinLogicalWidth; ++pos) { 417 if (!(m_layoutStruct[pos].logicalWidth.isFixed() && haveAuto && fixedWidth <= cellMinLogicalWidth)) { 418 int colMinLogicalWidth = std::max<int>(m_layoutStruct[pos].effectiveMinLogicalWidth, static_cast<int>(remainingMaxLogicalWidth ? cellMinLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / remainingMaxLogicalWidth : cellMinLogicalWidth)); 419 colMinLogicalWidth = std::min<int>(m_layoutStruct[pos].effectiveMinLogicalWidth + (cellMinLogicalWidth - remainingMinLogicalWidth), colMinLogicalWidth); 420 remainingMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; 421 remainingMinLogicalWidth -= m_layoutStruct[pos].effectiveMinLogicalWidth; 422 cellMinLogicalWidth -= colMinLogicalWidth; 423 m_layoutStruct[pos].effectiveMinLogicalWidth = colMinLogicalWidth; 424 } 425 } 426 } 427 } 428 if (!cellLogicalWidth.isPercent()) { 429 if (cellMaxLogicalWidth > spanMaxLogicalWidth) { 430 for (unsigned pos = effCol; spanMaxLogicalWidth >= 0 && pos < lastCol; ++pos) { 431 int colMaxLogicalWidth = std::max(m_layoutStruct[pos].effectiveMaxLogicalWidth, static_cast<int>(spanMaxLogicalWidth ? cellMaxLogicalWidth * static_cast<float>(m_layoutStruct[pos].effectiveMaxLogicalWidth) / spanMaxLogicalWidth : cellMaxLogicalWidth)); 432 spanMaxLogicalWidth -= m_layoutStruct[pos].effectiveMaxLogicalWidth; 433 cellMaxLogicalWidth -= colMaxLogicalWidth; 434 m_layoutStruct[pos].effectiveMaxLogicalWidth = colMaxLogicalWidth; 435 } 436 } 437 } else { 438 for (unsigned pos = effCol; pos < lastCol; ++pos) 439 m_layoutStruct[pos].maxLogicalWidth = std::max(m_layoutStruct[pos].maxLogicalWidth, m_layoutStruct[pos].minLogicalWidth); 440 } 441 // treat span ranges consisting of empty cells only as if they had content 442 if (spanHasEmptyCellsOnly) { 443 for (unsigned pos = effCol; pos < lastCol; ++pos) 444 m_layoutStruct[pos].emptyCellsOnly = false; 445 } 446 } 447 m_effectiveLogicalWidthDirty = false; 448 449 return std::min(maxLogicalWidth, INT_MAX / 2); 450} 451 452/* gets all cells that originate in a column and have a cellspan > 1 453 Sorts them by increasing cellspan 454*/ 455void AutoTableLayout::insertSpanCell(RenderTableCell *cell) 456{ 457 ASSERT_ARG(cell, cell && cell->colSpan() != 1); 458 if (!cell || cell->colSpan() == 1) 459 return; 460 461 unsigned size = m_spanCells.size(); 462 if (!size || m_spanCells[size-1] != 0) { 463 m_spanCells.grow(size + 10); 464 for (unsigned i = 0; i < 10; i++) 465 m_spanCells[size+i] = 0; 466 size += 10; 467 } 468 469 // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better 470 unsigned pos = 0; 471 unsigned span = cell->colSpan(); 472 while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan()) 473 pos++; 474 memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *)); 475 m_spanCells[pos] = cell; 476} 477 478 479void AutoTableLayout::layout() 480{ 481 // table layout based on the values collected in the layout structure. 482 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); 483 int available = tableLogicalWidth; 484 size_t nEffCols = m_table->numEffCols(); 485 486 // FIXME: It is possible to be called without having properly updated our internal representation. 487 // This means that our preferred logical widths were not recomputed as expected. 488 if (nEffCols != m_layoutStruct.size()) { 489 fullRecalc(); 490 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups). 491 nEffCols = m_table->numEffCols(); 492 } 493 494 if (m_effectiveLogicalWidthDirty) 495 calcEffectiveLogicalWidth(); 496 497 bool havePercent = false; 498 int totalRelative = 0; 499 int numAuto = 0; 500 int numFixed = 0; 501 float totalAuto = 0; 502 float totalFixed = 0; 503 float totalPercent = 0; 504 int allocAuto = 0; 505 unsigned numAutoEmptyCellsOnly = 0; 506 507 // fill up every cell with its minWidth 508 for (size_t i = 0; i < nEffCols; ++i) { 509 int cellLogicalWidth = m_layoutStruct[i].effectiveMinLogicalWidth; 510 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; 511 available -= cellLogicalWidth; 512 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 513 switch (logicalWidth.type()) { 514 case Percent: 515 havePercent = true; 516 totalPercent += logicalWidth.percent(); 517 break; 518 case Relative: 519 totalRelative += logicalWidth.value(); 520 break; 521 case Fixed: 522 numFixed++; 523 totalFixed += m_layoutStruct[i].effectiveMaxLogicalWidth; 524 break; 525 case Auto: 526 if (m_layoutStruct[i].emptyCellsOnly) 527 numAutoEmptyCellsOnly++; 528 else { 529 numAuto++; 530 totalAuto += m_layoutStruct[i].effectiveMaxLogicalWidth; 531 allocAuto += cellLogicalWidth; 532 } 533 break; 534 default: 535 break; 536 } 537 } 538 539 // allocate width to percent cols 540 if (available > 0 && havePercent) { 541 for (size_t i = 0; i < nEffCols; ++i) { 542 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 543 if (logicalWidth.isPercent()) { 544 int cellLogicalWidth = std::max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, minimumValueForLength(logicalWidth, tableLogicalWidth)); 545 available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth; 546 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; 547 } 548 } 549 if (totalPercent > 100) { 550 // remove overallocated space from the last columns 551 int excess = tableLogicalWidth * (totalPercent - 100) / 100; 552 for (unsigned i = nEffCols; i; ) { 553 --i; 554 if (m_layoutStruct[i].effectiveLogicalWidth.isPercent()) { 555 int cellLogicalWidth = m_layoutStruct[i].computedLogicalWidth; 556 int reduction = std::min(cellLogicalWidth, excess); 557 // the lines below might look inconsistent, but that's the way it's handled in mozilla 558 excess -= reduction; 559 int newLogicalWidth = std::max<int>(m_layoutStruct[i].effectiveMinLogicalWidth, cellLogicalWidth - reduction); 560 available += cellLogicalWidth - newLogicalWidth; 561 m_layoutStruct[i].computedLogicalWidth = newLogicalWidth; 562 } 563 } 564 } 565 } 566 567 // then allocate width to fixed cols 568 if (available > 0) { 569 for (size_t i = 0; i < nEffCols; ++i) { 570 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 571 if (logicalWidth.isFixed() && logicalWidth.value() > m_layoutStruct[i].computedLogicalWidth) { 572 available += m_layoutStruct[i].computedLogicalWidth - logicalWidth.value(); 573 m_layoutStruct[i].computedLogicalWidth = logicalWidth.value(); 574 } 575 } 576 } 577 578 // now satisfy relative 579 if (available > 0) { 580 for (size_t i = 0; i < nEffCols; ++i) { 581 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 582 if (logicalWidth.isRelative() && logicalWidth.value() != 0) { 583 // width=0* gets effMinWidth. 584 int cellLogicalWidth = logicalWidth.value() * tableLogicalWidth / totalRelative; 585 available += m_layoutStruct[i].computedLogicalWidth - cellLogicalWidth; 586 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; 587 } 588 } 589 } 590 591 // now satisfy variable 592 if (available > 0 && numAuto) { 593 available += allocAuto; // this gets redistributed 594 for (size_t i = 0; i < nEffCols; ++i) { 595 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 596 if (logicalWidth.isAuto() && totalAuto && !m_layoutStruct[i].emptyCellsOnly) { 597 int cellLogicalWidth = std::max<int>(m_layoutStruct[i].computedLogicalWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalAuto)); 598 available -= cellLogicalWidth; 599 totalAuto -= m_layoutStruct[i].effectiveMaxLogicalWidth; 600 m_layoutStruct[i].computedLogicalWidth = cellLogicalWidth; 601 } 602 } 603 } 604 605 // spread over fixed columns 606 if (available > 0 && numFixed) { 607 for (size_t i = 0; i < nEffCols; ++i) { 608 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 609 if (logicalWidth.isFixed()) { 610 int cellLogicalWidth = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effectiveMaxLogicalWidth) / totalFixed); 611 available -= cellLogicalWidth; 612 totalFixed -= m_layoutStruct[i].effectiveMaxLogicalWidth; 613 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; 614 } 615 } 616 } 617 618 // spread over percent colums 619 if (available > 0 && m_hasPercent && totalPercent < 100) { 620 for (size_t i = 0; i < nEffCols; ++i) { 621 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 622 if (logicalWidth.isPercentNotCalculated()) { 623 int cellLogicalWidth = available * logicalWidth.percent() / totalPercent; 624 available -= cellLogicalWidth; 625 totalPercent -= logicalWidth.percent(); 626 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; 627 if (!available || !totalPercent) 628 break; 629 } 630 } 631 } 632 633 // spread over the rest 634 if (available > 0 && nEffCols > numAutoEmptyCellsOnly) { 635 unsigned total = nEffCols - numAutoEmptyCellsOnly; 636 // still have some width to spread 637 for (unsigned i = nEffCols; i; ) { 638 --i; 639 // variable columns with empty cells only don't get any width 640 if (m_layoutStruct[i].effectiveLogicalWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly) 641 continue; 642 int cellLogicalWidth = available / total; 643 available -= cellLogicalWidth; 644 total--; 645 m_layoutStruct[i].computedLogicalWidth += cellLogicalWidth; 646 } 647 } 648 649 // If we have overallocated, reduce every cell according to the difference between desired width and minwidth 650 // this seems to produce to the pixel exact results with IE. Wonder is some of this also holds for width distributing. 651 if (available < 0) { 652 // Need to reduce cells with the following prioritization: 653 // (1) Auto 654 // (2) Relative 655 // (3) Fixed 656 // (4) Percent 657 // This is basically the reverse of how we grew the cells. 658 if (available < 0) { 659 int logicalWidthBeyondMin = 0; 660 for (unsigned i = nEffCols; i; ) { 661 --i; 662 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 663 if (logicalWidth.isAuto()) 664 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 665 } 666 667 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) { 668 --i; 669 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 670 if (logicalWidth.isAuto()) { 671 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 672 int reduce = available * minMaxDiff / logicalWidthBeyondMin; 673 m_layoutStruct[i].computedLogicalWidth += reduce; 674 available -= reduce; 675 logicalWidthBeyondMin -= minMaxDiff; 676 if (available >= 0) 677 break; 678 } 679 } 680 } 681 682 if (available < 0) { 683 int logicalWidthBeyondMin = 0; 684 for (unsigned i = nEffCols; i; ) { 685 --i; 686 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 687 if (logicalWidth.isRelative()) 688 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 689 } 690 691 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) { 692 --i; 693 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 694 if (logicalWidth.isRelative()) { 695 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 696 int reduce = available * minMaxDiff / logicalWidthBeyondMin; 697 m_layoutStruct[i].computedLogicalWidth += reduce; 698 available -= reduce; 699 logicalWidthBeyondMin -= minMaxDiff; 700 if (available >= 0) 701 break; 702 } 703 } 704 } 705 706 if (available < 0) { 707 int logicalWidthBeyondMin = 0; 708 for (unsigned i = nEffCols; i; ) { 709 --i; 710 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 711 if (logicalWidth.isFixed()) 712 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 713 } 714 715 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) { 716 --i; 717 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 718 if (logicalWidth.isFixed()) { 719 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 720 int reduce = available * minMaxDiff / logicalWidthBeyondMin; 721 m_layoutStruct[i].computedLogicalWidth += reduce; 722 available -= reduce; 723 logicalWidthBeyondMin -= minMaxDiff; 724 if (available >= 0) 725 break; 726 } 727 } 728 } 729 730 if (available < 0) { 731 int logicalWidthBeyondMin = 0; 732 for (unsigned i = nEffCols; i; ) { 733 --i; 734 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 735 if (logicalWidth.isPercent()) 736 logicalWidthBeyondMin += m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 737 } 738 739 for (unsigned i = nEffCols; i && logicalWidthBeyondMin > 0; ) { 740 --i; 741 Length& logicalWidth = m_layoutStruct[i].effectiveLogicalWidth; 742 if (logicalWidth.isPercent()) { 743 int minMaxDiff = m_layoutStruct[i].computedLogicalWidth - m_layoutStruct[i].effectiveMinLogicalWidth; 744 int reduce = available * minMaxDiff / logicalWidthBeyondMin; 745 m_layoutStruct[i].computedLogicalWidth += reduce; 746 available -= reduce; 747 logicalWidthBeyondMin -= minMaxDiff; 748 if (available >= 0) 749 break; 750 } 751 } 752 } 753 } 754 755 int pos = 0; 756 for (size_t i = 0; i < nEffCols; ++i) { 757 m_table->setColumnPosition(i, pos); 758 pos += m_layoutStruct[i].computedLogicalWidth + m_table->hBorderSpacing(); 759 } 760 m_table->setColumnPosition(m_table->columnPositions().size() - 1, pos); 761} 762 763} 764