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