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 Apple Computer, 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) { 93 if (child->isTableCol()) { 94 RenderTableCol* col = toRenderTableCol(child); 95 if (col->firstChild()) 96 grpWidth = col->style()->width(); 97 else { 98 Length w = col->style()->width(); 99 if (w.isAuto()) 100 w = grpWidth; 101 int effWidth = 0; 102 if (w.isFixed() && w.value() > 0) 103 effWidth = w.value(); 104 105 int span = col->span(); 106 while (span) { 107 int 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 ((w.isFixed() || w.isPercent()) && w.isPositive()) { 122 m_width[currentEffectiveColumn].setRawValue(w.type(), w.rawValue() * spanInCurrentEffectiveColumn); 123 usedWidth += effWidth * spanInCurrentEffectiveColumn; 124 } 125 span -= spanInCurrentEffectiveColumn; 126 currentEffectiveColumn++; 127 } 128 } 129 toRenderTableCol(child)->calcPrefWidths(); 130 } else 131 break; 132 133 RenderObject* next = child->firstChild(); 134 if (!next) 135 next = child->nextSibling(); 136 if (!next && child->parent()->isTableCol()) { 137 next = child->parent()->nextSibling(); 138 grpWidth = Length(); 139 } 140 child = next; 141 } 142 143 // Iterate over the first row in case some are unspecified. 144 RenderTableSection* section = m_table->header(); 145 if (!section) 146 section = m_table->firstBody(); 147 if (!section) 148 section = m_table->footer(); 149 if (section && !section->numRows()) 150 section = m_table->sectionBelow(section, true); 151 if (section) { 152 int cCol = 0; 153 RenderObject* firstRow = section->firstChild(); 154 child = firstRow->firstChild(); 155 while (child) { 156 if (child->isTableCell()) { 157 RenderTableCell* cell = toRenderTableCell(child); 158 if (cell->prefWidthsDirty()) 159 cell->calcPrefWidths(); 160 161 Length w = cell->styleOrColWidth(); 162 int span = cell->colSpan(); 163 int effWidth = 0; 164 if (w.isFixed() && w.isPositive()) 165 effWidth = w.value(); 166 167 int usedSpan = 0; 168 int i = 0; 169 while (usedSpan < span) { 170 ASSERT(cCol + i < nEffCols); 171 int eSpan = m_table->spanOfEffCol(cCol + i); 172 // Only set if no col element has already set it. 173 if (m_width[cCol + i].isAuto() && w.type() != Auto) { 174 m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span); 175 usedWidth += effWidth * eSpan / span; 176 } 177 usedSpan += eSpan; 178 i++; 179 } 180 cCol += i; 181 } 182 child = child->nextSibling(); 183 } 184 } 185 186 return usedWidth; 187 } 188 189 // Use a very large value (in effect infinite). But not too large! 190 // numeric_limits<int>::max() will too easily overflow widths. 191 // Keep this in synch with BLOCK_MAX_WIDTH in RenderBlock.cpp 192 #define TABLE_MAX_WIDTH 15000 193 194 void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) 195 { 196 // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. 197 198 // we might want to wait until we have all of the first row before 199 // layouting for the first time. 200 201 // only need to calculate the minimum width as the sum of the 202 // cols/cells with a fixed width. 203 // 204 // The maximum width is max(minWidth, tableWidth). 205 int bs = m_table->bordersPaddingAndSpacing(); 206 207 int tableWidth = m_table->style()->width().isFixed() ? m_table->style()->width().value() - bs : 0; 208 int mw = calcWidthArray(tableWidth) + bs; 209 210 minWidth = max(mw, tableWidth); 211 maxWidth = minWidth; 212 213 // This quirk is very similar to one that exists in RenderBlock::calcBlockPrefWidths(). 214 // Here's the example for this one: 215 /* 216 <table style="width:100%; background-color:red"><tr><td> 217 <table style="background-color:blue"><tr><td> 218 <table style="width:100%; background-color:green; table-layout:fixed"><tr><td> 219 Content 220 </td></tr></table> 221 </td></tr></table> 222 </td></tr></table> 223 */ 224 // In this example, the two inner tables should be as large as the outer table. 225 // We can achieve this effect by making the maxwidth of fixed tables with percentage 226 // widths be infinite. 227 if (m_table->style()->htmlHacks() && m_table->style()->width().isPercent() 228 && maxWidth < TABLE_MAX_WIDTH) 229 maxWidth = TABLE_MAX_WIDTH; 230 } 231 232 void FixedTableLayout::layout() 233 { 234 int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing(); 235 int nEffCols = m_table->numEffCols(); 236 Vector<int> calcWidth(nEffCols, 0); 237 238 int numAuto = 0; 239 int autoSpan = 0; 240 int totalFixedWidth = 0; 241 int totalPercentWidth = 0; 242 int totalRawPercent = 0; 243 244 // Compute requirements and try to satisfy fixed and percent widths. 245 // Percentages are of the table's width, so for example 246 // for a table width of 100px with columns (40px, 10%), the 10% compute 247 // to 10px here, and will scale up to 20px in the final (80px, 20px). 248 for (int i = 0; i < nEffCols; i++) { 249 if (m_width[i].isFixed()) { 250 calcWidth[i] = m_width[i].value(); 251 totalFixedWidth += calcWidth[i]; 252 } else if (m_width[i].isPercent()) { 253 calcWidth[i] = m_width[i].calcValue(tableWidth); 254 totalPercentWidth += calcWidth[i]; 255 totalRawPercent += m_width[i].rawValue(); 256 } else if (m_width[i].isAuto()) { 257 numAuto++; 258 autoSpan += m_table->spanOfEffCol(i); 259 } 260 } 261 262 int hspacing = m_table->hBorderSpacing(); 263 int totalWidth = totalFixedWidth + totalPercentWidth; 264 if (!numAuto || totalWidth > tableWidth) { 265 // If there are no auto columns, or if the total is too wide, take 266 // what we have and scale it to fit as necessary. 267 if (totalWidth != tableWidth) { 268 // Fixed widths only scale up 269 if (totalFixedWidth && totalWidth < tableWidth) { 270 totalFixedWidth = 0; 271 for (int i = 0; i < nEffCols; i++) { 272 if (m_width[i].isFixed()) { 273 calcWidth[i] = calcWidth[i] * tableWidth / totalWidth; 274 totalFixedWidth += calcWidth[i]; 275 } 276 } 277 } 278 if (totalRawPercent) { 279 totalPercentWidth = 0; 280 for (int i = 0; i < nEffCols; i++) { 281 if (m_width[i].isPercent()) { 282 calcWidth[i] = m_width[i].rawValue() * (tableWidth - totalFixedWidth) / totalRawPercent; 283 totalPercentWidth += calcWidth[i]; 284 } 285 } 286 } 287 totalWidth = totalFixedWidth + totalPercentWidth; 288 } 289 } else { 290 // Divide the remaining width among the auto columns. 291 int remainingWidth = tableWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); 292 int lastAuto = 0; 293 for (int i = 0; i < nEffCols; i++) { 294 if (m_width[i].isAuto()) { 295 int span = m_table->spanOfEffCol(i); 296 int w = remainingWidth * span / autoSpan; 297 calcWidth[i] = w + hspacing * (span - 1); 298 remainingWidth -= w; 299 if (!remainingWidth) 300 break; 301 lastAuto = i; 302 numAuto--; 303 autoSpan -= span; 304 } 305 } 306 // Last one gets the remainder. 307 if (remainingWidth) 308 calcWidth[lastAuto] += remainingWidth; 309 totalWidth = tableWidth; 310 } 311 312 if (totalWidth < tableWidth) { 313 // Spread extra space over columns. 314 int remainingWidth = tableWidth - totalWidth; 315 int total = nEffCols; 316 while (total) { 317 int w = remainingWidth / total; 318 remainingWidth -= w; 319 calcWidth[--total] += w; 320 } 321 if (nEffCols > 0) 322 calcWidth[nEffCols - 1] += remainingWidth; 323 } 324 325 int pos = 0; 326 for (int i = 0; i < nEffCols; i++) { 327 m_table->columnPositions()[i] = pos; 328 pos += calcWidth[i] + hspacing; 329 } 330 int colPositionsSize = m_table->columnPositions().size(); 331 if (colPositionsSize > 0) 332 m_table->columnPositions()[colPositionsSize - 1] = pos; 333 } 334 335 } // namespace WebCore 336