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 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 "FixedTableLayout.h" 24 25 #include "RenderTable.h" 26 #include "RenderTableCell.h" 27 #include "RenderTableCol.h" 28 #include "RenderTableSection.h" 29 30 /* 31 The text below is from the CSS 2.1 specs. 32 33 Fixed table layout 34 35 With this (fast) algorithm, the horizontal layout of the table does 36 not depend on the contents of the cells; it only depends on the 37 table's width, the width of the columns, and borders or cell 38 spacing. 39 40 The table's width may be specified explicitly with the 'width' 41 property. A value of 'auto' (for both 'display: table' and 'display: 42 inline-table') means use the automatic table layout algorithm. 43 44 In the fixed table layout algorithm, the width of each column is 45 determined as follows: 46 47 1. A column element with a value other than 'auto' for the 'width' 48 property sets the width for that column. 49 50 2. Otherwise, a cell in the first row with a value other than 51 'auto' for the 'width' property sets the width for that column. If 52 the cell spans more than one column, the width is divided over the 53 columns. 54 55 3. Any remaining columns equally divide the remaining horizontal 56 table space (minus borders or cell spacing). 57 58 The width of the table is then the greater of the value of the 59 'width' property for the table element and the sum of the column 60 widths (plus cell spacing or borders). If the table is wider than 61 the columns, the extra space should be distributed over the columns. 62 63 64 In this manner, the user agent can begin to lay out the table once 65 the entire first row has been received. Cells in subsequent rows do 66 not affect column widths. Any cell that has content that overflows 67 uses the 'overflow' property to determine whether to clip the 68 overflow content. 69 */ 70 71 using namespace std; 72 73 namespace WebCore { 74 75 FixedTableLayout::FixedTableLayout(RenderTable* table) 76 : TableLayout(table) 77 { 78 } 79 80 int FixedTableLayout::calcWidthArray(int) 81 { 82 int usedWidth = 0; 83 84 // iterate over all <col> elements 85 RenderObject* child = m_table->firstChild(); 86 int nEffCols = m_table->numEffCols(); 87 m_width.resize(nEffCols); 88 m_width.fill(Length(Auto)); 89 90 int currentEffectiveColumn = 0; 91 Length grpWidth; 92 while (child && child->isTableCol()) { 93 RenderTableCol* col = toRenderTableCol(child); 94 if (col->firstChild()) 95 grpWidth = col->style()->logicalWidth(); 96 else { 97 Length w = col->style()->logicalWidth(); 98 if (w.isAuto()) 99 w = grpWidth; 100 int effWidth = 0; 101 if (w.isFixed() && w.value() > 0) 102 effWidth = w.value(); 103 104 int span = col->span(); 105 while (span) { 106 int spanInCurrentEffectiveColumn; 107 if (currentEffectiveColumn >= nEffCols) { 108 m_table->appendColumn(span); 109 nEffCols++; 110 m_width.append(Length()); 111 spanInCurrentEffectiveColumn = span; 112 } else { 113 if (span < m_table->spanOfEffCol(currentEffectiveColumn)) { 114 m_table->splitColumn(currentEffectiveColumn, span); 115 nEffCols++; 116 m_width.append(Length()); 117 } 118 spanInCurrentEffectiveColumn = m_table->spanOfEffCol(currentEffectiveColumn); 119 } 120 if ((w.isFixed() || w.isPercent()) && w.isPositive()) { 121 m_width[currentEffectiveColumn] = w; 122 m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn; 123 usedWidth += effWidth * spanInCurrentEffectiveColumn; 124 } 125 span -= spanInCurrentEffectiveColumn; 126 currentEffectiveColumn++; 127 } 128 } 129 col->computePreferredLogicalWidths(); 130 131 RenderObject* next = child->firstChild(); 132 if (!next) 133 next = child->nextSibling(); 134 if (!next && child->parent()->isTableCol()) { 135 next = child->parent()->nextSibling(); 136 grpWidth = Length(); 137 } 138 child = next; 139 } 140 141 // Iterate over the first row in case some are unspecified. 142 RenderTableSection* section = m_table->header(); 143 if (!section) 144 section = m_table->firstBody(); 145 if (!section) 146 section = m_table->footer(); 147 if (section && !section->numRows()) 148 section = m_table->sectionBelow(section, true); 149 if (section) { 150 int cCol = 0; 151 RenderObject* firstRow = section->firstChild(); 152 child = firstRow->firstChild(); 153 while (child) { 154 if (child->isTableCell()) { 155 RenderTableCell* cell = toRenderTableCell(child); 156 if (cell->preferredLogicalWidthsDirty()) 157 cell->computePreferredLogicalWidths(); 158 159 Length w = cell->styleOrColLogicalWidth(); 160 int span = cell->colSpan(); 161 int effWidth = 0; 162 if (w.isFixed() && w.isPositive()) 163 effWidth = w.value(); 164 165 int usedSpan = 0; 166 int i = 0; 167 while (usedSpan < span && cCol + i < nEffCols) { 168 float eSpan = m_table->spanOfEffCol(cCol + i); 169 // Only set if no col element has already set it. 170 if (m_width[cCol + i].isAuto() && w.type() != Auto) { 171 m_width[cCol + i] = w; 172 m_width[cCol + i] *= eSpan / span; 173 usedWidth += effWidth * eSpan / span; 174 } 175 usedSpan += eSpan; 176 i++; 177 } 178 cCol += i; 179 } 180 child = child->nextSibling(); 181 } 182 } 183 184 return usedWidth; 185 } 186 187 // Use a very large value (in effect infinite). But not too large! 188 // numeric_limits<int>::max() will too easily overflow widths. 189 // Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp 190 #define TABLE_MAX_WIDTH 15000 191 192 void FixedTableLayout::computePreferredLogicalWidths(int& minWidth, int& maxWidth) 193 { 194 // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. 195 196 // we might want to wait until we have all of the first row before 197 // layouting for the first time. 198 199 // only need to calculate the minimum width as the sum of the 200 // cols/cells with a fixed width. 201 // 202 // The maximum width is max(minWidth, tableWidth). 203 int bordersPaddingAndSpacing = m_table->bordersPaddingAndSpacingInRowDirection(); 204 205 int tableLogicalWidth = m_table->style()->logicalWidth().isFixed() ? m_table->style()->logicalWidth().value() - bordersPaddingAndSpacing : 0; 206 int mw = calcWidthArray(tableLogicalWidth) + bordersPaddingAndSpacing; 207 208 minWidth = max(mw, tableLogicalWidth); 209 maxWidth = minWidth; 210 211 // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths(). 212 // Here's the example for this one: 213 /* 214 <table style="width:100%; background-color:red"><tr><td> 215 <table style="background-color:blue"><tr><td> 216 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td> 217 Content 218 </td></tr></table> 219 </td></tr></table> 220 </td></tr></table> 221 */ 222 // In this example, the two inner tables should be as large as the outer table. 223 // We can achieve this effect by making the maxwidth of fixed tables with percentage 224 // widths be infinite. 225 if (m_table->document()->inQuirksMode() && m_table->style()->logicalWidth().isPercent() && maxWidth < TABLE_MAX_WIDTH) 226 maxWidth = TABLE_MAX_WIDTH; 227 } 228 229 void FixedTableLayout::layout() 230 { 231 int tableLogicalWidth = m_table->logicalWidth() - m_table->bordersPaddingAndSpacingInRowDirection(); 232 int nEffCols = m_table->numEffCols(); 233 Vector<int> calcWidth(nEffCols, 0); 234 235 int numAuto = 0; 236 int autoSpan = 0; 237 int totalFixedWidth = 0; 238 int totalPercentWidth = 0; 239 float totalPercent = 0; 240 241 // Compute requirements and try to satisfy fixed and percent widths. 242 // Percentages are of the table's width, so for example 243 // for a table width of 100px with columns (40px, 10%), the 10% compute 244 // to 10px here, and will scale up to 20px in the final (80px, 20px). 245 for (int i = 0; i < nEffCols; i++) { 246 if (m_width[i].isFixed()) { 247 calcWidth[i] = m_width[i].value(); 248 totalFixedWidth += calcWidth[i]; 249 } else if (m_width[i].isPercent()) { 250 calcWidth[i] = m_width[i].calcValue(tableLogicalWidth); 251 totalPercentWidth += calcWidth[i]; 252 totalPercent += m_width[i].percent(); 253 } else if (m_width[i].isAuto()) { 254 numAuto++; 255 autoSpan += m_table->spanOfEffCol(i); 256 } 257 } 258 259 int hspacing = m_table->hBorderSpacing(); 260 int totalWidth = totalFixedWidth + totalPercentWidth; 261 if (!numAuto || totalWidth > tableLogicalWidth) { 262 // If there are no auto columns, or if the total is too wide, take 263 // what we have and scale it to fit as necessary. 264 if (totalWidth != tableLogicalWidth) { 265 // Fixed widths only scale up 266 if (totalFixedWidth && totalWidth < tableLogicalWidth) { 267 totalFixedWidth = 0; 268 for (int i = 0; i < nEffCols; i++) { 269 if (m_width[i].isFixed()) { 270 calcWidth[i] = calcWidth[i] * tableLogicalWidth / totalWidth; 271 totalFixedWidth += calcWidth[i]; 272 } 273 } 274 } 275 if (totalPercent) { 276 totalPercentWidth = 0; 277 for (int i = 0; i < nEffCols; i++) { 278 if (m_width[i].isPercent()) { 279 calcWidth[i] = m_width[i].percent() * (tableLogicalWidth - totalFixedWidth) / totalPercent; 280 totalPercentWidth += calcWidth[i]; 281 } 282 } 283 } 284 totalWidth = totalFixedWidth + totalPercentWidth; 285 } 286 } else { 287 // Divide the remaining width among the auto columns. 288 int remainingWidth = tableLogicalWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); 289 int lastAuto = 0; 290 for (int i = 0; i < nEffCols; i++) { 291 if (m_width[i].isAuto()) { 292 int span = m_table->spanOfEffCol(i); 293 int w = remainingWidth * span / autoSpan; 294 calcWidth[i] = w + hspacing * (span - 1); 295 remainingWidth -= w; 296 if (!remainingWidth) 297 break; 298 lastAuto = i; 299 numAuto--; 300 autoSpan -= span; 301 } 302 } 303 // Last one gets the remainder. 304 if (remainingWidth) 305 calcWidth[lastAuto] += remainingWidth; 306 totalWidth = tableLogicalWidth; 307 } 308 309 if (totalWidth < tableLogicalWidth) { 310 // Spread extra space over columns. 311 int remainingWidth = tableLogicalWidth - totalWidth; 312 int total = nEffCols; 313 while (total) { 314 int w = remainingWidth / total; 315 remainingWidth -= w; 316 calcWidth[--total] += w; 317 } 318 if (nEffCols > 0) 319 calcWidth[nEffCols - 1] += remainingWidth; 320 } 321 322 int pos = 0; 323 for (int i = 0; i < nEffCols; i++) { 324 m_table->columnPositions()[i] = pos; 325 pos += calcWidth[i] + hspacing; 326 } 327 int colPositionsSize = m_table->columnPositions().size(); 328 if (colPositionsSize > 0) 329 m_table->columnPositions()[colPositionsSize - 1] = pos; 330 } 331 332 } // namespace WebCore 333