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