|
1 /** |
|
2 * This file is part of the DOM implementation for KDE. |
|
3 * |
|
4 * Copyright (C) 1997 Martin Jones (mjones@kde.org) |
|
5 * (C) 1997 Torben Weis (weis@kde.org) |
|
6 * (C) 1998 Waldo Bastian (bastian@kde.org) |
|
7 * (C) 1999 Lars Knoll (knoll@kde.org) |
|
8 * (C) 1999 Antti Koivisto (koivisto@kde.org) |
|
9 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. |
|
10 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) |
|
11 * |
|
12 * This library is free software; you can redistribute it and/or |
|
13 * modify it under the terms of the GNU Library General Public |
|
14 * License as published by the Free Software Foundation; either |
|
15 * version 2 of the License, or (at your option) any later version. |
|
16 * |
|
17 * This library is distributed in the hope that it will be useful, |
|
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
20 * Library General Public License for more details. |
|
21 * |
|
22 * You should have received a copy of the GNU Library General Public License |
|
23 * along with this library; see the file COPYING.LIB. If not, write to |
|
24 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
|
25 * Boston, MA 02110-1301, USA. |
|
26 */ |
|
27 |
|
28 #include "config.h" |
|
29 #include "RenderTableSection.h" |
|
30 |
|
31 #include "CachedImage.h" |
|
32 #include "Document.h" |
|
33 #include "HTMLNames.h" |
|
34 #include "RenderTableCell.h" |
|
35 #include "RenderTableCol.h" |
|
36 #include "RenderTableRow.h" |
|
37 #include "RenderView.h" |
|
38 #include "TextStream.h" |
|
39 #include <limits> |
|
40 #include <wtf/Vector.h> |
|
41 |
|
42 using namespace std; |
|
43 |
|
44 namespace WebCore { |
|
45 |
|
46 using namespace HTMLNames; |
|
47 |
|
48 RenderTableSection::RenderTableSection(Node* node) |
|
49 : RenderContainer(node) |
|
50 , m_gridRows(0) |
|
51 , m_cCol(0) |
|
52 , m_cRow(-1) |
|
53 , m_needsCellRecalc(false) |
|
54 , m_outerBorderLeft(0) |
|
55 , m_outerBorderRight(0) |
|
56 , m_outerBorderTop(0) |
|
57 , m_outerBorderBottom(0) |
|
58 , m_overflowLeft(0) |
|
59 , m_overflowWidth(0) |
|
60 , m_overflowTop(0) |
|
61 , m_overflowHeight(0) |
|
62 , m_hasOverflowingCell(false) |
|
63 { |
|
64 // init RenderObject attributes |
|
65 setInline(false); // our object is not Inline |
|
66 } |
|
67 |
|
68 RenderTableSection::~RenderTableSection() |
|
69 { |
|
70 clearGrid(); |
|
71 } |
|
72 |
|
73 void RenderTableSection::destroy() |
|
74 { |
|
75 RenderTable* recalcTable = table(); |
|
76 |
|
77 RenderContainer::destroy(); |
|
78 |
|
79 // recalc cell info because RenderTable has unguarded pointers |
|
80 // stored that point to this RenderTableSection. |
|
81 if (recalcTable) |
|
82 recalcTable->setNeedsSectionRecalc(); |
|
83 } |
|
84 |
|
85 void RenderTableSection::setStyle(RenderStyle* newStyle) |
|
86 { |
|
87 // we don't allow changing this one |
|
88 if (style()) |
|
89 newStyle->setDisplay(style()->display()); |
|
90 else if (newStyle->display() != TABLE_FOOTER_GROUP && newStyle->display() != TABLE_HEADER_GROUP) |
|
91 newStyle->setDisplay(TABLE_ROW_GROUP); |
|
92 |
|
93 RenderContainer::setStyle(newStyle); |
|
94 } |
|
95 |
|
96 void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild) |
|
97 { |
|
98 // Make sure we don't append things after :after-generated content if we have it. |
|
99 if (!beforeChild && isAfterContent(lastChild())) |
|
100 beforeChild = lastChild(); |
|
101 |
|
102 bool isTableSection = element() && (element()->hasTagName(theadTag) || element()->hasTagName(tbodyTag) || element()->hasTagName(tfootTag)); |
|
103 |
|
104 if (!child->isTableRow()) { |
|
105 if (isTableSection && child->element() && child->element()->hasTagName(formTag) && document()->isHTMLDocument()) { |
|
106 RenderContainer::addChild(child, beforeChild); |
|
107 return; |
|
108 } |
|
109 |
|
110 RenderObject* last = beforeChild; |
|
111 if (!last) |
|
112 last = lastChild(); |
|
113 if (last && last->isAnonymous()) { |
|
114 last->addChild(child); |
|
115 return; |
|
116 } |
|
117 |
|
118 // If beforeChild is inside an anonymous cell/row, insert into the cell or into |
|
119 // the anonymous row containing it, if there is one. |
|
120 RenderObject* lastBox = last; |
|
121 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) |
|
122 lastBox = lastBox->parent(); |
|
123 if (lastBox && lastBox->isAnonymous()) { |
|
124 lastBox->addChild(child, beforeChild); |
|
125 return; |
|
126 } |
|
127 |
|
128 RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table */); |
|
129 RenderStyle* newStyle = new (renderArena()) RenderStyle(); |
|
130 newStyle->inheritFrom(style()); |
|
131 newStyle->setDisplay(TABLE_ROW); |
|
132 row->setStyle(newStyle); |
|
133 addChild(row, beforeChild); |
|
134 row->addChild(child); |
|
135 return; |
|
136 } |
|
137 |
|
138 if (beforeChild) |
|
139 setNeedsCellRecalc(); |
|
140 |
|
141 ++m_cRow; |
|
142 m_cCol = 0; |
|
143 |
|
144 // make sure we have enough rows |
|
145 if (!ensureRows(m_cRow + 1)) |
|
146 return; |
|
147 |
|
148 m_grid[m_cRow].rowRenderer = child; |
|
149 |
|
150 if (!beforeChild) { |
|
151 m_grid[m_cRow].height = child->style()->height(); |
|
152 if (m_grid[m_cRow].height.isRelative()) |
|
153 m_grid[m_cRow].height = Length(); |
|
154 } |
|
155 |
|
156 // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that. |
|
157 while (beforeChild && !beforeChild->isTableRow()) |
|
158 beforeChild = beforeChild->parent(); |
|
159 |
|
160 RenderContainer::addChild(child, beforeChild); |
|
161 } |
|
162 |
|
163 bool RenderTableSection::ensureRows(int numRows) |
|
164 { |
|
165 int nRows = m_gridRows; |
|
166 if (numRows > nRows) { |
|
167 if (numRows > static_cast<int>(m_grid.size())) { |
|
168 size_t maxSize = numeric_limits<size_t>::max() / sizeof(RowStruct); |
|
169 if (static_cast<size_t>(numRows) > maxSize) |
|
170 return false; |
|
171 m_grid.resize(numRows); |
|
172 } |
|
173 m_gridRows = numRows; |
|
174 int nCols = table()->numEffCols(); |
|
175 CellStruct emptyCellStruct; |
|
176 emptyCellStruct.cell = 0; |
|
177 emptyCellStruct.inColSpan = false; |
|
178 for (int r = nRows; r < numRows; r++) { |
|
179 m_grid[r].row = new Row(nCols); |
|
180 m_grid[r].row->fill(emptyCellStruct); |
|
181 m_grid[r].rowRenderer = 0; |
|
182 m_grid[r].baseline = 0; |
|
183 m_grid[r].height = Length(); |
|
184 } |
|
185 } |
|
186 |
|
187 return true; |
|
188 } |
|
189 |
|
190 void RenderTableSection::addCell(RenderTableCell* cell, RenderObject* row) |
|
191 { |
|
192 int rSpan = cell->rowSpan(); |
|
193 int cSpan = cell->colSpan(); |
|
194 Vector<RenderTable::ColumnStruct>& columns = table()->columns(); |
|
195 int nCols = columns.size(); |
|
196 |
|
197 // ### mozilla still seems to do the old HTML way, even for strict DTD |
|
198 // (see the annotation on table cell layouting in the CSS specs and the testcase below: |
|
199 // <TABLE border> |
|
200 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4 |
|
201 // <TR><TD colspan="2">5 |
|
202 // </TABLE> |
|
203 |
|
204 while (m_cCol < nCols && (cellAt(m_cRow, m_cCol).cell || cellAt(m_cRow, m_cCol).inColSpan)) |
|
205 m_cCol++; |
|
206 |
|
207 if (rSpan == 1) { |
|
208 // we ignore height settings on rowspan cells |
|
209 Length height = cell->style()->height(); |
|
210 if (height.isPositive() || (height.isRelative() && height.value() >= 0)) { |
|
211 Length cRowHeight = m_grid[m_cRow].height; |
|
212 switch (height.type()) { |
|
213 case Percent: |
|
214 if (!(cRowHeight.isPercent()) || |
|
215 (cRowHeight.isPercent() && cRowHeight.rawValue() < height.rawValue())) |
|
216 m_grid[m_cRow].height = height; |
|
217 break; |
|
218 case Fixed: |
|
219 if (cRowHeight.type() < Percent || |
|
220 (cRowHeight.isFixed() && cRowHeight.value() < height.value())) |
|
221 m_grid[m_cRow].height = height; |
|
222 break; |
|
223 case Relative: |
|
224 default: |
|
225 break; |
|
226 } |
|
227 } |
|
228 } |
|
229 |
|
230 // make sure we have enough rows |
|
231 if (!ensureRows(m_cRow + rSpan)) |
|
232 return; |
|
233 |
|
234 m_grid[m_cRow].rowRenderer = row; |
|
235 |
|
236 int col = m_cCol; |
|
237 // tell the cell where it is |
|
238 CellStruct currentCell; |
|
239 currentCell.cell = cell; |
|
240 currentCell.inColSpan = false; |
|
241 while (cSpan) { |
|
242 int currentSpan; |
|
243 if (m_cCol >= nCols) { |
|
244 table()->appendColumn(cSpan); |
|
245 currentSpan = cSpan; |
|
246 } else { |
|
247 if (cSpan < columns[m_cCol].span) |
|
248 table()->splitColumn(m_cCol, cSpan); |
|
249 currentSpan = columns[m_cCol].span; |
|
250 } |
|
251 |
|
252 for (int r = 0; r < rSpan; r++) { |
|
253 CellStruct& c = cellAt(m_cRow + r, m_cCol); |
|
254 if (currentCell.cell && !c.cell) |
|
255 c.cell = currentCell.cell; |
|
256 if (currentCell.inColSpan) |
|
257 c.inColSpan = true; |
|
258 } |
|
259 m_cCol++; |
|
260 cSpan -= currentSpan; |
|
261 currentCell.cell = 0; |
|
262 currentCell.inColSpan = true; |
|
263 } |
|
264 if (cell) { |
|
265 cell->setRow(m_cRow); |
|
266 cell->setCol(table()->effColToCol(col)); |
|
267 } |
|
268 } |
|
269 |
|
270 void RenderTableSection::setCellWidths() |
|
271 { |
|
272 Vector<int>& columnPos = table()->columnPositions(); |
|
273 bool pushedLayoutState = false; |
|
274 |
|
275 for (int i = 0; i < m_gridRows; i++) { |
|
276 Row& row = *m_grid[i].row; |
|
277 int cols = row.size(); |
|
278 for (int j = 0; j < cols; j++) { |
|
279 CellStruct current = row[j]; |
|
280 RenderTableCell* cell = current.cell; |
|
281 |
|
282 if (!cell) |
|
283 continue; |
|
284 int endCol = j; |
|
285 int cspan = cell->colSpan(); |
|
286 while (cspan && endCol < cols) { |
|
287 cspan -= table()->columns()[endCol].span; |
|
288 endCol++; |
|
289 } |
|
290 int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing(); |
|
291 int oldWidth = cell->width(); |
|
292 if (w != oldWidth) { |
|
293 cell->setNeedsLayout(true); |
|
294 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) { |
|
295 if (!pushedLayoutState) { |
|
296 // Technically, we should also push state for the row, but since |
|
297 // rows don't push a coordinate transform, that's not necessary. |
|
298 view()->pushLayoutState(this, IntSize(m_x, m_y)); |
|
299 pushedLayoutState = true; |
|
300 } |
|
301 cell->repaint(); |
|
302 } |
|
303 cell->setWidth(w); |
|
304 } |
|
305 } |
|
306 } |
|
307 |
|
308 if (pushedLayoutState) |
|
309 view()->popLayoutState(); |
|
310 } |
|
311 |
|
312 void RenderTableSection::calcRowHeight() |
|
313 { |
|
314 RenderTableCell* cell; |
|
315 |
|
316 int spacing = table()->vBorderSpacing(); |
|
317 bool pushedLayoutState = false; |
|
318 |
|
319 m_rowPos.resize(m_gridRows + 1); |
|
320 m_rowPos[0] = spacing; |
|
321 |
|
322 for (int r = 0; r < m_gridRows; r++) { |
|
323 m_rowPos[r + 1] = 0; |
|
324 m_grid[r].baseline = 0; |
|
325 int baseline = 0; |
|
326 int bdesc = 0; |
|
327 int ch = m_grid[r].height.calcMinValue(0); |
|
328 int pos = m_rowPos[r] + ch + (m_grid[r].rowRenderer ? spacing : 0); |
|
329 |
|
330 m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); |
|
331 |
|
332 Row* row = m_grid[r].row; |
|
333 int totalCols = row->size(); |
|
334 |
|
335 for (int c = 0; c < totalCols; c++) { |
|
336 CellStruct current = cellAt(r, c); |
|
337 cell = current.cell; |
|
338 if (!cell || current.inColSpan) |
|
339 continue; |
|
340 if (r < m_gridRows - 1 && cellAt(r + 1, c).cell == cell) |
|
341 continue; |
|
342 |
|
343 int indx = max(r - cell->rowSpan() + 1, 0); |
|
344 |
|
345 if (cell->overrideSize() != -1) { |
|
346 if (!pushedLayoutState) { |
|
347 // Technically, we should also push state for the row, but since |
|
348 // rows don't push a coordinate transform, that's not necessary. |
|
349 view()->pushLayoutState(this, IntSize(m_x, m_y)); |
|
350 pushedLayoutState = true; |
|
351 } |
|
352 cell->setOverrideSize(-1); |
|
353 cell->setChildNeedsLayout(true, false); |
|
354 cell->layoutIfNeeded(); |
|
355 } |
|
356 |
|
357 // Explicit heights use the border box in quirks mode. In strict mode do the right |
|
358 // thing and actually add in the border and padding. |
|
359 ch = cell->style()->height().calcValue(0) + |
|
360 (cell->style()->htmlHacks() ? 0 : (cell->paddingTop() + cell->paddingBottom() + |
|
361 cell->borderTop() + cell->borderBottom())); |
|
362 ch = max(ch, cell->height()); |
|
363 |
|
364 pos = m_rowPos[indx] + ch + (m_grid[r].rowRenderer ? spacing : 0); |
|
365 |
|
366 m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); |
|
367 |
|
368 // find out the baseline |
|
369 EVerticalAlign va = cell->style()->verticalAlign(); |
|
370 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { |
|
371 int b = cell->baselinePosition(); |
|
372 if (b > cell->borderTop() + cell->paddingTop()) { |
|
373 baseline = max(baseline, b); |
|
374 bdesc = max(bdesc, m_rowPos[indx] + ch - b); |
|
375 } |
|
376 } |
|
377 } |
|
378 |
|
379 //do we have baseline aligned elements? |
|
380 if (baseline) { |
|
381 // increase rowheight if baseline requires |
|
382 m_rowPos[r + 1] = max(m_rowPos[r + 1], baseline + bdesc + (m_grid[r].rowRenderer ? spacing : 0)); |
|
383 m_grid[r].baseline = baseline; |
|
384 } |
|
385 |
|
386 m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]); |
|
387 } |
|
388 |
|
389 if (pushedLayoutState) |
|
390 view()->popLayoutState(); |
|
391 } |
|
392 |
|
393 int RenderTableSection::layoutRows(int toAdd) |
|
394 { |
|
395 int rHeight; |
|
396 int rindx; |
|
397 int totalRows = m_gridRows; |
|
398 |
|
399 // Set the width of our section now. The rows will also be this width. |
|
400 m_width = table()->contentWidth(); |
|
401 m_overflowLeft = 0; |
|
402 m_overflowWidth = m_width; |
|
403 m_overflowTop = 0; |
|
404 m_overflowHeight = 0; |
|
405 m_hasOverflowingCell = false; |
|
406 |
|
407 if (table()->collapseBorders()) |
|
408 recalcOuterBorder(); |
|
409 |
|
410 if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) { |
|
411 int totalHeight = m_rowPos[totalRows] + toAdd; |
|
412 |
|
413 int dh = toAdd; |
|
414 int totalPercent = 0; |
|
415 int numAuto = 0; |
|
416 for (int r = 0; r < totalRows; r++) { |
|
417 if (m_grid[r].height.isAuto()) |
|
418 numAuto++; |
|
419 else if (m_grid[r].height.isPercent()) |
|
420 totalPercent += m_grid[r].height.rawValue(); |
|
421 } |
|
422 if (totalPercent) { |
|
423 // try to satisfy percent |
|
424 int add = 0; |
|
425 totalPercent = min(totalPercent, 100 * percentScaleFactor); |
|
426 int rh = m_rowPos[1] - m_rowPos[0]; |
|
427 for (int r = 0; r < totalRows; r++) { |
|
428 if (totalPercent > 0 && m_grid[r].height.isPercent()) { |
|
429 int toAdd = min(dh, (totalHeight * m_grid[r].height.rawValue() / (100 * percentScaleFactor)) - rh); |
|
430 // If toAdd is negative, then we don't want to shrink the row (this bug |
|
431 // affected Outlook Web Access). |
|
432 toAdd = max(0, toAdd); |
|
433 add += toAdd; |
|
434 dh -= toAdd; |
|
435 totalPercent -= m_grid[r].height.rawValue(); |
|
436 } |
|
437 if (r < totalRows - 1) |
|
438 rh = m_rowPos[r + 2] - m_rowPos[r + 1]; |
|
439 m_rowPos[r + 1] += add; |
|
440 } |
|
441 } |
|
442 if (numAuto) { |
|
443 // distribute over variable cols |
|
444 int add = 0; |
|
445 for (int r = 0; r < totalRows; r++) { |
|
446 if (numAuto > 0 && m_grid[r].height.isAuto()) { |
|
447 int toAdd = dh / numAuto; |
|
448 add += toAdd; |
|
449 dh -= toAdd; |
|
450 numAuto--; |
|
451 } |
|
452 m_rowPos[r + 1] += add; |
|
453 } |
|
454 } |
|
455 if (dh > 0 && m_rowPos[totalRows]) { |
|
456 // if some left overs, distribute equally. |
|
457 int tot = m_rowPos[totalRows]; |
|
458 int add = 0; |
|
459 int prev = m_rowPos[0]; |
|
460 for (int r = 0; r < totalRows; r++) { |
|
461 //weight with the original height |
|
462 add += dh * (m_rowPos[r + 1] - prev) / tot; |
|
463 prev = m_rowPos[r + 1]; |
|
464 m_rowPos[r + 1] += add; |
|
465 } |
|
466 } |
|
467 } |
|
468 |
|
469 int hspacing = table()->hBorderSpacing(); |
|
470 int vspacing = table()->vBorderSpacing(); |
|
471 int nEffCols = table()->numEffCols(); |
|
472 |
|
473 view()->pushLayoutState(this, IntSize(m_x, m_y)); |
|
474 |
|
475 for (int r = 0; r < totalRows; r++) { |
|
476 // Set the row's x/y position and width/height. |
|
477 if (RenderObject* rowRenderer = m_grid[r].rowRenderer) { |
|
478 rowRenderer->setPos(0, m_rowPos[r]); |
|
479 rowRenderer->setWidth(m_width); |
|
480 rowRenderer->setHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing); |
|
481 } |
|
482 |
|
483 for (int c = 0; c < nEffCols; c++) { |
|
484 RenderTableCell* cell = cellAt(r, c).cell; |
|
485 |
|
486 if (!cell) |
|
487 continue; |
|
488 if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell) |
|
489 continue; |
|
490 |
|
491 rindx = max(0, r - cell->rowSpan() + 1); |
|
492 |
|
493 rHeight = m_rowPos[r + 1] - m_rowPos[rindx] - vspacing; |
|
494 |
|
495 // Force percent height children to lay themselves out again. |
|
496 // This will cause these children to grow to fill the cell. |
|
497 // FIXME: There is still more work to do here to fully match WinIE (should |
|
498 // it become necessary to do so). In quirks mode, WinIE behaves like we |
|
499 // do, but it will clip the cells that spill out of the table section. In |
|
500 // strict mode, Mozilla and WinIE both regrow the table to accommodate the |
|
501 // new height of the cell (thus letting the percentages cause growth one |
|
502 // time only). We may also not be handling row-spanning cells correctly. |
|
503 // |
|
504 // Note also the oddity where replaced elements always flex, and yet blocks/tables do |
|
505 // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to |
|
506 // match the behavior perfectly, but we'll continue to refine it as we discover new |
|
507 // bugs. :) |
|
508 bool cellChildrenFlex = false; |
|
509 bool flexAllChildren = cell->style()->height().isFixed() || |
|
510 (!table()->style()->height().isAuto() && rHeight != cell->height()); |
|
511 |
|
512 for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { |
|
513 if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || o->scrollsOverflow() || flexAllChildren)) { |
|
514 // Tables with no sections do not flex. |
|
515 if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) { |
|
516 o->setNeedsLayout(true, false); |
|
517 cell->setChildNeedsLayout(true, false); |
|
518 cellChildrenFlex = true; |
|
519 } |
|
520 } |
|
521 } |
|
522 if (cellChildrenFlex) { |
|
523 // Alignment within a cell is based off the calculated |
|
524 // height, which becomes irrelevant once the cell has |
|
525 // been resized based off its percentage. -dwh |
|
526 cell->setOverrideSize(max(0, |
|
527 rHeight - cell->borderTop() - cell->paddingTop() - |
|
528 cell->borderBottom() - cell->paddingBottom())); |
|
529 cell->layoutIfNeeded(); |
|
530 |
|
531 // If the baseline moved, we may have to update the data for our row. Find out the new baseline. |
|
532 EVerticalAlign va = cell->style()->verticalAlign(); |
|
533 if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { |
|
534 int b = cell->baselinePosition(); |
|
535 if (b > cell->borderTop() + cell->paddingTop()) |
|
536 m_grid[r].baseline = max(m_grid[r].baseline, b); |
|
537 } |
|
538 } |
|
539 |
|
540 int te = 0; |
|
541 switch (cell->style()->verticalAlign()) { |
|
542 case SUB: |
|
543 case SUPER: |
|
544 case TEXT_TOP: |
|
545 case TEXT_BOTTOM: |
|
546 case BASELINE: |
|
547 te = getBaseline(r) - cell->baselinePosition(); |
|
548 break; |
|
549 case TOP: |
|
550 te = 0; |
|
551 break; |
|
552 case MIDDLE: |
|
553 te = (rHeight - cell->height()) / 2; |
|
554 break; |
|
555 case BOTTOM: |
|
556 te = rHeight - cell->height(); |
|
557 break; |
|
558 default: |
|
559 break; |
|
560 } |
|
561 |
|
562 int oldTe = cell->borderTopExtra(); |
|
563 int oldBe = cell->borderBottomExtra(); |
|
564 |
|
565 int be = rHeight - cell->height() - te; |
|
566 cell->setCellTopExtra(te); |
|
567 cell->setCellBottomExtra(be); |
|
568 if ((te != oldTe || be > oldBe) && !table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) |
|
569 cell->repaint(); |
|
570 |
|
571 IntRect oldCellRect(cell->xPos(), cell->yPos() - cell->borderTopExtra() , cell->width(), cell->height()); |
|
572 |
|
573 if (style()->direction() == RTL) { |
|
574 cell->setPos(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]); |
|
575 } else |
|
576 cell->setPos(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]); |
|
577 |
|
578 m_overflowLeft = min(m_overflowLeft, cell->xPos() + cell->overflowLeft(false)); |
|
579 m_overflowWidth = max(m_overflowWidth, cell->xPos() + cell->overflowWidth(false)); |
|
580 m_overflowTop = min(m_overflowTop, cell->yPos() + cell->overflowTop(false)); |
|
581 m_overflowHeight = max(m_overflowHeight, cell->yPos() + cell->overflowHeight(false)); |
|
582 m_hasOverflowingCell |= cell->overflowLeft(false) || cell->overflowWidth(false) > cell->width() || cell->overflowTop(false) || cell->overflowHeight(false) > cell->height(); |
|
583 |
|
584 // If the cell moved, we have to repaint it as well as any floating/positioned |
|
585 // descendants. An exception is if we need a layout. In this case, we know we're going to |
|
586 // repaint ourselves (and the cell) anyway. |
|
587 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) |
|
588 cell->repaintDuringLayoutIfMoved(oldCellRect); |
|
589 } |
|
590 } |
|
591 |
|
592 view()->popLayoutState(); |
|
593 |
|
594 m_height = m_rowPos[totalRows]; |
|
595 m_overflowHeight = max(m_overflowHeight, m_height); |
|
596 return m_height; |
|
597 } |
|
598 |
|
599 int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const |
|
600 { |
|
601 int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf); |
|
602 if (!includeOverflowInterior && hasOverflowClip()) |
|
603 return bottom; |
|
604 |
|
605 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { |
|
606 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { |
|
607 if (cell->isTableCell()) |
|
608 bottom = max(bottom, cell->yPos() + cell->lowestPosition(false)); |
|
609 } |
|
610 } |
|
611 |
|
612 return bottom; |
|
613 } |
|
614 |
|
615 int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const |
|
616 { |
|
617 int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf); |
|
618 if (!includeOverflowInterior && hasOverflowClip()) |
|
619 return right; |
|
620 |
|
621 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { |
|
622 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { |
|
623 if (cell->isTableCell()) |
|
624 right = max(right, cell->xPos() + cell->rightmostPosition(false)); |
|
625 } |
|
626 } |
|
627 |
|
628 return right; |
|
629 } |
|
630 |
|
631 int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const |
|
632 { |
|
633 int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf); |
|
634 if (!includeOverflowInterior && hasOverflowClip()) |
|
635 return left; |
|
636 |
|
637 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { |
|
638 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { |
|
639 if (cell->isTableCell()) |
|
640 left = min(left, cell->xPos() + cell->leftmostPosition(false)); |
|
641 } |
|
642 } |
|
643 |
|
644 return left; |
|
645 } |
|
646 |
|
647 int RenderTableSection::calcOuterBorderTop() const |
|
648 { |
|
649 int totalCols = table()->numEffCols(); |
|
650 if (!m_gridRows || !totalCols) |
|
651 return 0; |
|
652 |
|
653 unsigned borderWidth = 0; |
|
654 |
|
655 const BorderValue& sb = style()->borderTop(); |
|
656 if (sb.style() == BHIDDEN) |
|
657 return -1; |
|
658 if (sb.style() > BHIDDEN) |
|
659 borderWidth = sb.width; |
|
660 |
|
661 const BorderValue& rb = firstChild()->style()->borderTop(); |
|
662 if (rb.style() == BHIDDEN) |
|
663 return -1; |
|
664 if (rb.style() > BHIDDEN && rb.width > borderWidth) |
|
665 borderWidth = rb.width; |
|
666 |
|
667 bool allHidden = true; |
|
668 for (int c = 0; c < totalCols; c++) { |
|
669 const CellStruct& current = cellAt(0, c); |
|
670 if (current.inColSpan || !current.cell) |
|
671 continue; |
|
672 const BorderValue& cb = current.cell->style()->borderTop(); |
|
673 // FIXME: Don't repeat for the same col group |
|
674 RenderTableCol* colGroup = table()->colElement(c); |
|
675 if (colGroup) { |
|
676 const BorderValue& gb = colGroup->style()->borderTop(); |
|
677 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) |
|
678 continue; |
|
679 else |
|
680 allHidden = false; |
|
681 if (gb.style() > BHIDDEN && gb.width > borderWidth) |
|
682 borderWidth = gb.width; |
|
683 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
684 borderWidth = cb.width; |
|
685 } else { |
|
686 if (cb.style() == BHIDDEN) |
|
687 continue; |
|
688 else |
|
689 allHidden = false; |
|
690 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
691 borderWidth = cb.width; |
|
692 } |
|
693 } |
|
694 if (allHidden) |
|
695 return -1; |
|
696 |
|
697 return borderWidth / 2; |
|
698 } |
|
699 |
|
700 int RenderTableSection::calcOuterBorderBottom() const |
|
701 { |
|
702 int totalCols = table()->numEffCols(); |
|
703 if (!m_gridRows || !totalCols) |
|
704 return 0; |
|
705 |
|
706 unsigned borderWidth = 0; |
|
707 |
|
708 const BorderValue& sb = style()->borderBottom(); |
|
709 if (sb.style() == BHIDDEN) |
|
710 return -1; |
|
711 if (sb.style() > BHIDDEN) |
|
712 borderWidth = sb.width; |
|
713 |
|
714 const BorderValue& rb = lastChild()->style()->borderBottom(); |
|
715 if (rb.style() == BHIDDEN) |
|
716 return -1; |
|
717 if (rb.style() > BHIDDEN && rb.width > borderWidth) |
|
718 borderWidth = rb.width; |
|
719 |
|
720 bool allHidden = true; |
|
721 for (int c = 0; c < totalCols; c++) { |
|
722 const CellStruct& current = cellAt(m_gridRows - 1, c); |
|
723 if (current.inColSpan || !current.cell) |
|
724 continue; |
|
725 const BorderValue& cb = current.cell->style()->borderBottom(); |
|
726 // FIXME: Don't repeat for the same col group |
|
727 RenderTableCol* colGroup = table()->colElement(c); |
|
728 if (colGroup) { |
|
729 const BorderValue& gb = colGroup->style()->borderBottom(); |
|
730 if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) |
|
731 continue; |
|
732 else |
|
733 allHidden = false; |
|
734 if (gb.style() > BHIDDEN && gb.width > borderWidth) |
|
735 borderWidth = gb.width; |
|
736 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
737 borderWidth = cb.width; |
|
738 } else { |
|
739 if (cb.style() == BHIDDEN) |
|
740 continue; |
|
741 else |
|
742 allHidden = false; |
|
743 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
744 borderWidth = cb.width; |
|
745 } |
|
746 } |
|
747 if (allHidden) |
|
748 return -1; |
|
749 |
|
750 return (borderWidth + 1) / 2; |
|
751 } |
|
752 |
|
753 int RenderTableSection::calcOuterBorderLeft(bool rtl) const |
|
754 { |
|
755 int totalCols = table()->numEffCols(); |
|
756 if (!m_gridRows || !totalCols) |
|
757 return 0; |
|
758 |
|
759 unsigned borderWidth = 0; |
|
760 |
|
761 const BorderValue& sb = style()->borderLeft(); |
|
762 if (sb.style() == BHIDDEN) |
|
763 return -1; |
|
764 if (sb.style() > BHIDDEN) |
|
765 borderWidth = sb.width; |
|
766 |
|
767 int leftmostColumn = rtl ? totalCols - 1 : 0; |
|
768 RenderTableCol* colGroup = table()->colElement(leftmostColumn); |
|
769 if (colGroup) { |
|
770 const BorderValue& gb = colGroup->style()->borderLeft(); |
|
771 if (gb.style() == BHIDDEN) |
|
772 return -1; |
|
773 if (gb.style() > BHIDDEN && gb.width > borderWidth) |
|
774 borderWidth = gb.width; |
|
775 } |
|
776 |
|
777 bool allHidden = true; |
|
778 for (int r = 0; r < m_gridRows; r++) { |
|
779 const CellStruct& current = cellAt(r, leftmostColumn); |
|
780 if (!current.cell) |
|
781 continue; |
|
782 // FIXME: Don't repeat for the same cell |
|
783 const BorderValue& cb = current.cell->style()->borderLeft(); |
|
784 const BorderValue& rb = current.cell->parent()->style()->borderLeft(); |
|
785 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) |
|
786 continue; |
|
787 else |
|
788 allHidden = false; |
|
789 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
790 borderWidth = cb.width; |
|
791 if (rb.style() > BHIDDEN && rb.width > borderWidth) |
|
792 borderWidth = rb.width; |
|
793 } |
|
794 if (allHidden) |
|
795 return -1; |
|
796 |
|
797 return borderWidth / 2; |
|
798 } |
|
799 |
|
800 int RenderTableSection::calcOuterBorderRight(bool rtl) const |
|
801 { |
|
802 int totalCols = table()->numEffCols(); |
|
803 if (!m_gridRows || !totalCols) |
|
804 return 0; |
|
805 |
|
806 unsigned borderWidth = 0; |
|
807 |
|
808 const BorderValue& sb = style()->borderRight(); |
|
809 if (sb.style() == BHIDDEN) |
|
810 return -1; |
|
811 if (sb.style() > BHIDDEN) |
|
812 borderWidth = sb.width; |
|
813 |
|
814 int rightmostColumn = rtl ? 0 : totalCols - 1; |
|
815 RenderTableCol* colGroup = table()->colElement(rightmostColumn); |
|
816 if (colGroup) { |
|
817 const BorderValue& gb = colGroup->style()->borderRight(); |
|
818 if (gb.style() == BHIDDEN) |
|
819 return -1; |
|
820 if (gb.style() > BHIDDEN && gb.width > borderWidth) |
|
821 borderWidth = gb.width; |
|
822 } |
|
823 |
|
824 bool allHidden = true; |
|
825 for (int r = 0; r < m_gridRows; r++) { |
|
826 const CellStruct& current = cellAt(r, rightmostColumn); |
|
827 if (!current.cell) |
|
828 continue; |
|
829 // FIXME: Don't repeat for the same cell |
|
830 const BorderValue& cb = current.cell->style()->borderRight(); |
|
831 const BorderValue& rb = current.cell->parent()->style()->borderRight(); |
|
832 if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) |
|
833 continue; |
|
834 else |
|
835 allHidden = false; |
|
836 if (cb.style() > BHIDDEN && cb.width > borderWidth) |
|
837 borderWidth = cb.width; |
|
838 if (rb.style() > BHIDDEN && rb.width > borderWidth) |
|
839 borderWidth = rb.width; |
|
840 } |
|
841 if (allHidden) |
|
842 return -1; |
|
843 |
|
844 return (borderWidth + 1) / 2; |
|
845 } |
|
846 |
|
847 void RenderTableSection::recalcOuterBorder() |
|
848 { |
|
849 bool rtl = table()->style()->direction() == RTL; |
|
850 m_outerBorderTop = calcOuterBorderTop(); |
|
851 m_outerBorderBottom = calcOuterBorderBottom(); |
|
852 m_outerBorderLeft = calcOuterBorderLeft(rtl); |
|
853 m_outerBorderRight = calcOuterBorderRight(rtl); |
|
854 } |
|
855 |
|
856 |
|
857 void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty) |
|
858 { |
|
859 // put this back in when all layout tests can handle it |
|
860 // ASSERT(!needsLayout()); |
|
861 // avoid crashing on bugs that cause us to paint with dirty layout |
|
862 if (needsLayout()) |
|
863 return; |
|
864 |
|
865 unsigned totalRows = m_gridRows; |
|
866 unsigned totalCols = table()->columns().size(); |
|
867 |
|
868 if (!totalRows || !totalCols) |
|
869 return; |
|
870 |
|
871 tx += m_x; |
|
872 ty += m_y; |
|
873 |
|
874 // Check which rows and cols are visible and only paint these. |
|
875 // FIXME: Could use a binary search here. |
|
876 PaintPhase paintPhase = paintInfo.phase; |
|
877 int x = paintInfo.rect.x(); |
|
878 int y = paintInfo.rect.y(); |
|
879 int w = paintInfo.rect.width(); |
|
880 int h = paintInfo.rect.height(); |
|
881 |
|
882 int os = 2 * maximalOutlineSize(paintPhase); |
|
883 unsigned startrow = 0; |
|
884 unsigned endrow = totalRows; |
|
885 |
|
886 // If some cell overflows, just paint all of them. |
|
887 if (!m_hasOverflowingCell && m_rowPos.size()) { |
|
888 for (; startrow < totalRows; startrow++) { |
|
889 if (ty + m_rowPos[startrow + 1] >= y - os) |
|
890 break; |
|
891 } |
|
892 if (startrow == totalRows && ty + m_rowPos[totalRows] + table()->outerBorderBottom() >= y - os) |
|
893 startrow--; |
|
894 |
|
895 for (; endrow > 0; endrow--) { |
|
896 if (ty + m_rowPos[endrow - 1] <= y + h + os) |
|
897 break; |
|
898 } |
|
899 if (!endrow && ty + m_rowPos[0] - table()->outerBorderTop() <= y + h + os) |
|
900 endrow++; |
|
901 } |
|
902 |
|
903 unsigned startcol = 0; |
|
904 unsigned endcol = totalCols; |
|
905 // FIXME: Implement RTL. |
|
906 if (!m_hasOverflowingCell && style()->direction() == LTR) { |
|
907 for (; startcol < totalCols; startcol++) { |
|
908 if (tx + table()->columnPositions()[startcol + 1] >= x - os) |
|
909 break; |
|
910 } |
|
911 if (startcol == totalCols && tx + table()->columnPositions()[totalCols] + table()->outerBorderRight() >= x - os) |
|
912 startcol--; |
|
913 |
|
914 for (; endcol > 0; endcol--) { |
|
915 if (tx + table()->columnPositions()[endcol - 1] <= x + w + os) |
|
916 break; |
|
917 } |
|
918 if (!endcol && tx + table()->columnPositions()[0] - table()->outerBorderLeft() <= y + w + os) |
|
919 endcol++; |
|
920 } |
|
921 |
|
922 if (startcol < endcol) { |
|
923 // draw the cells |
|
924 for (unsigned r = startrow; r < endrow; r++) { |
|
925 unsigned c = startcol; |
|
926 // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it |
|
927 while (c && cellAt(r, c).inColSpan) |
|
928 c--; |
|
929 for (; c < endcol; c++) { |
|
930 CellStruct current = cellAt(r, c); |
|
931 RenderTableCell* cell = current.cell; |
|
932 |
|
933 // Cells must always paint in the order in which they appear taking into account |
|
934 // their upper left originating row/column. For cells with rowspans, avoid repainting |
|
935 // if we've already seen the cell. |
|
936 if (!cell || (r > startrow && (cellAt(r - 1, c).cell == cell))) |
|
937 continue; |
|
938 |
|
939 RenderTableRow* row = static_cast<RenderTableRow*>(cell->parent()); |
|
940 |
|
941 if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) { |
|
942 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of |
|
943 // the column group, column, row group, row, and then the cell. |
|
944 RenderObject* col = table()->colElement(c); |
|
945 RenderObject* colGroup = 0; |
|
946 if (col && col->parent()->style()->display() == TABLE_COLUMN_GROUP) |
|
947 colGroup = col->parent(); |
|
948 |
|
949 // Column groups and columns first. |
|
950 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in |
|
951 // the stack, since we have already opened a transparency layer (potentially) for the table row group. |
|
952 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the |
|
953 // cell. |
|
954 cell->paintBackgroundsBehindCell(paintInfo, tx, ty, colGroup); |
|
955 cell->paintBackgroundsBehindCell(paintInfo, tx, ty, col); |
|
956 |
|
957 // Paint the row group next. |
|
958 cell->paintBackgroundsBehindCell(paintInfo, tx, ty, this); |
|
959 |
|
960 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for |
|
961 // painting the row background for the cell. |
|
962 if (!row->hasLayer()) |
|
963 cell->paintBackgroundsBehindCell(paintInfo, tx, ty, row); |
|
964 } |
|
965 |
|
966 if ((!cell->hasLayer() && !row->hasLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) |
|
967 cell->paint(paintInfo, tx, ty); |
|
968 } |
|
969 } |
|
970 } |
|
971 } |
|
972 |
|
973 void RenderTableSection::imageChanged(CachedImage* image) |
|
974 { |
|
975 if (!image || !image->canRender() || !parent()) |
|
976 return; |
|
977 |
|
978 // FIXME: Examine cells and repaint only the rect the image paints in. |
|
979 repaint(); |
|
980 } |
|
981 |
|
982 void RenderTableSection::recalcCells() |
|
983 { |
|
984 m_cCol = 0; |
|
985 m_cRow = -1; |
|
986 clearGrid(); |
|
987 m_gridRows = 0; |
|
988 |
|
989 for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { |
|
990 if (row->isTableRow()) { |
|
991 m_cRow++; |
|
992 m_cCol = 0; |
|
993 if (!ensureRows(m_cRow + 1)) |
|
994 break; |
|
995 m_grid[m_cRow].rowRenderer = row; |
|
996 |
|
997 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { |
|
998 if (cell->isTableCell()) |
|
999 addCell(static_cast<RenderTableCell*>(cell), row); |
|
1000 } |
|
1001 } |
|
1002 } |
|
1003 m_needsCellRecalc = false; |
|
1004 setNeedsLayout(true); |
|
1005 } |
|
1006 |
|
1007 void RenderTableSection::clearGrid() |
|
1008 { |
|
1009 int rows = m_gridRows; |
|
1010 while (rows--) |
|
1011 delete m_grid[rows].row; |
|
1012 } |
|
1013 |
|
1014 int RenderTableSection::numColumns() const |
|
1015 { |
|
1016 int result = 0; |
|
1017 |
|
1018 for (int r = 0; r < m_gridRows; ++r) { |
|
1019 for (int c = result; c < table()->numEffCols(); ++c) { |
|
1020 const CellStruct& cell = cellAt(r, c); |
|
1021 if (cell.cell || cell.inColSpan) |
|
1022 result = c; |
|
1023 } |
|
1024 } |
|
1025 |
|
1026 return result + 1; |
|
1027 } |
|
1028 |
|
1029 void RenderTableSection::appendColumn(int pos) |
|
1030 { |
|
1031 for (int row = 0; row < m_gridRows; ++row) { |
|
1032 m_grid[row].row->resize(pos + 1); |
|
1033 CellStruct& c = cellAt(row, pos); |
|
1034 c.cell = 0; |
|
1035 c.inColSpan = false; |
|
1036 } |
|
1037 } |
|
1038 |
|
1039 void RenderTableSection::splitColumn(int pos, int newSize) |
|
1040 { |
|
1041 if (m_cCol > pos) |
|
1042 m_cCol++; |
|
1043 for (int row = 0; row < m_gridRows; ++row) { |
|
1044 m_grid[row].row->resize(newSize); |
|
1045 Row& r = *m_grid[row].row; |
|
1046 memmove(r.data() + pos + 1, r.data() + pos, (newSize - 1 - pos) * sizeof(CellStruct)); |
|
1047 r[pos + 1].cell = 0; |
|
1048 r[pos + 1].inColSpan = r[pos].inColSpan || r[pos].cell; |
|
1049 } |
|
1050 } |
|
1051 |
|
1052 RenderObject* RenderTableSection::removeChildNode(RenderObject* child, bool fullRemove) |
|
1053 { |
|
1054 setNeedsCellRecalc(); |
|
1055 return RenderContainer::removeChildNode(child, fullRemove); |
|
1056 } |
|
1057 |
|
1058 // Hit Testing |
|
1059 bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) |
|
1060 { |
|
1061 // Table sections cannot ever be hit tested. Effectively they do not exist. |
|
1062 // Just forward to our children always. |
|
1063 tx += m_x; |
|
1064 ty += m_y; |
|
1065 |
|
1066 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { |
|
1067 // FIXME: We have to skip over inline flows, since they can show up inside table rows |
|
1068 // at the moment (a demoted inline <form> for example). If we ever implement a |
|
1069 // table-specific hit-test method (which we should do for performance reasons anyway), |
|
1070 // then we can remove this check. |
|
1071 if (!child->hasLayer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { |
|
1072 updateHitTestResult(result, IntPoint(x - tx, y - ty)); |
|
1073 return true; |
|
1074 } |
|
1075 } |
|
1076 |
|
1077 return false; |
|
1078 } |
|
1079 |
|
1080 #ifndef NDEBUG |
|
1081 void RenderTableSection::dump(TextStream* stream, DeprecatedString ind) const |
|
1082 { |
|
1083 *stream << endl << ind << "grid=(" << m_gridRows << "," << table()->numEffCols() << ")" << endl << ind; |
|
1084 for (int r = 0; r < m_gridRows; r++) { |
|
1085 for (int c = 0; c < table()->numEffCols(); c++) { |
|
1086 if (cellAt(r, c).cell && !cellAt(r, c).inColSpan) |
|
1087 *stream << "(" << cellAt(r, c).cell->row() << "," << cellAt(r, c).cell->col() << "," |
|
1088 << cellAt(r, c).cell->rowSpan() << "," << cellAt(r, c).cell->colSpan() << ") "; |
|
1089 else |
|
1090 *stream << cellAt(r, c).cell << "null cell "; |
|
1091 } |
|
1092 *stream << endl << ind; |
|
1093 } |
|
1094 RenderContainer::dump(stream,ind); |
|
1095 } |
|
1096 #endif |
|
1097 |
|
1098 } // namespace WebCore |