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