source: webkit/trunk/Source/WebCore/rendering/RenderTableSection.cpp

Last change on this file was 290225, checked in by Alan Bujtas, 3 years ago

RenderBox::flipForWritingModeForChild should take const RenderBox& as the child renderer
https://bugs.webkit.org/show_bug.cgi?id=236895

Reviewed by Antti Koivisto.

  • rendering/LegacyInlineElementBox.cpp:

(WebCore::LegacyInlineElementBox::paint):
(WebCore::LegacyInlineElementBox::nodeAtPoint):

  • rendering/RenderBlock.cpp:

(WebCore::RenderBlock::paintChild):
(WebCore::RenderBlock::hitTestContents):
(WebCore::RenderBlock::paintExcludedChildrenInBorder):
(WebCore::RenderBlock::hitTestExcludedChildrenInBorder):

  • rendering/RenderBlockFlow.cpp:

(WebCore::RenderBlockFlow::paintColumnRules):

  • rendering/RenderBox.cpp:

(WebCore::RenderBox::flipForWritingModeForChild const):
(WebCore::RenderBox::topLeftLocation const):

  • rendering/RenderBox.h:
  • rendering/RenderFlexibleBox.cpp:

(WebCore::RenderFlexibleBox::hitTestChildren):

  • rendering/RenderTable.cpp:

(WebCore::RenderTable::paintObject):
(WebCore::RenderTable::nodeAtPoint):

  • rendering/RenderTableRow.cpp:

(WebCore::RenderTableRow::nodeAtPoint):

  • rendering/RenderTableSection.cpp:

(WebCore::RenderTableSection::paintCell):
(WebCore::RenderTableSection::paintObject):
(WebCore::RenderTableSection::nodeAtPoint):

  • Property svn:eol-style set to native
