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 using namespace std; 73 74 namespace WebCore { 75 76 FixedTableLayout::FixedTableLayout(RenderTable* table) 77 : TableLayout(table) 78 { 79 } 80 81 int FixedTableLayout::calcWidthArray() 82 { 83 // FIXME: We might want to wait until we have all of the first row before computing for the first time. 84 int usedWidth = 0; 85 86 // iterate over all <col> elements 87 unsigned nEffCols = m_table->numEffCols(); 88 m_width.resize(nEffCols); 89 m_width.fill(Length(Auto)); 90 91 unsigned currentEffectiveColumn = 0; 92 for (RenderTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) { 93 // RenderTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits 94 // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's 95 // ancestors as dirty. 96 col->clearPreferredLogicalWidthsDirtyBits(); 97 98 // Width specified by column-groups that have column child does not affect column width in fixed layout tables 99 if (col->isTableColumnGroupWithColumnChildren()) 100 continue; 101 102 Length colStyleLogicalWidth = col->style()->logicalWidth(); 103 int effectiveColWidth = 0; 104 if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0) 105 effectiveColWidth = colStyleLogicalWidth.value(); 106 107 unsigned span = col->span(); 108 while (span) { 109 unsigned spanInCurrentEffectiveColumn; 110 if (currentEffectiveColumn >= nEffCols) { 111 m_table->appendColumn(span); 112 nEffCols++; 113 m_width.append(Length()); 114 spanInCurrentEffectiveColumn = span; 115 } else { 116 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) { 117 m_table->splitColumn(currentEffectiveColumn, span); 118 nEffCols++; 119 m_width.append(Length()); 120 } 121 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn); 122 } 123 if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.isPercent()) && colStyleLogicalWidth.isPositive()) { 124 m_width[currentEffectiveColumn] = colStyleLogicalWidth; 125 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn; 126 usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn; 127 } 128 span -= spanInCurrentEffectiveColumn; 129 currentEffectiveColumn++; 130 } 131 } 132 133 // Iterate over the first row in case some are unspecified. 134 RenderTableSection* section = m_table->topNonEmptySection(); 135 if (!section) 136 return usedWidth; 137 138 unsigned currentColumn = 0; 139 140 RenderTableRow* firstRow = section->firstRow(); 141 for (RenderTableCell* cell = firstRow->firstCell(); cell; cell = cell->nextCell()) { 142 Length logicalWidth = cell->styleOrColLogicalWidth(); 143 unsigned span = cell->colSpan(); 144 int fixedBorderBoxLogicalWidth = 0; 145 // FIXME: Support other length types. If the width is non-auto, it should probably just use 146 // RenderBox::computeLogicalWidthUsing to compute the width. 147 if (logicalWidth.isFixed() && logicalWidth.isPositive()) { 148 fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth.value()); 149 logicalWidth.setValue(fixedBorderBoxLogicalWidth); 150 } 151 152 unsigned usedSpan = 0; 153 while (usedSpan < span && currentColumn < nEffCols) { 154 float eSpan = m_table->spanOfEffCol(currentColumn); 155 // Only set if no col element has already set it. 156 if (m_width[currentColumn].isAuto() && logicalWidth.type() != Auto) { 157 m_width[currentColumn] = logicalWidth; 158 m_width[currentColumn] *= eSpan / span; 159 usedWidth += fixedBorderBoxLogicalWidth * eSpan / span; 160 } 161 usedSpan += eSpan; 162 ++currentColumn; 163 } 164 165 // FixedTableLayout doesn't use min/maxPreferredLogicalWidths, but we need to clear the 166 // dirty bit on the cell so that we'll correctly mark its ancestors dirty 167 // in case we later call setPreferredLogicalWidthsDirty() on it later. 168 if (cell->preferredLogicalWidthsDirty()) 169 cell->clearPreferredLogicalWidthsDirty(); 170 } 171 172 return usedWidth; 173 } 174 175 void FixedTableLayout::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) 176 { 177 minWidth = maxWidth = calcWidthArray(); 178 } 179 180 void FixedTableLayout::applyPreferredLogicalWidthQuirks(LayoutUnit& minWidth, LayoutUnit& maxWidth) const 181 { 182 Length tableLogicalWidth = m_table->style()->logicalWidth(); 183 if (tableLogicalWidth.isFixed() && tableLogicalWidth.isPositive()) 184 minWidth = maxWidth = max<int>(minWidth, tableLogicalWidth.value() - m_table->bordersPaddingAndSpacingInRowDirection()); 185 186 /* 187 <table style="width:100%; background-color:red"><tr><td> 188 <table style="background-color:blue"><tr><td> 189 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td> 190 Content 191 </td></tr></table> 192 </td></tr></table> 193 </td></tr></table> 194 */ 195 // In this example, the two inner tables should be as large as the outer table. 196 // We can achieve this effect by making the maxwidth of fixed tables with percentage 197 // widths be infinite. 198 if (m_table->style()->logicalWidth().isPercent() && maxWidth < tableMaxWidth) 199 maxWidth = tableMaxWidth; 200 } 201 202 void FixedTableLayout::layout() 203 { 204 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); 205 unsigned nEffCols = m_table->numEffCols(); 206 207 // FIXME: It is possible to be called without having properly updated our internal representation. 208 // This means that our preferred logical widths were not recomputed as expected. 209 if (nEffCols != m_width.size()) { 210 calcWidthArray(); 211 // FIXME: Table layout shouldn't modify our table structure (but does due to columns and column-groups). 212 nEffCols = m_table->numEffCols(); 213 } 214 215 Vector<int> calcWidth(nEffCols, 0); 216 217 unsigned numAuto = 0; 218 unsigned autoSpan = 0; 219 int totalFixedWidth = 0; 220 int totalPercentWidth = 0; 221 float totalPercent = 0; 222 223 // Compute requirements and try to satisfy fixed and percent widths. 224 // Percentages are of the table's width, so for example 225 // for a table width of 100px with columns (40px, 10%), the 10% compute 226 // to 10px here, and will scale up to 20px in the final (80px, 20px). 227 for (unsigned i = 0; i < nEffCols; i++) { 228 if (m_width[i].isFixed()) { 229 calcWidth[i] = m_width[i].value(); 230 totalFixedWidth += calcWidth[i]; 231 } else if (m_width[i].isPercent()) { 232 calcWidth[i] = valueForLength(m_width[i], tableLogicalWidth); 233 totalPercentWidth += calcWidth[i]; 234 totalPercent += m_width[i].percent(); 235 } else if (m_width[i].isAuto()) { 236 numAuto++; 237 autoSpan += m_table->spanOfEffCol(i); 238 } 239 } 240 241 int hspacing = m_table->hBorderSpacing(); 242 int totalWidth = totalFixedWidth + totalPercentWidth; 243 if (!numAuto || totalWidth > tableLogicalWidth) { 244 // If there are no auto columns, or if the total is too wide, take 245 // what we have and scale it to fit as necessary. 246 if (totalWidth != tableLogicalWidth) { 247 // Fixed widths only scale up 248 if (totalFixedWidth && totalWidth < tableLogicalWidth) { 249 totalFixedWidth = 0; 250 for (unsigned i = 0; i < nEffCols; i++) { 251 if (m_width[i].isFixed()) { 252 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth; 253 totalFixedWidth += calcWidth[i]; 254 } 255 } 256 } 257 if (totalPercent) { 258 totalPercentWidth = 0; 259 for (unsigned i = 0; i < nEffCols; i++) { 260 if (m_width[i].isPercent()) { 261 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent; 262 totalPercentWidth += calcWidth[i]; 263 } 264 } 265 } 266 totalWidth = totalFixedWidth + totalPercentWidth; 267 } 268 } else { 269 // Divide the remaining width among the auto columns. 270 ASSERT(autoSpan >= numAuto); 271 int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); 272 int lastAuto = 0; 273 for (unsigned i = 0; i < nEffCols; i++) { 274 if (m_width[i].isAuto()) { 275 unsigned span = m_table->spanOfEffCol(i); 276 int w = remainingWidth * span / autoSpan; 277 calcWidth[i] = w + hspacing * (span - 1); 278 remainingWidth -= w; 279 if (!remainingWidth) 280 break; 281 lastAuto = i; 282 numAuto--; 283 ASSERT(autoSpan >= span); 284 autoSpan -= span; 285 } 286 } 287 // Last one gets the remainder. 288 if (remainingWidth) 289 calcWidth[lastAuto] += remainingWidth; 290 totalWidth = tableLogicalWidth; 291 } 292 293 if (totalWidth < tableLogicalWidth) { 294 // Spread extra space over columns. 295 int remainingWidth = tableLogicalWidth - totalWidth; 296 int total = nEffCols; 297 while (total) { 298 int w = remainingWidth / total; 299 remainingWidth -= w; 300 calcWidth[--total] += w; 301 } 302 if (nEffCols > 0) 303 calcWidth[nEffCols - 1] += remainingWidth; 304 } 305 306 int pos = 0; 307 for (unsigned i = 0; i < nEffCols; i++) { 308 m_table->setColumnPosition(i, pos); 309 pos += calcWidth[i] + hspacing; 310 } 311 int colPositionsSize = m_table->columnPositions().size(); 312 if (colPositionsSize > 0) 313 m_table->setColumnPosition(colPositionsSize - 1, pos); 314 } 315 316 void FixedTableLayout::willChangeTableLayout() 317 { 318 // When switching table layout algorithm, we need to dirty the preferred 319 // logical widths as we cleared the bits without computing them. 320 // (see calcWidthArray above.) This optimization is preferred to always 321 // computing the logical widths we never intended to use. 322 m_table->recalcSectionsIfNeeded(); 323 for (RenderTableSection* section = m_table->topNonEmptySection(); section; section = m_table->sectionBelow(section)) { 324 for (unsigned i = 0; i < section->numRows(); i++) { 325 RenderTableRow* row = section->rowRendererAt(i); 326 if (!row) 327 continue; 328 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) 329 cell->setPreferredLogicalWidthsDirty(); 330 } 331 } 332 } 333 334 } // namespace WebCore 335