1 /* 2 * Copyright (C) 2002 Lars Knoll (knoll (at) kde.org) 3 * (C) 2002 Dirk Mueller (mueller (at) kde.org) 4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013 Apple Inc. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22 #include "config.h" 23 #include "core/rendering/FixedTableLayout.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 "platform/LayoutUnit.h" 30 31 /* 32 The text below is from the CSS 2.1 specs. 33 34 Fixed table layout 35 36 With this (fast) algorithm, the horizontal layout of the table does 37 not depend on the contents of the cells; it only depends on the 38 table's width, the width of the columns, and borders or cell 39 spacing. 40 41 The table's width may be specified explicitly with the 'width' 42 property. A value of 'auto' (for both 'display: table' and 'display: 43 inline-table') means use the automatic table layout algorithm. 44 45 In the fixed table layout algorithm, the width of each column is 46 determined as follows: 47 48 1. A column element with a value other than 'auto' for the 'width' 49 property sets the width for that column. 50 51 2. Otherwise, a cell in the first row with a value other than 52 'auto' for the 'width' property sets the width for that column. If 53 the cell spans more than one column, the width is divided over the 54 columns. 55 56 3. Any remaining columns equally divide the remaining horizontal 57 table space (minus borders or cell spacing). 58 59 The width of the table is then the greater of the value of the 60 'width' property for the table element and the sum of the column 61 widths (plus cell spacing or borders). If the table is wider than 62 the columns, the extra space should be distributed over the columns. 63 64 65 In this manner, the user agent can begin to lay out the table once 66 the entire first row has been received. Cells in subsequent rows do 67 not affect column widths. Any cell that has content that overflows 68 uses the 'overflow' property to determine whether to clip the 69 overflow content. 70 */ 71 72 namespace blink { 73 74 FixedTableLayout::FixedTableLayout(RenderTable* table) 75 : TableLayout(table) 76 { 77 } 78 79 int FixedTableLayout::calcWidthArray() 80 { 81 // FIXME: We might want to wait until we have all of the first row before computing for the first time. 82 int usedWidth = 0; 83 84 // iterate over all <col> elements 85 unsigned nEffCols = m_table->numEffCols(); 86 m_width.resize(nEffCols); 87 m_width.fill(Length(Auto)); 88 89 unsigned currentEffectiveColumn = 0; 90 for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) { 91 // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits 92 // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's 93 // ancestors as dirty. 94 col->clearPreferredLogicalWidthsDirtyBits(); 95 96 // Width specified by column-groups that have column child does not affect column width in fixed layout tables 97 if (col->isTableColumnGroupWithColumnChildren()) 98 continue; 99 100 Length colStyleLogicalWidth = col->style()->logicalWidth(); 101 int effectiveColWidth = 0; 102 if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0) 103 effectiveColWidth = colStyleLogicalWidth.value(); 104 105 unsigned span = col->span(); 106 while (span) { 107 unsigned spanInCurrentEffectiveColumn; 108 if (currentEffectiveColumn >= nEffCols) { 109 m_table->appendColumn(span); 110 nEffCols++; 111 m_width.append(Length()); 112 spanInCurrentEffectiveColumn = span; 113 } else { 114 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) { 115 m_table->splitColumn(currentEffectiveColumn, span); 116 nEffCols++; 117 m_width.append(Length()); 118 } 119 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn); 120 } 121 if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.isPercent()) && colStyleLogicalWidth.isPositive()) { 122 m_width[currentEffectiveColumn] = colStyleLogicalWidth; 123 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn; 124 usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn; 125 } 126 span -= spanInCurrentEffectiveColumn; 127 currentEffectiveColumn++; 128 } 129 } 130 131 // Iterate over the first row in case some are unspecified. 132 RenderTableSection* section = m_table->topNonEmptySection(); 133 if (!section) 134 return usedWidth; 135 136 unsigned currentColumn = 0; 137 138 RenderTableRow* firstRow = section->firstRow(); 139 for (RenderTableCell* cell = firstRow->firstCell(); cell; cell = cell->nextCell()) { 140 Length logicalWidth = cell->styleOrColLogicalWidth(); 141 142 // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725 143 if (logicalWidth.isCalculated()) 144 logicalWidth = Length(); // Make it Auto 145 146 unsigned span = cell->colSpan(); 147 int fixedBorderBoxLogicalWidth = 0; 148 // FIXME: Support other length types. If the width is non-auto, it should probably just use 149 // RenderBox::computeLogicalWidthUsing to compute the width. 150 if (logicalWidth.isFixed() && logicalWidth.isPositive()) { 151 fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth.value()); 152 logicalWidth.setValue(fixedBorderBoxLogicalWidth); 153 } 154 155 unsigned usedSpan = 0; 156 while (usedSpan < span && currentColumn < nEffCols) { 157 float eSpan = m_table->spanOfEffCol(currentColumn); 158 // Only set if no col element has already set it. 159 if (m_width[currentColumn].isAuto() && logicalWidth.type() != Auto) { 160 m_width[currentColumn] = logicalWidth; 161 m_width[currentColumn] *= eSpan / span; 162 usedWidth += fixedBorderBoxLogicalWidth * eSpan / span; 163 } 164 usedSpan += eSpan; 165 ++currentColumn; 166 } 167 168 // FixedTableLayout doesn't use min/maxPreferredLogicalWidths, but we need to clear the 169 // dirty bit on the cell so that we'll correctly mark its ancestors dirty 170 // in case we later call setPreferredLogicalWidthsDirty() on it later. 171 if (cell->preferredLogicalWidthsDirty()) 172 cell->clearPreferredLogicalWidthsDirty(); 173 } 174 175 return usedWidth; 176 } 177 178 void FixedTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) 179 { 180 minWidth = maxWidth = calcWidthArray(); 181 } 182 183 void FixedTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const 184 { 185 Length tableLogicalWidth = m_table->style()->logicalWidth(); 186 if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) 187 minWidth = maxWidth = max<int>(minWidth, tableLogicalWidth.value() - m_table->bordersPaddingAndSpacingInRowDirection()); 188 189 /* 190 <table style="width:100%; background-color:red"><tr><td> 191 <table style="background-color:blue"><tr><td> 192 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td> 193 Content 194 </td></tr></table> 195 </td></tr></table> 196 </td></tr></table> 197 */ 198 // In this example, the two inner tables should be as large as the outer table. 199 // We can achieve this effect by making the maxwidth of fixed tables with percentage 200 // widths be infinite. 201 if (m_table->style()->logicalWidth().isPercent() && maxWidth < tableMaxWidth) 202 maxWidth = tableMaxWidth; 203 } 204 205 void FixedTableLayout::layout() 206 { 207 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); 208 unsigned nEffCols = m_table->numEffCols(); 209 210 // FIXME: It is possible to be called without having properly updated our internal representation. 211 // This means that our preferred logical widths were not recomputed as expected. 212 if (nEffCols != m_width.size()) { 213 calcWidthArray(); 214 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups). 215 nEffCols = m_table->numEffCols(); 216 } 217 218 Vector<int> calcWidth(nEffCols, 0); 219 220 unsigned numAuto = 0; 221 unsigned autoSpan = 0; 222 int totalFixedWidth = 0; 223 int totalPercentWidth = 0; 224 float totalPercent = 0; 225 226 // Compute requirements and try to satisfy fixed and percent widths. 227 // Percentages are of the table's width, so for example 228 // for a table width of 100px with columns (40px, 10%), the 10% compute 229 // to 10px here, and will scale up to 20px in the final (80px, 20px). 230 for (unsigned i = 0; i < nEffCols; i++) { 231 if (m_width[i].isFixed()) { 232 calcWidth[i] = m_width[i].value(); 233 totalFixedWidth += calcWidth[i]; 234 } else if (m_width[i].isPercent()) { 235 calcWidth[i] = valueForLength(m_width[i], tableLogicalWidth); 236 totalPercentWidth += calcWidth[i]; 237 totalPercent += m_width[i].percent(); 238 } else if (m_width[i].isAuto()) { 239 numAuto++; 240 autoSpan += m_table->spanOfEffCol(i); 241 } 242 } 243 244 int hspacing = m_table->hBorderSpacing(); 245 int totalWidth = totalFixedWidth + totalPercentWidth; 246 if (!numAuto || totalWidth > tableLogicalWidth) { 247 // If there are no auto columns, or if the total is too wide, take 248 // what we have and scale it to fit as necessary. 249 if (totalWidth != tableLogicalWidth) { 250 // Fixed widths only scale up 251 if (totalFixedWidth && totalWidth < tableLogicalWidth) { 252 totalFixedWidth = 0; 253 for (unsigned i = 0; i < nEffCols; i++) { 254 if (m_width[i].isFixed()) { 255 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth; 256 totalFixedWidth += calcWidth[i]; 257 } 258 } 259 } 260 if (totalPercent) { 261 totalPercentWidth = 0; 262 for (unsigned i = 0; i < nEffCols; i++) { 263 if (m_width[i].isPercent()) { 264 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent; 265 totalPercentWidth += calcWidth[i]; 266 } 267 } 268 } 269 totalWidth = totalFixedWidth + totalPercentWidth; 270 } 271 } else { 272 // Divide the remaining width among the auto columns. 273 ASSERT(autoSpan >= numAuto); 274 int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); 275 int lastAuto = 0; 276 for (unsigned i = 0; i < nEffCols; i++) { 277 if (m_width[i].isAuto()) { 278 unsigned span = m_table->spanOfEffCol(i); 279 int w = remainingWidth * span / autoSpan; 280 calcWidth[i] = w + hspacing * (span - 1); 281 remainingWidth -= w; 282 if (!remainingWidth) 283 break; 284 lastAuto = i; 285 numAuto--; 286 ASSERT(autoSpan >= span); 287 autoSpan -= span; 288 } 289 } 290 // Last one gets the remainder. 291 if (remainingWidth) 292 calcWidth[lastAuto] += remainingWidth; 293 totalWidth = tableLogicalWidth; 294 } 295 296 if (totalWidth < tableLogicalWidth) { 297 // Spread extra space over columns. 298 int remainingWidth = tableLogicalWidth - totalWidth; 299 int total = nEffCols; 300 while (total) { 301 int w = remainingWidth / total; 302 remainingWidth -= w; 303 calcWidth[--total] += w; 304 } 305 if (nEffCols > 0) 306 calcWidth[nEffCols - 1] += remainingWidth; 307 } 308 309 int pos = 0; 310 for (unsigned i = 0; i < nEffCols; i++) { 311 m_table->setColumnPosition(i, pos); 312 pos += calcWidth[i] + hspacing; 313 } 314 int colPositionsSize = m_table->columnPositions().size(); 315 if (colPositionsSize > 0) 316 m_table->setColumnPosition(colPositionsSize - 1, pos); 317 } 318 319 void FixedTableLayout::willChangeTableLayout() 320 { 321 // When switching table layout algorithm, we need to dirty the preferred 322 // logical widths as we cleared the bits without computing them. 323 // (see calcWidthArray above.) This optimization is preferred to always 324 // computing the logical widths we never intended to use. 325 m_table->recalcSectionsIfNeeded(); 326 for (RenderTableSection* section = m_table->topNonEmptySection(); section; section = m_table->sectionBelow(section)) { 327 for (unsigned i = 0; i < section->numRows(); i++) { 328 RenderTableRow* row = section->rowRendererAt(i); 329 if (!row) 330 continue; 331 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) 332 cell->setPreferredLogicalWidthsDirty(); 333 } 334 } 335 } 336 337 } // namespace blink 338