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 |
|
---|
46 | namespace WebCore {
|
---|
47 |
|
---|
48 | using namespace HTMLNames;
|
---|
49 |
|
---|
50 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTableSection);
|
---|
51 |
|
---|
52 | // Those 2 variables are used to balance the memory consumption vs the repaint time on big tables.
|
---|
53 | static const unsigned gMinTableSizeToUseFastPaintPathWithOverflowingCell = 75 * 75;
|
---|
54 | static const float gMaxAllowedOverflowingCellRatioForFastPaintPath = 0.1f;
|
---|
55 |
|
---|
56 | static 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 |
|
---|
64 | static 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 |
|
---|
90 | RenderTableSection::RenderTableSection(Element& element, RenderStyle&& style)
|
---|
91 | : RenderBox(element, WTFMove(style), 0)
|
---|
92 | {
|
---|
93 | setInline(false);
|
---|
94 | }
|
---|
95 |
|
---|
96 | RenderTableSection::RenderTableSection(Document& document, RenderStyle&& style)
|
---|
97 | : RenderBox(document, WTFMove(style), 0)
|
---|
98 | {
|
---|
99 | setInline(false);
|
---|
100 | }
|
---|
101 |
|
---|
102 | RenderTableSection::~RenderTableSection() = default;
|
---|
103 |
|
---|
104 | void 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 |
|
---|
115 | void 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 |
|
---|
124 | void 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 |
|
---|
142 | void 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 |
|
---|
155 | void 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 |
|
---|
214 | static 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 |
|
---|
223 | LayoutUnit 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 |
|
---|
328 | void 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 |
|
---|
377 | void 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 |
|
---|
404 | void 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 |
|
---|
422 | void 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 |
|
---|
443 | LayoutUnit 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 |
|
---|
471 | static 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 |
|
---|
480 | void 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 |
|
---|
537 | void 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 |
|
---|
632 | void RenderTableSection::computeOverflowFromCells()
|
---|
633 | {
|
---|
634 | unsigned totalRows = m_grid.size();
|
---|
635 | unsigned nEffCols = table()->numEffCols();
|
---|
636 | computeOverflowFromCells(totalRows, nEffCols);
|
---|
637 | }
|
---|
638 |
|
---|
639 | void 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 |
|
---|
676 | LayoutUnit 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 |
|
---|
726 | LayoutUnit 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 |
|
---|
776 | LayoutUnit 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 |
|
---|
819 | LayoutUnit 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 |
|
---|
862 | void RenderTableSection::recalcOuterBorder()
|
---|
863 | {
|
---|
864 | m_outerBorderBefore = calcOuterBorderBefore();
|
---|
865 | m_outerBorderAfter = calcOuterBorderAfter();
|
---|
866 | m_outerBorderStart = calcOuterBorderStart();
|
---|
867 | m_outerBorderEnd = calcOuterBorderEnd();
|
---|
868 | }
|
---|
869 |
|
---|
870 | std::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 |
|
---|
894 | void 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 |
|
---|
919 | static 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.
|
---|
926 | static 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 |
|
---|
934 | void 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 |
|
---|
966 | LayoutRect 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 |
|
---|
983 | CellSpan 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 |
|
---|
1000 | CellSpan 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 |
|
---|
1018 | CellSpan 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 |
|
---|
1043 | CellSpan 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 |
|
---|
1074 | void 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 |
|
---|
1084 | LayoutUnit 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 |
|
---|
1095 | LayoutUnit 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 |
|
---|
1105 | LayoutUnit 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 |
|
---|
1115 | LayoutUnit 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 |
|
---|
1126 | void 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 |
|
---|
1161 | static 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 |
|
---|
1187 | void 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 |
|
---|
1305 | void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*)
|
---|
1306 | {
|
---|
1307 | // FIXME: Examine cells and repaint only the rect the image paints in.
|
---|
1308 | repaint();
|
---|
1309 | }
|
---|
1310 |
|
---|
1311 | void 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 |
|
---|
1341 | void 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).
|
---|
1352 | void 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 |
|
---|
1363 | void 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 |
|
---|
1374 | unsigned 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 |
|
---|
1390 | const BorderValue& RenderTableSection::borderAdjoiningStartCell(const RenderTableCell& cell) const
|
---|
1391 | {
|
---|
1392 | ASSERT(cell.isFirstOrLastCellInRow());
|
---|
1393 | return isDirectionSame(this, &cell) ? style().borderStart() : style().borderEnd();
|
---|
1394 | }
|
---|
1395 |
|
---|
1396 | const BorderValue& RenderTableSection::borderAdjoiningEndCell(const RenderTableCell& cell) const
|
---|
1397 | {
|
---|
1398 | ASSERT(cell.isFirstOrLastCellInRow());
|
---|
1399 | return isDirectionSame(this, &cell) ? style().borderEnd() : style().borderStart();
|
---|
1400 | }
|
---|
1401 |
|
---|
1402 | const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableStart() const
|
---|
1403 | {
|
---|
1404 | unsigned adjoiningStartCellColumnIndex = isDirectionSame(this, table()) ? 0 : table()->lastColumnIndex();
|
---|
1405 | return cellAt(0, adjoiningStartCellColumnIndex).primaryCell();
|
---|
1406 | }
|
---|
1407 |
|
---|
1408 | const RenderTableCell* RenderTableSection::firstRowCellAdjoiningTableEnd() const
|
---|
1409 | {
|
---|
1410 | unsigned adjoiningEndCellColumnIndex = isDirectionSame(this, table()) ? table()->lastColumnIndex() : 0;
|
---|
1411 | return cellAt(0, adjoiningEndCellColumnIndex).primaryCell();
|
---|
1412 | }
|
---|
1413 |
|
---|
1414 | void 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 |
|
---|
1422 | void 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
|
---|
1448 | bool 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 |
|
---|
1515 | void RenderTableSection::clearCachedCollapsedBorders()
|
---|
1516 | {
|
---|
1517 | if (!table()->collapseBorders())
|
---|
1518 | return;
|
---|
1519 | m_cellsCollapsedBorders.clear();
|
---|
1520 | }
|
---|
1521 |
|
---|
1522 | void 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 |
|
---|
1531 | void 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 |
|
---|
1538 | CollapsedBorderValue 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 |
|
---|
1548 | RenderPtr<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 |
|
---|
1555 | RenderPtr<RenderTableSection> RenderTableSection::createAnonymousWithParentRenderer(const RenderTable& parent)
|
---|
1556 | {
|
---|
1557 | return RenderTableSection::createTableSectionWithStyle(parent.document(), parent.style());
|
---|
1558 | }
|
---|
1559 |
|
---|
1560 | void 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
|
---|