File size: 66.2 KB
Line 
1/*
2 * Copyright (C) 1997 Martin Jones ([email protected])
3 * (C) 1997 Torben Weis ([email protected])
4 * (C) 1998 Waldo Bastian ([email protected])
5 * (C) 1999 Lars Knoll ([email protected])
6 * (C) 1999 Antti Koivisto ([email protected])
7 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov ([email protected])
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#include "Document.h"
29#include "HitTestResult.h"
30#include "HTMLNames.h"
31#include "PaintInfo.h"
32#include "RenderChildIterator.h"
33#include "RenderLayoutState.h"
34#include "RenderTableCell.h"
35#include "RenderTableCol.h"
36#include "RenderTableRow.h"
37#include "RenderTextControl.h"
38#include "RenderTreeBuilder.h"
39#include "RenderView.h"
40#include "StyleInheritedData.h"
41#include <limits>
42#include <wtf/IsoMallocInlines.h>
43#include <wtf/HashSet.h>
44#include <wtf/StackStats.h>
45
46namespace WebCore {
47
48using namespace HTMLNames;
49
50WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTableSection);
51
52// Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
53static const unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
54static const float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
55
56static inline void setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(RenderTableSection::RowStruct& row)
57{
58 ASSERT(row.rowRenderer);
59 row.logicalHeight = row.rowRenderer->style().logicalHeight();
60 if (row.logicalHeight.isRelative())
61 row.logicalHeight = Length();
62}
63
64static inline void updateLogicalHeightForCell(RenderTableSection::RowStruct& row, const RenderTableCell* cell)
65{
66 // We ignore height settings on rowspan cells.
67 if (cell->rowSpan() != 1)
68 return;
69
70 Length logicalHeight = cell->style().logicalHeight();
71 if (logicalHeight.isPositive() || (logicalHeight.isRelative() && logicalHeight.value() >= 0)) {
72 Length cRowLogicalHeight = row.logicalHeight;
73 switch (logicalHeight.type()) {
74 case LengthType::Percent:
75 if (!cRowLogicalHeight.isPercent() || cRowLogicalHeight.percent() < logicalHeight.percent())
76 row.logicalHeight = logicalHeight;
77 break;
78 case LengthType::Fixed:
79 if (cRowLogicalHeight.isAuto() || cRowLogicalHeight.isRelative()
80 || (cRowLogicalHeight.isFixed() && cRowLogicalHeight.value() < logicalHeight.value()))
81 row.logicalHeight = logicalHeight;
82 break;
83 case LengthType::Relative:
84 default:
85 break;
86 }
87 }
88}
89
90RenderTableSection::RenderTableSection(Element& element, RenderStyle&& style)
91 : RenderBox(element, WTFMove(style), 0)
92{
93 setInline(false);
94}
95
96RenderTableSection::RenderTableSection(Document& document, RenderStyle&& style)
97 : RenderBox(document, WTFMove(style), 0)
98{
99 setInline(false);
100}
101
102RenderTableSection::~RenderTableSection() = default;
103
104void RenderTableSection::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
105{
106 RenderBox::styleDidChange(diff, oldStyle);
107 propagateStyleToAnonymousChildren(PropagateToAllChildren);
108
109 // If border was changed, notify table.
110 RenderTable* table = this->table();
111 if (table && oldStyle && oldStyle->border() != style().border())
112 table->invalidateCollapsedBorders();
113}
114
115void RenderTableSection::willBeRemovedFromTree(IsInternalMove isInternalMove)
116{
117 RenderBox::willBeRemovedFromTree(isInternalMove);
118
119 // Preventively invalidate our cells as we may be re-inserted into
120 // a new table which would require us to rebuild our structure.
121 setNeedsCellRecalc();
122}
123
124void RenderTableSection::willInsertTableRow(RenderTableRow& child, RenderObject* beforeChild)
125{
126 if (beforeChild)
127 setNeedsCellRecalc();
128
129 unsigned insertionRow = m_cRow;
130 ++m_cRow;
131 m_cCol = 0;
132
133 ensureRows(m_cRow);
134
135 m_grid[insertionRow].rowRenderer = &child;
136 child.setRowIndex(insertionRow);
137
138 if (!beforeChild)
139 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]);
140}
141
142void RenderTableSection::ensureRows(unsigned numRows)
143{
144 if (numRows <= m_grid.size())
145 return;
146
147 unsigned oldSize = m_grid.size();
148 m_grid.grow(numRows);
149
150 unsigned effectiveColumnCount = std::max(1u, table()->numEffCols());
151 for (unsigned row = oldSize; row < m_grid.size(); ++row)
152 m_grid[row].row.resizeToFit(effectiveColumnCount);
153}
154
155void RenderTableSection::addCell(RenderTableCell* cell, RenderTableRow* row)
156{
157 // We don't insert the cell if we need cell recalc as our internal columns' representation
158 // will have drifted from the table's representation. Also recalcCells will call addCell
159 // at a later time after sync'ing our columns' with the table's.
160 if (needsCellRecalc())
161 return;
162
163 unsigned rSpan = cell->rowSpan();
164 unsigned cSpan = cell->colSpan();
165 const Vector<RenderTable::ColumnStruct>& columns = table()->columns();
166 unsigned nCols = columns.size();
167 unsigned insertionRow = row->rowIndex();
168
169 // ### mozilla still seems to do the old HTML way, even for strict DTD
170 // (see the annotation on table cell layouting in the CSS specs and the testcase below:
171 // <TABLE border>
172 // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4
173 // <TR><TD colspan="2">5
174 // </TABLE>
175 while (m_cCol < nCols && (cellAt(insertionRow, m_cCol).hasCells() || cellAt(insertionRow, m_cCol).inColSpan))
176 m_cCol++;
177
178 updateLogicalHeightForCell(m_grid[insertionRow], cell);
179
180 ensureRows(insertionRow + rSpan);
181
182 m_grid[insertionRow].rowRenderer = row;
183
184 unsigned col = m_cCol;
185 // tell the cell where it is
186 bool inColSpan = false;
187 while (cSpan) {
188 unsigned currentSpan;
189 if (m_cCol >= nCols) {
190 table()->appendColumn(cSpan);
191 currentSpan = cSpan;
192 } else {
193 if (cSpan < columns[m_cCol].span)
194 table()->splitColumn(m_cCol, cSpan);
195 currentSpan = columns[m_cCol].span;
196 }
197 for (unsigned r = 0; r < rSpan; r++) {
198 CellStruct& c = cellAt(insertionRow + r, m_cCol);
199 ASSERT(cell);
200 c.cells.append(cell);
201 // If cells overlap then we take the slow path for painting.
202 if (c.cells.size() > 1)
203 m_hasMultipleCellLevels = true;
204 if (inColSpan)
205 c.inColSpan = true;
206 }
207 m_cCol++;
208 cSpan -= currentSpan;
209 inColSpan = true;
210 }
211 cell->setCol(table()->effColToCol(col));
212}
213
214static LayoutUnit resolveLogicalHeightForRow(const Length& rowLogicalHeight)
215{
216 if (rowLogicalHeight.isFixed())
217 return LayoutUnit(rowLogicalHeight.value());
218 if (rowLogicalHeight.isCalculated())
219 return LayoutUnit(rowLogicalHeight.nonNanCalculatedValue(0));
220 return 0;
221}
222
223LayoutUnit RenderTableSection::calcRowLogicalHeight()
224{
225 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
226
227 ASSERT(!needsLayout());
228
229 RenderTableCell* cell;
230
231 // We ignore the border-spacing on any non-top section as it is already included in the previous section's last row position.
232 LayoutUnit spacing;
233 if (this == table()->topSection())
234 spacing = table()->vBorderSpacing();
235
236 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
237
238 m_rowPos.resize(m_grid.size() + 1);
239 m_rowPos[0] = spacing;
240
241 unsigned totalRows = m_grid.size();
242
243 for (unsigned r = 0; r < totalRows; r++) {
244 m_grid[r].baseline = 0;
245 LayoutUnit baselineDescent;
246
247 // Our base size is the biggest logical height from our cells' styles (excluding row spanning cells).
248 m_rowPos[r + 1] = std::max(m_rowPos[r] + resolveLogicalHeightForRow(m_grid[r].logicalHeight), 0_lu);
249
250 Row& row = m_grid[r].row;
251 unsigned totalCols = row.size();
252
253 for (unsigned c = 0; c < totalCols; c++) {
254 CellStruct& current = cellAt(r, c);
255 for (unsigned i = 0; i < current.cells.size(); i++) {
256 cell = current.cells[i];
257 if (current.inColSpan && cell->rowSpan() == 1)
258 continue;
259
260 // FIXME: We are always adding the height of a rowspan to the last rows which doesn't match
261 // other browsers. See webkit.org/b/52185 for example.
262 if ((cell->rowIndex() + cell->rowSpan() - 1) != r) {
263 // We will apply the height of the rowspan to the current row if next row is not valid.
264 if ((r + 1) < totalRows) {
265 unsigned col = 0;
266 CellStruct nextRowCell = cellAt(r + 1, col);
267
268 // We are trying to find that next row is valid or not.
269 while (nextRowCell.cells.size() && nextRowCell.cells[0]->rowSpan() > 1 && nextRowCell.cells[0]->rowIndex() < (r + 1)) {
270 col++;
271 if (col < totalCols)
272 nextRowCell = cellAt(r + 1, col);
273 else
274 break;
275 }
276
277 // We are adding the height of the rowspan to the current row if next row is not valid.
278 if (col < totalCols && nextRowCell.cells.size())
279 continue;
280 }
281 }
282
283 // For row spanning cells, |r| is the last row in the span.
284 unsigned cellStartRow = cell->rowIndex();
285
286 if (cell->hasOverridingLogicalHeight()) {
287 cell->clearIntrinsicPadding();
288 cell->clearOverridingContentSize();
289 cell->setChildNeedsLayout(MarkOnlyThis);
290 cell->layoutIfNeeded();
291 }
292
293 LayoutUnit cellLogicalHeight = cell->logicalHeightForRowSizing();
294 m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[cellStartRow] + cellLogicalHeight);
295
296 // Find out the baseline. The baseline is set on the first row in a rowspan.
297 if (cell->isBaselineAligned()) {
298 LayoutUnit baselinePosition = cell->cellBaselinePosition() - cell->intrinsicPaddingBefore();
299 LayoutUnit borderAndComputedPaddingBefore = cell->borderAndPaddingBefore() - cell->intrinsicPaddingBefore();
300 if (baselinePosition > borderAndComputedPaddingBefore) {
301 m_grid[cellStartRow].baseline = std::max(m_grid[cellStartRow].baseline, baselinePosition);
302 // The descent of a cell that spans multiple rows does not affect the height of the first row it spans, so don't let it
303 // become the baseline descent applied to the rest of the row. Also we don't account for the baseline descent of
304 // non-spanning cells when computing a spanning cell's extent.
305 LayoutUnit cellStartRowBaselineDescent;
306 if (cell->rowSpan() == 1) {
307 baselineDescent = std::max(baselineDescent, cellLogicalHeight - baselinePosition);
308 cellStartRowBaselineDescent = baselineDescent;
309 }
310 m_rowPos[cellStartRow + 1] = std::max(m_rowPos[cellStartRow + 1], m_rowPos[cellStartRow] + m_grid[cellStartRow].baseline + cellStartRowBaselineDescent);
311 }
312 }
313 }
314 }
315
316 // Add the border-spacing to our final position.
317 // Use table border-spacing even in non-top sections
318 spacing = table()->vBorderSpacing();
319 m_rowPos[r + 1] += m_grid[r].rowRenderer ? spacing : 0_lu;
320 m_rowPos[r + 1] = std::max(m_rowPos[r + 1], m_rowPos[r]);
321 }
322
323 ASSERT(!needsLayout());
324
325 return m_rowPos[m_grid.size()];
326}
327
328void RenderTableSection::layout()
329{
330 StackStats::LayoutCheckPoint layoutCheckPoint;
331 ASSERT(needsLayout());
332 ASSERT(!needsCellRecalc());
333 ASSERT(!table()->needsSectionRecalc());
334
335 m_forceSlowPaintPathWithOverflowingCell = false;
336 // addChild may over-grow m_grid but we don't want to throw away the memory too early as addChild
337 // can be called in a loop (e.g during parsing). Doing it now ensures we have a stable-enough structure.
338 m_grid.shrinkToFit();
339
340 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
341 bool paginated = view().frameView().layoutContext().layoutState()->isPaginated();
342
343 const Vector<LayoutUnit>& columnPos = table()->columnPositions();
344
345 for (unsigned r = 0; r < m_grid.size(); ++r) {
346 Row& row = m_grid[r].row;
347 unsigned cols = row.size();
348 // First, propagate our table layout's information to the cells. This will mark the row as needing layout
349 // if there was a column logical width change.
350 for (unsigned startColumn = 0; startColumn < cols; ++startColumn) {
351 CellStruct& current = row[startColumn];
352 RenderTableCell* cell = current.primaryCell();
353 if (!cell || current.inColSpan)
354 continue;
355
356 unsigned endCol = startColumn;
357 unsigned cspan = cell->colSpan();
358 while (cspan && endCol < cols) {
359 ASSERT(endCol < table()->columns().size());
360 cspan -= table()->columns()[endCol].span;
361 endCol++;
362 }
363 LayoutUnit tableLayoutLogicalWidth = columnPos[endCol] - columnPos[startColumn] - table()->hBorderSpacing();
364 cell->setCellLogicalWidth(tableLayoutLogicalWidth);
365 }
366
367 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
368 if (!rowRenderer->needsLayout() && paginated && view().frameView().layoutContext().layoutState()->pageLogicalHeightChanged())
369 rowRenderer->setChildNeedsLayout(MarkOnlyThis);
370
371 rowRenderer->layoutIfNeeded();
372 }
373 }
374 clearNeedsLayout();
375}
376
377void RenderTableSection::distributeExtraLogicalHeightToPercentRows(LayoutUnit& extraLogicalHeight, int totalPercent)
378{
379 if (!totalPercent)
380 return;
381
382 unsigned totalRows = m_grid.size();
383 LayoutUnit totalHeight = m_rowPos[totalRows] + extraLogicalHeight;
384 LayoutUnit totalLogicalHeightAdded;
385 totalPercent = std::min(totalPercent, 100);
386 LayoutUnit rowHeight = m_rowPos[1] - m_rowPos[0];
387 for (unsigned r = 0; r < totalRows; ++r) {
388 if (totalPercent > 0 && m_grid[r].logicalHeight.isPercent()) {
389 LayoutUnit toAdd = std::min(extraLogicalHeight, LayoutUnit((totalHeight * m_grid[r].logicalHeight.percent() / 100) - rowHeight));
390 // If toAdd is negative, then we don't want to shrink the row (this bug
391 // affected Outlook Web Access).
392 toAdd = std::max(0_lu, toAdd);
393 totalLogicalHeightAdded += toAdd;
394 extraLogicalHeight -= toAdd;
395 totalPercent -= m_grid[r].logicalHeight.percent();
396 }
397 ASSERT(totalRows >= 1);
398 if (r < totalRows - 1)
399 rowHeight = m_rowPos[r + 2] - m_rowPos[r + 1];
400 m_rowPos[r + 1] += totalLogicalHeightAdded;
401 }
402}
403
404void RenderTableSection::distributeExtraLogicalHeightToAutoRows(LayoutUnit& extraLogicalHeight, unsigned autoRowsCount)
405{
406 if (!autoRowsCount)
407 return;
408
409 LayoutUnit totalLogicalHeightAdded;
410 for (unsigned r = 0; r < m_grid.size(); ++r) {
411 if (autoRowsCount > 0 && m_grid[r].logicalHeight.isAuto()) {
412 // Recomputing |extraLogicalHeightForRow| guarantees that we properly ditribute round |extraLogicalHeight|.
413 LayoutUnit extraLogicalHeightForRow = extraLogicalHeight / autoRowsCount;
414 totalLogicalHeightAdded += extraLogicalHeightForRow;
415 extraLogicalHeight -= extraLogicalHeightForRow;
416 --autoRowsCount;
417 }
418 m_rowPos[r + 1] += totalLogicalHeightAdded;
419 }
420}
421
422void RenderTableSection::distributeRemainingExtraLogicalHeight(LayoutUnit& extraLogicalHeight)
423{
424 unsigned totalRows = m_grid.size();
425
426 if (extraLogicalHeight <= 0 || !m_rowPos[totalRows])
427 return;
428
429 // FIXME: m_rowPos[totalRows] - m_rowPos[0] is the total rows' size.
430 LayoutUnit totalRowSize = m_rowPos[totalRows];
431 LayoutUnit totalLogicalHeightAdded;
432 LayoutUnit previousRowPosition = m_rowPos[0];
433 for (unsigned r = 0; r < totalRows; r++) {
434 // weight with the original height
435 totalLogicalHeightAdded += extraLogicalHeight * (m_rowPos[r + 1] - previousRowPosition) / totalRowSize;
436 previousRowPosition = m_rowPos[r + 1];
437 m_rowPos[r + 1] += totalLogicalHeightAdded;
438 }
439
440 extraLogicalHeight -= totalLogicalHeightAdded;
441}
442
443LayoutUnit RenderTableSection::distributeExtraLogicalHeightToRows(LayoutUnit extraLogicalHeight)
444{
445 if (!extraLogicalHeight)
446 return extraLogicalHeight;
447
448 unsigned totalRows = m_grid.size();
449 if (!totalRows)
450 return extraLogicalHeight;
451
452 if (!m_rowPos[totalRows] && nextSibling())
453 return extraLogicalHeight;
454
455 unsigned autoRowsCount = 0;
456 int totalPercent = 0;
457 for (unsigned r = 0; r < totalRows; r++) {
458 if (m_grid[r].logicalHeight.isAuto())
459 ++autoRowsCount;
460 else if (m_grid[r].logicalHeight.isPercent())
461 totalPercent += m_grid[r].logicalHeight.percent();
462 }
463
464 LayoutUnit remainingExtraLogicalHeight = extraLogicalHeight;
465 distributeExtraLogicalHeightToPercentRows(remainingExtraLogicalHeight, totalPercent);
466 distributeExtraLogicalHeightToAutoRows(remainingExtraLogicalHeight, autoRowsCount);
467 distributeRemainingExtraLogicalHeight(remainingExtraLogicalHeight);
468 return extraLogicalHeight - remainingExtraLogicalHeight;
469}
470
471static bool shouldFlexCellChild(const RenderTableCell& cell, const RenderBox& cellDescendant)
472{
473 if (!cell.style().logicalHeight().isSpecified())
474 return false;
475 if (cellDescendant.scrollsOverflowY())
476 return true;
477 return cellDescendant.shouldTreatChildAsReplacedInTableCells();
478}
479
480void RenderTableSection::relayoutCellIfFlexed(RenderTableCell& cell, int rowIndex, int rowHeight)
481{
482 // Force percent height children to lay themselves out again.
483 // This will cause these children to grow to fill the cell.
484 // FIXME: There is still more work to do here to fully match WinIE (should
485 // it become necessary to do so). In quirks mode, WinIE behaves like we
486 // do, but it will clip the cells that spill out of the table section. In
487 // strict mode, Mozilla and WinIE both regrow the table to accommodate the
488 // new height of the cell (thus letting the percentages cause growth one
489 // time only). We may also not be handling row-spanning cells correctly.
490 //
491 // Note also the oddity where replaced elements always flex, and yet blocks/tables do
492 // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to
493 // match the behavior perfectly, but we'll continue to refine it as we discover new
494 // bugs. :)
495 bool cellChildrenFlex = false;
496 bool flexAllChildren = cell.style().logicalHeight().isFixed() || (!table()->style().logicalHeight().isAuto() && rowHeight != cell.logicalHeight());
497
498 for (auto& renderer : childrenOfType<RenderBox>(cell)) {
499 if (renderer.style().logicalHeight().isPercentOrCalculated()
500 && (flexAllChildren || shouldFlexCellChild(cell, renderer))
501 && (!is<RenderTable>(renderer) || downcast<RenderTable>(renderer).hasSections())) {
502 cellChildrenFlex = true;
503 break;
504 }
505 }
506
507 if (!cellChildrenFlex) {
508 if (TrackedRendererListHashSet* percentHeightDescendants = cell.percentHeightDescendants()) {
509 for (auto* descendant : *percentHeightDescendants) {
510 if (flexAllChildren || shouldFlexCellChild(cell, *descendant)) {
511 cellChildrenFlex = true;
512 break;
513 }
514 }
515 }
516 }
517
518 if (!cellChildrenFlex)
519 return;
520
521 cell.setChildNeedsLayout(MarkOnlyThis);
522 // Alignment within a cell is based off the calculated
523 // height, which becomes irrelevant once the cell has
524 // been resized based off its percentage.
525 cell.setOverridingLogicalHeightFromRowHeight(rowHeight);
526 cell.layoutIfNeeded();
527
528 if (!cell.isBaselineAligned())
529 return;
530
531 // If the baseline moved, we may have to update the data for our row. Find out the new baseline.
532 LayoutUnit baseline = cell.cellBaselinePosition();
533 if (baseline > cell.borderAndPaddingBefore())
534 m_grid[rowIndex].baseline = std::max(m_grid[rowIndex].baseline, baseline);
535}
536
537void RenderTableSection::layoutRows()
538{
539 SetLayoutNeededForbiddenScope layoutForbiddenScope(*this);
540
541 ASSERT(!needsLayout());
542
543 unsigned totalRows = m_grid.size();
544
545 // Set the width of our section now. The rows will also be this width.
546 setLogicalWidth(table()->contentLogicalWidth());
547 m_forceSlowPaintPathWithOverflowingCell = false;
548
549 LayoutUnit vspacing = table()->vBorderSpacing();
550 unsigned nEffCols = table()->numEffCols();
551
552 LayoutStateMaintainer statePusher(*this, locationOffset(), hasTransform() || style().isFlippedBlocksWritingMode());
553
554 for (unsigned r = 0; r < totalRows; r++) {
555 // Set the row's x/y position and width/height.
556 if (RenderTableRow* rowRenderer = m_grid[r].rowRenderer) {
557 // FIXME: the x() position of the row should be table()->hBorderSpacing() so that it can
558 // report the correct offsetLeft. However, that will require a lot of rebaselining of test results.
559 rowRenderer->setLocation(LayoutPoint(0_lu, m_rowPos[r]));
560 rowRenderer->setLogicalWidth(logicalWidth());
561 rowRenderer->setLogicalHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing);
562 rowRenderer->updateLayerTransform();
563 rowRenderer->clearOverflow();
564 rowRenderer->addVisualEffectOverflow();
565 }
566
567 LayoutUnit rowHeightIncreaseForPagination;
568
569 for (unsigned c = 0; c < nEffCols; c++) {
570 CellStruct& cs = cellAt(r, c);
571 RenderTableCell* cell = cs.primaryCell();
572
573 if (!cell || cs.inColSpan)
574 continue;
575
576 int rowIndex = cell->rowIndex();
577 LayoutUnit rHeight = m_rowPos[rowIndex + cell->rowSpan()] - m_rowPos[rowIndex] - vspacing;
578
579 relayoutCellIfFlexed(*cell, r, rHeight);
580
581 cell->computeIntrinsicPadding(rHeight);
582
583 LayoutRect oldCellRect = cell->frameRect();
584
585 setLogicalPositionForCell(cell, c);
586
587 auto* layoutState = view().frameView().layoutContext().layoutState();
588 if (!cell->needsLayout() && layoutState->pageLogicalHeight() && layoutState->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())
589 cell->setChildNeedsLayout(MarkOnlyThis);
590
591 cell->layoutIfNeeded();
592
593 // FIXME: Make pagination work with vertical tables.
594 if (layoutState->pageLogicalHeight() && cell->logicalHeight() != rHeight) {
595 // FIXME: Pagination might have made us change size. For now just shrink or grow the cell to fit without doing a relayout.
596 // We'll also do a basic increase of the row height to accommodate the cell if it's bigger, but this isn't quite right
597 // either. It's at least stable though and won't result in an infinite # of relayouts that may never stabilize.
598 if (cell->logicalHeight() > rHeight)
599 rowHeightIncreaseForPagination = std::max(rowHeightIncreaseForPagination, cell->logicalHeight() - rHeight);
600 cell->setLogicalHeight(rHeight);
601 }
602
603 LayoutSize childOffset(cell->location() - oldCellRect.location());
604 if (childOffset.width() || childOffset.height()) {
605 view().frameView().layoutContext().addLayoutDelta(childOffset);
606
607 // If the child moved, we have to repaint it as well as any floating/positioned
608 // descendants. An exception is if we need a layout. In this case, we know we're going to
609 // repaint ourselves (and the child) anyway.
610 if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout())
611 cell->repaintDuringLayoutIfMoved(oldCellRect);
612 }
613 }
614 if (rowHeightIncreaseForPagination) {
615 for (unsigned rowIndex = r + 1; rowIndex <= totalRows; rowIndex++)
616 m_rowPos[rowIndex] += rowHeightIncreaseForPagination;
617 for (unsigned c = 0; c < nEffCols; ++c) {
618 Vector<RenderTableCell*, 1>& cells = cellAt(r, c).cells;
619 for (size_t i = 0; i < cells.size(); ++i)
620 cells[i]->setLogicalHeight(cells[i]->logicalHeight() + rowHeightIncreaseForPagination);
621 }
622 }
623 }
624
625 ASSERT(!needsLayout());
626
627 setLogicalHeight(m_rowPos[totalRows]);
628
629 computeOverflowFromCells(totalRows, nEffCols);
630}
631
632void RenderTableSection::computeOverflowFromCells()
633{
634 unsigned totalRows = m_grid.size();
635 unsigned nEffCols = table()->numEffCols();
636 computeOverflowFromCells(totalRows, nEffCols);
637}
638
639void RenderTableSection::computeOverflowFromCells(unsigned totalRows, unsigned nEffCols)
640{
641 clearOverflow();
642 m_overflowingCells.clear();
643 unsigned totalCellsCount = nEffCols * totalRows;
644 unsigned maxAllowedOverflowingCellsCount = totalCellsCount < gMinTableSizeToUseFastPaintPathWithOverflowingCell ? 0 : gMaxAllowedOverflowingCellRatioForFastPaintPath * totalCellsCount;
645
646#if ASSERT_ENABLED
647 bool hasOverflowingCell = false;
648#endif
649 // Now that our height has been determined, add in overflow from cells.
650 for (unsigned r = 0; r < totalRows; r++) {
651 for (unsigned c = 0; c < nEffCols; c++) {
652 CellStruct& cs = cellAt(r, c);
653 RenderTableCell* cell = cs.primaryCell();
654 if (!cell || cs.inColSpan)
655 continue;
656 if (r < totalRows - 1 && cell == primaryCellAt(r + 1, c))
657 continue;
658 addOverflowFromChild(cell);
659#if ASSERT_ENABLED
660 hasOverflowingCell |= cell->hasVisualOverflow();
661#endif
662 if (cell->hasVisualOverflow() && !m_forceSlowPaintPathWithOverflowingCell) {
663 m_overflowingCells.add(cell);
664 if (m_overflowingCells.size() > maxAllowedOverflowingCellsCount) {
665 // We need to set m_forcesSlowPaintPath only if there is a least one overflowing cells as the hit testing code rely on this information.
666 m_forceSlowPaintPathWithOverflowingCell = true;
667 // The slow path does not make any use of the overflowing cells info, don't hold on to the memory.
668 m_overflowingCells.clear();
669 }
670 }
671 }
672 }
673 ASSERT(hasOverflowingCell == this->hasOverflowingCell());
674}
675
676LayoutUnit RenderTableSection::calcOuterBorderBefore() const
677{
678 unsigned totalCols = table()->numEffCols();
679 if (!m_grid.size() || !totalCols)
680 return 0;
681
682 LayoutUnit borderWidth;
683
684 const BorderValue& sb = style().borderBefore();
685 if (sb.style() == BorderStyle::Hidden)
686 return -1;
687 if (sb.style() > BorderStyle::Hidden)
688 borderWidth = sb.width();
689
690 const BorderValue& rb = firstRow()->style().borderBefore();
691 if (rb.style() == BorderStyle::Hidden)
692 return -1;
693 if (rb.style() > BorderStyle::Hidden && rb.width() > borderWidth)
694 borderWidth = rb.width();
695
696 bool allHidden = true;
697 for (unsigned c = 0; c < totalCols; c++) {
698 const CellStruct& current = cellAt(0, c);
699 if (current.inColSpan || !current.hasCells())
700 continue;
701 const BorderValue& cb = current.primaryCell()->style().borderBefore(); // FIXME: Make this work with perpendicular and flipped cells.
702 // FIXME: Don't repeat for the same col group
703 RenderTableCol* colGroup = table()->colElement(c);
704 if (colGroup) {
705 const BorderValue& gb = colGroup->style().borderBefore();
706 if (gb.style() == BorderStyle::Hidden || cb.style() == BorderStyle::Hidden)
707 continue;
708 allHidden = false;
709 if (gb.style() > BorderStyle::Hidden && gb.width() > borderWidth)
710 borderWidth = gb.width();
711 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
712 borderWidth = cb.width();
713 } else {
714 if (cb.style() == BorderStyle::Hidden)
715 continue;
716 allHidden = false;
717 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
718 borderWidth = cb.width();
719 }
720 }
721 if (allHidden)
722 return -1;
723 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), false);
724}
725
726LayoutUnit RenderTableSection::calcOuterBorderAfter() const
727{
728 unsigned totalCols = table()->numEffCols();
729 if (!m_grid.size() || !totalCols)
730 return 0;
731
732 LayoutUnit borderWidth;
733
734 const BorderValue& sb = style().borderAfter();
735 if (sb.style() == BorderStyle::Hidden)
736 return -1;
737 if (sb.style() > BorderStyle::Hidden)
738 borderWidth = sb.width();
739
740 const BorderValue& rb = lastRow()->style().borderAfter();
741 if (rb.style() == BorderStyle::Hidden)
742 return -1;
743 if (rb.style() > BorderStyle::Hidden && rb.width() > borderWidth)
744 borderWidth = rb.width();
745
746 bool allHidden = true;
747 for (unsigned c = 0; c < totalCols; c++) {
748 const CellStruct& current = cellAt(m_grid.size() - 1, c);
749 if (current.inColSpan || !current.hasCells())
750 continue;
751 const BorderValue& cb = current.primaryCell()->style().borderAfter(); // FIXME: Make this work with perpendicular and flipped cells.
752 // FIXME: Don't repeat for the same col group
753 RenderTableCol* colGroup = table()->colElement(c);
754 if (colGroup) {
755 const BorderValue& gb = colGroup->style().borderAfter();
756 if (gb.style() == BorderStyle::Hidden || cb.style() == BorderStyle::Hidden)
757 continue;
758 allHidden = false;
759 if (gb.style() > BorderStyle::Hidden && gb.width() > borderWidth)
760 borderWidth = gb.width();
761 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
762 borderWidth = cb.width();
763 } else {
764 if (cb.style() == BorderStyle::Hidden)
765 continue;
766 allHidden = false;
767 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
768 borderWidth = cb.width();
769 }
770 }
771 if (allHidden)
772 return -1;
773 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), true);
774}
775
776LayoutUnit RenderTableSection::calcOuterBorderStart() const
777{
778 unsigned totalCols = table()->numEffCols();
779 if (!m_grid.size() || !totalCols)
780 return 0;
781
782 LayoutUnit borderWidth;
783
784 const BorderValue& sb = style().borderStart();
785 if (sb.style() == BorderStyle::Hidden)
786 return -1;
787 if (sb.style() > BorderStyle::Hidden)
788 borderWidth = sb.width();
789
790 if (RenderTableCol* colGroup = table()->colElement(0)) {
791 const BorderValue& gb = colGroup->style().borderStart();
792 if (gb.style() == BorderStyle::Hidden)
793 return -1;
794 if (gb.style() > BorderStyle::Hidden && gb.width() > borderWidth)
795 borderWidth = gb.width();
796 }
797
798 bool allHidden = true;
799 for (unsigned r = 0; r < m_grid.size(); r++) {
800 const CellStruct& current = cellAt(r, 0);
801 if (!current.hasCells())
802 continue;
803 // FIXME: Don't repeat for the same cell
804 const BorderValue& cb = current.primaryCell()->style().borderStart(); // FIXME: Make this work with perpendicular and flipped cells.
805 const BorderValue& rb = current.primaryCell()->parent()->style().borderStart();
806 if (cb.style() == BorderStyle::Hidden || rb.style() == BorderStyle::Hidden)
807 continue;
808 allHidden = false;
809 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
810 borderWidth = cb.width();
811 if (rb.style() > BorderStyle::Hidden && rb.width() > borderWidth)
812 borderWidth = rb.width();
813 }
814 if (allHidden)
815 return -1;
816 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), !table()->style().isLeftToRightDirection());
817}
818
819LayoutUnit RenderTableSection::calcOuterBorderEnd() const
820{
821 unsigned totalCols = table()->numEffCols();
822 if (!m_grid.size() || !totalCols)
823 return 0;
824
825 LayoutUnit borderWidth;
826
827 const BorderValue& sb = style().borderEnd();
828 if (sb.style() == BorderStyle::Hidden)
829 return -1;
830 if (sb.style() > BorderStyle::Hidden)
831 borderWidth = sb.width();
832
833 if (RenderTableCol* colGroup = table()->colElement(totalCols - 1)) {
834 const BorderValue& gb = colGroup->style().borderEnd();
835 if (gb.style() == BorderStyle::Hidden)
836 return -1;
837 if (gb.style() > BorderStyle::Hidden && gb.width() > borderWidth)
838 borderWidth = gb.width();
839 }
840
841 bool allHidden = true;
842 for (unsigned r = 0; r < m_grid.size(); r++) {
843 const CellStruct& current = cellAt(r, totalCols - 1);
844 if (!current.hasCells())
845 continue;
846 // FIXME: Don't repeat for the same cell
847 const BorderValue& cb = current.primaryCell()->style().borderEnd(); // FIXME: Make this work with perpendicular and flipped cells.
848 const BorderValue& rb = current.primaryCell()->parent()->style().borderEnd();
849 if (cb.style() == BorderStyle::Hidden || rb.style() == BorderStyle::Hidden)
850 continue;
851 allHidden = false;
852 if (cb.style() > BorderStyle::Hidden && cb.width() > borderWidth)
853 borderWidth = cb.width();
854 if (rb.style() > BorderStyle::Hidden && rb.width() > borderWidth)
855 borderWidth = rb.width();
856 }
857 if (allHidden)
858 return -1;
859 return CollapsedBorderValue::adjustedCollapsedBorderWidth(borderWidth, document().deviceScaleFactor(), table()->style().isLeftToRightDirection());
860}
861
862void RenderTableSection::recalcOuterBorder()
863{
864 m_outerBorderBefore = calcOuterBorderBefore();
865 m_outerBorderAfter = calcOuterBorderAfter();
866 m_outerBorderStart = calcOuterBorderStart();
867 m_outerBorderEnd = calcOuterBorderEnd();
868}
869
870std::optional<LayoutUnit> RenderTableSection::firstLineBaseline() const
871{
872 if (!m_grid.size())
873 return std::optional<LayoutUnit>();
874
875 LayoutUnit firstLineBaseline = m_grid[0].baseline;
876 if (firstLineBaseline)
877 return firstLineBaseline + m_rowPos[0];
878
879 std::optional<LayoutUnit> result;
880 const Row& firstRow = m_grid[0].row;
881 for (size_t i = 0; i < firstRow.size(); ++i) {
882 const CellStruct& cs = firstRow.at(i);
883 const RenderTableCell* cell = cs.primaryCell();
884 // Only cells with content have a baseline
885 if (cell && cell->contentLogicalHeight()) {
886 LayoutUnit candidate = cell->logicalTop() + cell->borderAndPaddingBefore() + cell->contentLogicalHeight();
887 result = std::max(result.value_or(candidate), candidate);
888 }
889 }
890
891 return result;
892}
893
894void RenderTableSection::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
895{
896 ASSERT(!needsLayout());
897 // avoid crashing on bugs that cause us to paint with dirty layout
898 if (needsLayout())
899 return;
900
901 unsigned totalRows = m_grid.size();
902 unsigned totalCols = table()->columns().size();
903
904 if (!totalRows || !totalCols)
905 return;
906
907 LayoutPoint adjustedPaintOffset = paintOffset + location();
908
909 PaintPhase phase = paintInfo.phase;
910 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset);
911 paintObject(paintInfo, adjustedPaintOffset);
912 if (pushedClip)
913 popContentsClip(paintInfo, phase, adjustedPaintOffset);
914
915 if ((phase == PaintPhase::Outline || phase == PaintPhase::SelfOutline) && style().visibility() == Visibility::Visible)
916 paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
917}
918
919static inline bool compareCellPositions(RenderTableCell* elem1, RenderTableCell* elem2)
920{
921 return elem1->rowIndex() < elem2->rowIndex();
922}
923
924// This comparison is used only when we have overflowing cells as we have an unsorted array to sort. We thus need
925// to sort both on rows and columns to properly repaint.
926static inline bool compareCellPositionsWithOverflowingCells(RenderTableCell* elem1, RenderTableCell* elem2)
927{
928 if (elem1->rowIndex() != elem2->rowIndex())
929 return elem1->rowIndex() < elem2->rowIndex();
930
931 return elem1->col() < elem2->col();
932}
933
934void RenderTableSection::paintCell(RenderTableCell* cell, PaintInfo& paintInfo, const LayoutPoint& paintOffset)
935{
936 LayoutPoint cellPoint = flipForWritingModeForChild(*cell, paintOffset);
937 PaintPhase paintPhase = paintInfo.phase;
938 RenderTableRow& row = downcast<RenderTableRow>(*cell->parent());
939
940 if (paintPhase == PaintPhase::BlockBackground || paintPhase == PaintPhase::ChildBlockBackground) {
941 // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of
942 // the column group, column, row group, row, and then the cell.
943 RenderTableCol* column = table()->colElement(cell->col());
944 RenderTableCol* columnGroup = column ? column->enclosingColumnGroup() : nullptr;
945
946 // Column groups and columns first.
947 // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in
948 // the stack, since we have already opened a transparency layer (potentially) for the table row group.
949 // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the
950 // cell.
951 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup);
952 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, column);
953
954 // Paint the row group next.
955 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, this);
956
957 // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for
958 // painting the row background for the cell.
959 if (!row.hasSelfPaintingLayer())
960 cell->paintBackgroundsBehindCell(paintInfo, cellPoint, &row);
961 }
962 if ((!cell->hasSelfPaintingLayer() && !row.hasSelfPaintingLayer()))
963 cell->paint(paintInfo, cellPoint);
964}
965
966LayoutRect RenderTableSection::logicalRectForWritingModeAndDirection(const LayoutRect& rect) const
967{
968 LayoutRect tableAlignedRect(rect);
969
970 flipForWritingMode(tableAlignedRect);
971
972 if (!style().isHorizontalWritingMode())
973 tableAlignedRect = tableAlignedRect.transposedRect();
974
975 const Vector<LayoutUnit>& columnPos = table()->columnPositions();
976 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
977 if (!style().isLeftToRightDirection())
978 tableAlignedRect.setX(columnPos[columnPos.size() - 1] - tableAlignedRect.maxX());
979
980 return tableAlignedRect;
981}
982
983CellSpan RenderTableSection::dirtiedRows(const LayoutRect& damageRect) const
984{
985 if (m_forceSlowPaintPathWithOverflowingCell)
986 return fullTableRowSpan();
987
988 CellSpan coveredRows = spannedRows(damageRect, IncludeAllIntersectingCells);
989
990 // To repaint the border we might need to repaint first or last row even if they are not spanned themselves.
991 if (coveredRows.start >= m_rowPos.size() - 1 && m_rowPos[m_rowPos.size() - 1] + table()->outerBorderAfter() >= damageRect.y())
992 --coveredRows.start;
993
994 if (!coveredRows.end && m_rowPos[0] - table()->outerBorderBefore() <= damageRect.maxY())
995 ++coveredRows.end;
996
997 return coveredRows;
998}
999
1000CellSpan RenderTableSection::dirtiedColumns(const LayoutRect& damageRect) const
1001{
1002 if (m_forceSlowPaintPathWithOverflowingCell)
1003 return fullTableColumnSpan();
1004
1005 CellSpan coveredColumns = spannedColumns(damageRect, IncludeAllIntersectingCells);
1006
1007 const Vector<LayoutUnit>& columnPos = table()->columnPositions();
1008 // To repaint the border we might need to repaint first or last column even if they are not spanned themselves.
1009 if (coveredColumns.start >= columnPos.size() - 1 && columnPos[columnPos.size() - 1] + table()->outerBorderEnd() >= damageRect.x())
1010 --coveredColumns.start;
1011
1012 if (!coveredColumns.end && columnPos[0] - table()->outerBorderStart() <= damageRect.maxX())
1013 ++coveredColumns.end;
1014
1015 return coveredColumns;
1016}
1017
1018CellSpan RenderTableSection::spannedRows(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
1019{
1020 // Find the first row that starts after rect top.
1021 unsigned nextRow = std::upper_bound(m_rowPos.begin(), m_rowPos.end(), flippedRect.y()) - m_rowPos.begin();
1022 if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextRow && m_rowPos[nextRow - 1] == flippedRect.y())
1023 --nextRow;
1024
1025 if (nextRow == m_rowPos.size())
1026 return CellSpan(m_rowPos.size() - 1, m_rowPos.size() - 1); // After all rows.
1027
1028 unsigned startRow = nextRow > 0 ? nextRow - 1 : 0;
1029
1030 // Find the first row that starts after rect bottom.
1031 unsigned endRow;
1032 if (m_rowPos[nextRow] >= flippedRect.maxY())
1033 endRow = nextRow;
1034 else {
1035 endRow = std::upper_bound(m_rowPos.begin() + static_cast<int32_t>(nextRow), m_rowPos.end(), flippedRect.maxY()) - m_rowPos.begin();
1036 if (endRow == m_rowPos.size())
1037 endRow = m_rowPos.size() - 1;
1038 }
1039
1040 return CellSpan(startRow, endRow);
1041}
1042
1043CellSpan RenderTableSection::spannedColumns(const LayoutRect& flippedRect, ShouldIncludeAllIntersectingCells shouldIncludeAllIntersectionCells) const
1044{
1045 const Vector<LayoutUnit>& columnPos = table()->columnPositions();
1046
1047 // Find the first column that starts after rect left.
1048 // lower_bound doesn't handle the edge between two cells properly as it would wrongly return the
1049 // cell on the logical top/left.
1050 // upper_bound on the other hand properly returns the cell on the logical bottom/right, which also
1051 // matches the behavior of other browsers.
1052 unsigned nextColumn = std::upper_bound(columnPos.begin(), columnPos.end(), flippedRect.x()) - columnPos.begin();
1053 if (shouldIncludeAllIntersectionCells == IncludeAllIntersectingCells && nextColumn && columnPos[nextColumn - 1] == flippedRect.x())
1054 --nextColumn;
1055
1056 if (nextColumn == columnPos.size())
1057 return CellSpan(columnPos.size() - 1, columnPos.size() - 1); // After all columns.
1058
1059 unsigned startColumn = nextColumn > 0 ? nextColumn - 1 : 0;
1060
1061 // Find the first column that starts after rect right.
1062 unsigned endColumn;
1063 if (columnPos[nextColumn] >= flippedRect.maxX())
1064 endColumn = nextColumn;
1065 else {
1066 endColumn = std::upper_bound(columnPos.begin() + static_cast<int32_t>(nextColumn), columnPos.end(), flippedRect.maxX()) - columnPos.begin();
1067 if (endColumn == columnPos.size())
1068 endColumn = columnPos.size() - 1;
1069 }
1070
1071 return CellSpan(startColumn, endColumn);
1072}
1073
1074void RenderTableSection::paintRowGroupBorder(const PaintInfo& paintInfo, bool antialias, LayoutRect rect, BoxSide side, CSSPropertyID borderColor, BorderStyle borderStyle, BorderStyle tableBorderStyle)
1075{
1076 if (tableBorderStyle == BorderStyle::Hidden)
1077 return;
1078 rect.intersect(paintInfo.rect);
1079 if (rect.isEmpty())
1080 return;
1081 drawLineForBoxSide(paintInfo.context(), rect, side, style().visitedDependentColorWithColorFilter(borderColor), borderStyle, 0, 0, antialias);
1082}
1083
1084LayoutUnit RenderTableSection::offsetLeftForRowGroupBorder(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row)
1085{
1086 if (style().isHorizontalWritingMode()) {
1087 if (style().isLeftToRightDirection())
1088 return cell ? cell->x() + cell->width() : 0_lu;
1089 return -outerBorderLeft(&style());
1090 }
1091 bool isLastRow = row + 1 == m_grid.size();
1092 return rowGroupRect.width() - m_rowPos[row + 1] + (isLastRow ? -outerBorderLeft(&style()) : 0_lu);
1093}
1094
1095LayoutUnit RenderTableSection::offsetTopForRowGroupBorder(RenderTableCell* cell, BoxSide borderSide, unsigned row)
1096{
1097 bool isLastRow = row + 1 == m_grid.size();
1098 if (style().isHorizontalWritingMode())
1099 return m_rowPos[row] + (!row && borderSide == BoxSide::Right ? -outerBorderTop(&style()) : isLastRow && borderSide == BoxSide::Left ? outerBorderTop(&style()) : 0_lu);
1100 if (style().isLeftToRightDirection())
1101 return (cell ? cell->y() + cell->height() : 0_lu) + (borderSide == BoxSide::Left ? outerBorderTop(&style()) : 0_lu);
1102 return borderSide == BoxSide::Right ? -outerBorderTop(&style()) : 0_lu;
1103}
1104
1105LayoutUnit RenderTableSection::verticalRowGroupBorderHeight(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row)
1106{
1107 bool isLastRow = row + 1 == m_grid.size();
1108 if (style().isHorizontalWritingMode())
1109 return m_rowPos[row + 1] - m_rowPos[row] + (!row ? outerBorderTop(&style()) : isLastRow ? outerBorderBottom(&style()) : 0_lu);
1110 if (style().isLeftToRightDirection())
1111 return rowGroupRect.height() - (cell ? cell->y() + cell->height() : 0_lu) + outerBorderBottom(&style());
1112 return cell ? rowGroupRect.height() - (cell->y() - cell->height()) : 0_lu;
1113}
1114
1115LayoutUnit RenderTableSection::horizontalRowGroupBorderWidth(RenderTableCell* cell, const LayoutRect& rowGroupRect, unsigned row, unsigned column)
1116{
1117 if (style().isHorizontalWritingMode()) {
1118 if (style().isLeftToRightDirection())
1119 return rowGroupRect.width() - (cell ? cell->x() + cell->width() : 0_lu) + (!column ? outerBorderLeft(&style()) : column == table()->numEffCols() ? outerBorderRight(&style()) : 0_lu);
1120 return cell ? rowGroupRect.width() - (cell->x() - cell->width()) : 0_lu;
1121 }
1122 bool isLastRow = row + 1 == m_grid.size();
1123 return m_rowPos[row + 1] - m_rowPos[row] + (isLastRow ? outerBorderLeft(&style()) : !row ? outerBorderRight(&style()) : 0_lu);
1124}
1125
1126void RenderTableSection::paintRowGroupBorderIfRequired(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, unsigned row, unsigned column, BoxSide borderSide, RenderTableCell* cell)
1127{
1128 if (table()->currentBorderValue()->precedence() > BorderPrecedence::RowGroup)
1129 return;
1130 if (paintInfo.context().paintingDisabled())
1131 return;
1132
1133 const RenderStyle& style = this->style();
1134 bool antialias = shouldAntialiasLines(paintInfo.context());
1135 LayoutRect rowGroupRect = LayoutRect(paintOffset, size());
1136 rowGroupRect.moveBy(-LayoutPoint(outerBorderLeft(&style), (borderSide == BoxSide::Right) ? 0_lu : outerBorderTop(&style)));
1137
1138 switch (borderSide) {
1139 case BoxSide::Top:
1140 paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y(),
1141 horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), LayoutUnit(style.borderTop().width())), BoxSide::Top, CSSPropertyBorderTopColor, style.borderTopStyle(), table()->style().borderTopStyle());
1142 break;
1143 case BoxSide::Bottom:
1144 paintRowGroupBorder(paintInfo, antialias, LayoutRect(paintOffset.x() + offsetLeftForRowGroupBorder(cell, rowGroupRect, row), rowGroupRect.y() + rowGroupRect.height(),
1145 horizontalRowGroupBorderWidth(cell, rowGroupRect, row, column), LayoutUnit(style.borderBottom().width())), BoxSide::Bottom, CSSPropertyBorderBottomColor, style.borderBottomStyle(), table()->style().borderBottomStyle());
1146 break;
1147 case BoxSide::Left:
1148 paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), LayoutUnit(style.borderLeft().width()),
1149 verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BoxSide::Left, CSSPropertyBorderLeftColor, style.borderLeftStyle(), table()->style().borderLeftStyle());
1150 break;
1151 case BoxSide::Right:
1152 paintRowGroupBorder(paintInfo, antialias, LayoutRect(rowGroupRect.x() + rowGroupRect.width(), rowGroupRect.y() + offsetTopForRowGroupBorder(cell, borderSide, row), LayoutUnit(style.borderRight().width()),
1153 verticalRowGroupBorderHeight(cell, rowGroupRect, row)), BoxSide::Right, CSSPropertyBorderRightColor, style.borderRightStyle(), table()->style().borderRightStyle());
1154 break;
1155 default:
1156 break;
1157 }
1158
1159}
1160
1161static BoxSide physicalBorderForDirection(const RenderStyle* styleForCellFlow, CollapsedBorderSide side)
1162{
1163
1164 switch (side) {
1165 case CBSStart:
1166 if (styleForCellFlow->isHorizontalWritingMode())
1167 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Left : BoxSide::Right;
1168 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Top : BoxSide::Bottom;
1169 case CBSEnd:
1170 if (styleForCellFlow->isHorizontalWritingMode())
1171 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Right : BoxSide::Left;
1172 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Bottom : BoxSide::Top;
1173 case CBSBefore:
1174 if (styleForCellFlow->isHorizontalWritingMode())
1175 return BoxSide::Top;
1176 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Right : BoxSide::Left;
1177 case CBSAfter:
1178 if (styleForCellFlow->isHorizontalWritingMode())
1179 return BoxSide::Bottom;
1180 return styleForCellFlow->isLeftToRightDirection() ? BoxSide::Left : BoxSide::Right;
1181 default:
1182 ASSERT_NOT_REACHED();
1183 return BoxSide::Left;
1184 }
1185}
1186
1187void RenderTableSection::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1188{
1189 LayoutRect localRepaintRect = paintInfo.rect;
1190 localRepaintRect.moveBy(-paintOffset);
1191
1192 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(localRepaintRect);
1193
1194 CellSpan dirtiedRows = this->dirtiedRows(tableAlignedRect);
1195 CellSpan dirtiedColumns = this->dirtiedColumns(tableAlignedRect);
1196
1197 if (dirtiedColumns.start < dirtiedColumns.end) {
1198 if (!m_hasMultipleCellLevels && !m_overflowingCells.size()) {
1199 if (paintInfo.phase == PaintPhase::CollapsedTableBorders) {
1200 // Collapsed borders are painted from the bottom right to the top left so that precedence
1201 // due to cell position is respected. We need to paint one row beyond the topmost dirtied
1202 // row to calculate its collapsed border value.
1203 unsigned startRow = dirtiedRows.start ? dirtiedRows.start - 1 : 0;
1204 for (unsigned r = dirtiedRows.end; r > startRow; r--) {
1205 unsigned row = r - 1;
1206 bool shouldPaintRowGroupBorder = false;
1207 for (unsigned c = dirtiedColumns.end; c > dirtiedColumns.start; c--) {
1208 unsigned col = c - 1;
1209 CellStruct& current = cellAt(row, col);
1210 RenderTableCell* cell = current.primaryCell();
1211 if (!cell) {
1212 if (!c)
1213 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSStart));
1214 else if (c == table()->numEffCols())
1215 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSEnd));
1216 shouldPaintRowGroupBorder = true;
1217 continue;
1218 }
1219 if ((row > dirtiedRows.start && primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start && primaryCellAt(row, col - 1) == cell))
1220 continue;
1221
1222 // If we had a run of null cells paint their corresponding section of the row group's border if necessary. Note that
1223 // this will only happen once within a row as the null cells will always be clustered together on one end of the row.
1224 if (shouldPaintRowGroupBorder) {
1225 if (r == m_grid.size())
1226 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSAfter), cell);
1227 else if (!row && !table()->sectionAbove(this))
1228 paintRowGroupBorderIfRequired(paintInfo, paintOffset, row, col, physicalBorderForDirection(&style(), CBSBefore), cell);
1229 shouldPaintRowGroupBorder = false;
1230 }
1231
1232 LayoutPoint cellPoint = flipForWritingModeForChild(*cell, paintOffset);
1233 cell->paintCollapsedBorders(paintInfo, cellPoint);
1234 }
1235 }
1236 } else {
1237 // Draw the dirty cells in the order that they appear.
1238 for (unsigned r = dirtiedRows.start; r < dirtiedRows.end; r++) {
1239 RenderTableRow* row = m_grid[r].rowRenderer;
1240 if (row && !row->hasSelfPaintingLayer())
1241 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1242 for (unsigned c = dirtiedColumns.start; c < dirtiedColumns.end; c++) {
1243 CellStruct& current = cellAt(r, c);
1244 RenderTableCell* cell = current.primaryCell();
1245 if (!cell || (r > dirtiedRows.start && primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start && primaryCellAt(r, c - 1) == cell))
1246 continue;
1247 paintCell(cell, paintInfo, paintOffset);
1248 }
1249 }
1250 }
1251 } else {
1252 // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet.
1253#ifndef NDEBUG
1254 unsigned totalRows = m_grid.size();
1255 unsigned totalCols = table()->columns().size();
1256 ASSERT(m_overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath);
1257#endif
1258
1259 // To make sure we properly repaint the section, we repaint all the overflowing cells that we collected.
1260 auto cells = copyToVector(m_overflowingCells);
1261
1262 HashSet<RenderTableCell*> spanningCells;
1263
1264 for (unsigned r = dirtiedRows.start; r < dirtiedRows.end; r++) {
1265 RenderTableRow* row = m_grid[r].rowRenderer;
1266 if (row && !row->hasSelfPaintingLayer())
1267 row->paintOutlineForRowIfNeeded(paintInfo, paintOffset);
1268 for (unsigned c = dirtiedColumns.start; c < dirtiedColumns.end; c++) {
1269 CellStruct& current = cellAt(r, c);
1270 if (!current.hasCells())
1271 continue;
1272 for (unsigned i = 0; i < current.cells.size(); ++i) {
1273 if (m_overflowingCells.contains(current.cells[i]))
1274 continue;
1275
1276 if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) {
1277 if (!spanningCells.add(current.cells[i]).isNewEntry)
1278 continue;
1279 }
1280
1281 cells.append(current.cells[i]);
1282 }
1283 }
1284 }
1285
1286 // Sort the dirty cells by paint order.
1287 if (!m_overflowingCells.size())
1288 std::stable_sort(cells.begin(), cells.end(), compareCellPositions);
1289 else
1290 std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells);
1291
1292 if (paintInfo.phase == PaintPhase::CollapsedTableBorders) {
1293 for (unsigned i = cells.size(); i > 0; --i) {
1294 LayoutPoint cellPoint = flipForWritingModeForChild(*cells[i - 1], paintOffset);
1295 cells[i - 1]->paintCollapsedBorders(paintInfo, cellPoint);
1296 }
1297 } else {
1298 for (unsigned i = 0; i < cells.size(); ++i)
1299 paintCell(cells[i], paintInfo, paintOffset);
1300 }
1301 }
1302 }
1303}
1304
1305void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
1306{
1307 // FIXME: Examine cells and repaint only the rect the image paints in.
1308 repaint();
1309}
1310
1311void RenderTableSection::recalcCells()
1312{
1313 ASSERT(m_needsCellRecalc);
1314 // We reset the flag here to ensure that addCell() works. This is safe to do because we clear the grid
1315 // and update its dimensions to be consistent with the table's column representation before we rebuild
1316 // the grid using addCell().
1317 m_needsCellRecalc = false;
1318
1319 m_cCol = 0;
1320 m_cRow = 0;
1321 m_grid.clear();
1322
1323 for (RenderTableRow* row = firstRow(); row; row = row->nextRow()) {
1324 unsigned insertionRow = m_cRow;
1325 m_cRow++;
1326 m_cCol = 0;
1327 ensureRows(m_cRow);
1328
1329 m_grid[insertionRow].rowRenderer = row;
1330 row->setRowIndex(insertionRow);
1331 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[insertionRow]);
1332
1333 for (RenderTableCell* cell = row->firstCell(); cell; cell = cell->nextCell())
1334 addCell(cell, row);
1335 }
1336
1337 m_grid.shrinkToFit();
1338 setNeedsLayout();
1339}
1340
1341void RenderTableSection::removeRedundantColumns()
1342{
1343 auto maximumNumberOfColumns = table()->numEffCols();
1344 for (auto& rowItem : m_grid) {
1345 if (rowItem.row.size() <= maximumNumberOfColumns)
1346 continue;
1347 rowItem.row.resize(maximumNumberOfColumns);
1348 }
1349}
1350
1351// FIXME: This function could be made O(1) in certain cases (like for the non-most-constrainive cells' case).
1352void RenderTableSection::rowLogicalHeightChanged(unsigned rowIndex)
1353{
1354 if (needsCellRecalc())
1355 return;
1356
1357 setRowLogicalHeightToRowStyleLogicalHeightIfNotRelative(m_grid[rowIndex]);
1358
1359 for (RenderTableCell* cell = m_grid[rowIndex].rowRenderer->firstCell(); cell; cell = cell->nextCell())
1360 updateLogicalHeightForCell(m_grid[rowIndex], cell);
1361}
1362
1363void RenderTableSection::setNeedsCellRecalc()
1364{
1365 m_needsCellRecalc = true;
1366
1367 // Clear the grid now to ensure that we don't hold onto any stale pointers (e.g. a cell renderer that is being removed).
1368 m_grid.clear();
1369
1370 if (RenderTable* t = table())
1371 t->setNeedsSectionRecalc();
1372}
1373
1374unsigned RenderTableSection::numColumns() const
1375{
1376 ASSERT(!m_needsCellRecalc);
1377 unsigned result = 0;
1378
1379 for (unsigned r = 0; r < m_grid.size(); ++r) {
1380 for (unsigned c = result; c < table()->numEffCols(); ++c) {
1381 const CellStruct& cell = cellAt(r, c);
1382 if (cell.hasCells() || cell.inColSpan)
1383 result = c;
1384 }
1385 }
1386
1387 return result + 1;
1388}
1389
1390const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell& cell) const
1391{
1392 ASSERT(cell.isFirstOrLastCellInRow());
1393 return isDirectionSame(this, &cell) ? style().borderStart() : style().borderEnd();
1394}
1395
1396const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell& cell) const
1397{
1398 ASSERT(cell.isFirstOrLastCellInRow());
1399 return isDirectionSame(this, &cell) ? style().borderEnd() : style().borderStart();
1400}
1401
1402const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
1403{
1404 unsigned adjoiningStartCellColumnIndex = isDirectionSame(this, table()) ? 0 : table()->lastColumnIndex();
1405 return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
1406}
1407
1408const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
1409{
1410 unsigned adjoiningEndCellColumnIndex = isDirectionSame(this, table()) ? table()->lastColumnIndex() : 0;
1411 return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
1412}
1413
1414void RenderTableSection::appendColumn(unsigned pos)
1415{
1416 ASSERT(!m_needsCellRecalc);
1417
1418 for (unsigned row = 0; row < m_grid.size(); ++row)
1419 m_grid[row].row.resize(pos + 1);
1420}
1421
1422void RenderTableSection::splitColumn(unsigned pos, unsigned first)
1423{
1424 ASSERT(!m_needsCellRecalc);
1425
1426 if (m_cCol > pos)
1427 m_cCol++;
1428 for (unsigned row = 0; row < m_grid.size(); ++row) {
1429 Row& r = m_grid[row].row;
1430 r.insert(pos + 1, CellStruct());
1431 if (r[pos].hasCells()) {
1432 r[pos + 1].cells.appendVector(r[pos].cells);
1433 RenderTableCell* cell = r[pos].primaryCell();
1434 ASSERT(cell);
1435 ASSERT(cell->colSpan() >= (r[pos].inColSpan ? 1u : 0));
1436 unsigned colleft = cell->colSpan() - r[pos].inColSpan;
1437 if (first > colleft)
1438 r[pos + 1].inColSpan = 0;
1439 else
1440 r[pos + 1].inColSpan = first + r[pos].inColSpan;
1441 } else {
1442 r[pos + 1].inColSpan = 0;
1443 }
1444 }
1445}
1446
1447// Hit Testing
1448bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1449{
1450 // If we have no children then we have nothing to do.
1451 if (!firstRow())
1452 return false;
1453
1454 // Table sections cannot ever be hit tested. Effectively they do not exist.
1455 // Just forward to our children always.
1456 LayoutPoint adjustedLocation = accumulatedOffset + location();
1457
1458 if (hasNonVisibleOverflow() && !locationInContainer.intersects(overflowClipRect(adjustedLocation, nullptr)))
1459 return false;
1460
1461 if (hasOverflowingCell()) {
1462 for (RenderTableRow* row = lastRow(); row; row = row->previousRow()) {
1463 // FIXME: We have to skip over inline flows, since they can show up inside table rows
1464 // at the moment (a demoted inline <form> for example). If we ever implement a
1465 // table-specific hit-test method (which we should do for performance reasons anyway),
1466 // then we can remove this check.
1467 if (!row->hasSelfPaintingLayer()) {
1468 LayoutPoint childPoint = flipForWritingModeForChild(*row, adjustedLocation);
1469 if (row->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1470 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1471 return true;
1472 }
1473 }
1474 }
1475 return false;
1476 }
1477
1478 recalcCellsIfNeeded();
1479
1480 LayoutRect hitTestRect = locationInContainer.boundingBox();
1481 hitTestRect.moveBy(-adjustedLocation);
1482
1483 LayoutRect tableAlignedRect = logicalRectForWritingModeAndDirection(hitTestRect);
1484 CellSpan rowSpan = spannedRows(tableAlignedRect, DoNotIncludeAllIntersectingCells);
1485 CellSpan columnSpan = spannedColumns(tableAlignedRect, DoNotIncludeAllIntersectingCells);
1486
1487 // Now iterate over the spanned rows and columns.
1488 for (unsigned hitRow = rowSpan.start; hitRow < rowSpan.end; ++hitRow) {
1489 for (unsigned hitColumn = columnSpan.start; hitColumn < columnSpan.end; ++hitColumn) {
1490 CellStruct& current = cellAt(hitRow, hitColumn);
1491
1492 // If the cell is empty, there's nothing to do
1493 if (!current.hasCells())
1494 continue;
1495
1496 for (unsigned i = current.cells.size() ; i; ) {
1497 --i;
1498 RenderTableCell* cell = current.cells[i];
1499 LayoutPoint cellPoint = flipForWritingModeForChild(*cell, adjustedLocation);
1500 if (static_cast<RenderObject*>(cell)->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
1501 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
1502 return true;
1503 }
1504 }
1505 if (!request.resultIsElementList())
1506 break;
1507 }
1508 if (!request.resultIsElementList())
1509 break;
1510 }
1511
1512 return false;
1513}
1514
1515void RenderTableSection::clearCachedCollapsedBorders()
1516{
1517 if (!table()->collapseBorders())
1518 return;
1519 m_cellsCollapsedBorders.clear();
1520}
1521
1522void RenderTableSection::removeCachedCollapsedBorders(const RenderTableCell& cell)
1523{
1524 if (!table()->collapseBorders())
1525 return;
1526
1527 for (int side = CBSBefore; side <= CBSEnd; ++side)
1528 m_cellsCollapsedBorders.remove(std::make_pair(&cell, side));
1529}
1530
1531void RenderTableSection::setCachedCollapsedBorder(const RenderTableCell& cell, CollapsedBorderSide side, CollapsedBorderValue border)
1532{
1533 ASSERT(table()->collapseBorders());
1534 ASSERT(border.width());
1535 m_cellsCollapsedBorders.set(std::make_pair(&cell, side), border);
1536}
1537
1538CollapsedBorderValue RenderTableSection::cachedCollapsedBorder(const RenderTableCell& cell, CollapsedBorderSide side)
1539{
1540 ASSERT(table()->collapseBorders() && table()->collapsedBordersAreValid());
1541 auto it = m_cellsCollapsedBorders.find(std::make_pair(&cell, side));
1542 // Only non-empty collapsed borders are in the hashmap.
1543 if (it == m_cellsCollapsedBorders.end())
1544 return CollapsedBorderValue(BorderValue(), Color(), BorderPrecedence::Cell);
1545 return it->value;
1546}
1547
1548RenderPtr<RenderTableSection> RenderTableSection::createTableSectionWithStyle(Document& document, const RenderStyle& style)
1549{
1550 auto section = createRenderer<RenderTableSection>(document, RenderStyle::createAnonymousStyleWithDisplay(style, DisplayType::TableRowGroup));
1551 section->initializeStyle();
1552 return section;
1553}
1554
1555RenderPtr<RenderTableSection> RenderTableSection::createAnonymousWithParentRenderer(const RenderTable& parent)
1556{
1557 return RenderTableSection::createTableSectionWithStyle(parent.document(), parent.style());
1558}
1559
1560void RenderTableSection::setLogicalPositionForCell(RenderTableCell* cell, unsigned effectiveColumn) const
1561{
1562 LayoutPoint oldCellLocation = cell->location();
1563
1564 LayoutPoint cellLocation(0_lu, m_rowPos[cell->rowIndex()]);
1565 LayoutUnit horizontalBorderSpacing = table()->hBorderSpacing();
1566
1567 // FIXME: The table's direction should determine our row's direction, not the section's (see bug 96691).
1568 if (!style().isLeftToRightDirection())
1569 cellLocation.setX(table()->columnPositions()[table()->numEffCols()] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + horizontalBorderSpacing);
1570 else
1571 cellLocation.setX(table()->columnPositions()[effectiveColumn] + horizontalBorderSpacing);
1572
1573 cell->setLogicalLocation(cellLocation);
1574 view().frameView().layoutContext().addLayoutDelta(oldCellLocation - cell->location());
1575}
1576
1577} // namespace WebCore
Note: See TracBrowser for help on using the repository browser.