1/*
2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 */
24
25#include "config.h"
26#include "RenderTableCell.h"
27
28#include "CollapsedBorderValue.h"
29#include "FloatQuad.h"
30#include "GraphicsContext.h"
31#include "HTMLNames.h"
32#include "HTMLTableCellElement.h"
33#include "PaintInfo.h"
34#include "RenderTableCol.h"
35#include "RenderTheme.h"
36#include "RenderView.h"
37#include "Settings.h"
38#include "StyleProperties.h"
39#include "TransformState.h"
40#include <wtf/IsoMallocInlines.h>
41#include <wtf/StackStats.h>
42
43#if ENABLE(MATHML)
44#include "MathMLElement.h"
45#include "MathMLNames.h"
46#endif
47
48namespace WebCore {
49
50using namespace HTMLNames;
51
52WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTableCell);
53
54struct SameSizeAsRenderTableCell : public RenderBlockFlow {
55 unsigned bitfields;
56 LayoutUnit paddings[2];
57};
58
59COMPILE_ASSERT(sizeof(RenderTableCell) == sizeof(SameSizeAsRenderTableCell), RenderTableCell_should_stay_small);
60COMPILE_ASSERT(sizeof(CollapsedBorderValue) <= 24, CollapsedBorderValue_should_stay_small);
61
62RenderTableCell::RenderTableCell(Element& element, RenderStyle&& style)
63 : RenderBlockFlow(element, WTFMove(style))
64 , m_column(unsetColumnIndex)
65 , m_cellWidthChanged(false)
66 , m_hasColSpan(false)
67 , m_hasRowSpan(false)
68 , m_hasEmptyCollapsedBeforeBorder(false)
69 , m_hasEmptyCollapsedAfterBorder(false)
70 , m_hasEmptyCollapsedStartBorder(false)
71 , m_hasEmptyCollapsedEndBorder(false)
72{
73 // We only update the flags when notified of DOM changes in colSpanOrRowSpanChanged()
74 // so we need to set their initial values here in case something asks for colSpan()/rowSpan() before then.
75 updateColAndRowSpanFlags();
76}
77
78RenderTableCell::RenderTableCell(Document& document, RenderStyle&& style)
79 : RenderBlockFlow(document, WTFMove(style))
80 , m_column(unsetColumnIndex)
81 , m_cellWidthChanged(false)
82 , m_hasColSpan(false)
83 , m_hasRowSpan(false)
84 , m_hasEmptyCollapsedBeforeBorder(false)
85 , m_hasEmptyCollapsedAfterBorder(false)
86 , m_hasEmptyCollapsedStartBorder(false)
87 , m_hasEmptyCollapsedEndBorder(false)
88{
89}
90
91void RenderTableCell::willBeRemovedFromTree()
92{
93 RenderBlockFlow::willBeRemovedFromTree();
94 if (!table() || !section())
95 return;
96 RenderTableSection* section = this->section();
97 table()->invalidateCollapsedBorders();
98 section->setNeedsCellRecalc();
99}
100
101unsigned RenderTableCell::parseColSpanFromDOM() const
102{
103 ASSERT(element());
104 if (is<HTMLTableCellElement>(*element()))
105 return std::min<unsigned>(downcast<HTMLTableCellElement>(*element()).colSpan(), maxColumnIndex);
106#if ENABLE(MATHML)
107 if (element()->hasTagName(MathMLNames::mtdTag))
108 return std::min<unsigned>(downcast<MathMLElement>(*element()).colSpan(), maxColumnIndex);
109#endif
110 return 1;
111}
112
113unsigned RenderTableCell::parseRowSpanFromDOM() const
114{
115 ASSERT(element());
116 if (is<HTMLTableCellElement>(*element()))
117 return std::min<unsigned>(downcast<HTMLTableCellElement>(*element()).rowSpan(), maxRowIndex);
118#if ENABLE(MATHML)
119 if (element()->hasTagName(MathMLNames::mtdTag))
120 return std::min<unsigned>(downcast<MathMLElement>(*element()).rowSpan(), maxRowIndex);
121#endif
122 return 1;
123}
124
125void RenderTableCell::updateColAndRowSpanFlags()
126{
127 // The vast majority of table cells do not have a colspan or rowspan,
128 // so we keep a bool to know if we need to bother reading from the DOM.
129 m_hasColSpan = element() && parseColSpanFromDOM() != 1;
130 m_hasRowSpan = element() && parseRowSpanFromDOM() != 1;
131}
132
133void RenderTableCell::colSpanOrRowSpanChanged()
134{
135 ASSERT(element());
136#if ENABLE(MATHML)
137 ASSERT(element()->hasTagName(tdTag) || element()->hasTagName(thTag) || element()->hasTagName(MathMLNames::mtdTag));
138#else
139 ASSERT(element()->hasTagName(tdTag) || element()->hasTagName(thTag));
140#endif
141
142 updateColAndRowSpanFlags();
143
144 // FIXME: I suspect that we could return early here if !m_hasColSpan && !m_hasRowSpan.
145
146 setNeedsLayoutAndPrefWidthsRecalc();
147 if (parent() && section())
148 section()->setNeedsCellRecalc();
149}
150
151Length RenderTableCell::logicalWidthFromColumns(RenderTableCol* firstColForThisCell, Length widthFromStyle) const
152{
153 ASSERT(firstColForThisCell && firstColForThisCell == table()->colElement(col()));
154 RenderTableCol* tableCol = firstColForThisCell;
155
156 unsigned colSpanCount = colSpan();
157 LayoutUnit colWidthSum;
158 for (unsigned i = 1; i <= colSpanCount; i++) {
159 Length colWidth = tableCol->style().logicalWidth();
160
161 // Percentage value should be returned only for colSpan == 1.
162 // Otherwise we return original width for the cell.
163 if (!colWidth.isFixed()) {
164 if (colSpanCount > 1)
165 return widthFromStyle;
166 return colWidth;
167 }
168
169 colWidthSum += colWidth.value();
170 tableCol = tableCol->nextColumn();
171 // If no next <col> tag found for the span we just return what we have for now.
172 if (!tableCol)
173 break;
174 }
175
176 // Column widths specified on <col> apply to the border box of the cell, see bug 8126.
177 // FIXME: Why is border/padding ignored in the negative width case?
178 if (colWidthSum > 0)
179 return Length(std::max<LayoutUnit>(0, colWidthSum - borderAndPaddingLogicalWidth()), Fixed);
180 return Length(colWidthSum, Fixed);
181}
182
183void RenderTableCell::computePreferredLogicalWidths()
184{
185 // The child cells rely on the grids up in the sections to do their computePreferredLogicalWidths work. Normally the sections are set up early, as table
186 // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections'
187 // grids. We must refresh those grids before the child cells try to use them.
188 table()->recalcSectionsIfNeeded();
189
190 RenderBlockFlow::computePreferredLogicalWidths();
191 if (!element() || !style().autoWrap() || !element()->hasAttributeWithoutSynchronization(nowrapAttr))
192 return;
193
194 Length w = styleOrColLogicalWidth();
195 if (w.isFixed()) {
196 // Nowrap is set, but we didn't actually use it because of the
197 // fixed width set on the cell. Even so, it is a WinIE/Moz trait
198 // to make the minwidth of the cell into the fixed width. They do this
199 // even in strict mode, so do not make this a quirk. Affected the top
200 // of hiptop.com.
201 m_minPreferredLogicalWidth = std::max<LayoutUnit>(w.value(), m_minPreferredLogicalWidth);
202 }
203}
204
205void RenderTableCell::computeIntrinsicPadding(LayoutUnit rowHeight)
206{
207 LayoutUnit oldIntrinsicPaddingBefore = intrinsicPaddingBefore();
208 LayoutUnit oldIntrinsicPaddingAfter = intrinsicPaddingAfter();
209 LayoutUnit logicalHeightWithoutIntrinsicPadding = logicalHeight() - oldIntrinsicPaddingBefore - oldIntrinsicPaddingAfter;
210
211 LayoutUnit intrinsicPaddingBefore;
212 switch (style().verticalAlign()) {
213 case VerticalAlign::Sub:
214 case VerticalAlign::Super:
215 case VerticalAlign::TextTop:
216 case VerticalAlign::TextBottom:
217 case VerticalAlign::Length:
218 case VerticalAlign::Baseline: {
219 LayoutUnit baseline = cellBaselinePosition();
220 if (baseline > borderAndPaddingBefore())
221 intrinsicPaddingBefore = section()->rowBaseline(rowIndex()) - (baseline - oldIntrinsicPaddingBefore);
222 break;
223 }
224 case VerticalAlign::Top:
225 break;
226 case VerticalAlign::Middle:
227 intrinsicPaddingBefore = (rowHeight - logicalHeightWithoutIntrinsicPadding) / 2;
228 break;
229 case VerticalAlign::Bottom:
230 intrinsicPaddingBefore = rowHeight - logicalHeightWithoutIntrinsicPadding;
231 break;
232 case VerticalAlign::BaselineMiddle:
233 break;
234 }
235
236 LayoutUnit intrinsicPaddingAfter = rowHeight - logicalHeightWithoutIntrinsicPadding - intrinsicPaddingBefore;
237 setIntrinsicPaddingBefore(intrinsicPaddingBefore);
238 setIntrinsicPaddingAfter(intrinsicPaddingAfter);
239
240 // FIXME: Changing an intrinsic padding shouldn't trigger a relayout as it only shifts the cell inside the row but
241 // doesn't change the logical height.
242 if (intrinsicPaddingBefore != oldIntrinsicPaddingBefore || intrinsicPaddingAfter != oldIntrinsicPaddingAfter)
243 setNeedsLayout(MarkOnlyThis);
244}
245
246void RenderTableCell::updateLogicalWidth()
247{
248}
249
250void RenderTableCell::setCellLogicalWidth(LayoutUnit tableLayoutLogicalWidth)
251{
252 if (tableLayoutLogicalWidth == logicalWidth())
253 return;
254
255 setNeedsLayout(MarkOnlyThis);
256 row()->setChildNeedsLayout(MarkOnlyThis);
257
258 if (!table()->selfNeedsLayout() && checkForRepaintDuringLayout())
259 repaint();
260
261 setLogicalWidth(tableLayoutLogicalWidth);
262 setCellWidthChanged(true);
263}
264
265void RenderTableCell::layout()
266{
267 StackStats::LayoutCheckPoint layoutCheckPoint;
268
269 int oldCellBaseline = cellBaselinePosition();
270 layoutBlock(cellWidthChanged());
271
272 // If we have replaced content, the intrinsic height of our content may have changed since the last time we laid out. If that's the case the intrinsic padding we used
273 // for layout (the padding required to push the contents of the cell down to the row's baseline) is included in our new height and baseline and makes both
274 // of them wrong. So if our content's intrinsic height has changed push the new content up into the intrinsic padding and relayout so that the rest of
275 // table and row layout can use the correct baseline and height for this cell.
276 if (isBaselineAligned() && section()->rowBaseline(rowIndex()) && cellBaselinePosition() > section()->rowBaseline(rowIndex())) {
277 LayoutUnit newIntrinsicPaddingBefore = std::max<LayoutUnit>(0, intrinsicPaddingBefore() - std::max<LayoutUnit>(0, cellBaselinePosition() - oldCellBaseline));
278 setIntrinsicPaddingBefore(newIntrinsicPaddingBefore);
279 setNeedsLayout(MarkOnlyThis);
280 layoutBlock(cellWidthChanged());
281 }
282 invalidateHasEmptyCollapsedBorders();
283
284 // FIXME: This value isn't the intrinsic content logical height, but we need
285 // to update the value as its used by flexbox layout. crbug.com/367324
286 cacheIntrinsicContentLogicalHeightForFlexItem(contentLogicalHeight());
287
288 setCellWidthChanged(false);
289}
290
291LayoutUnit RenderTableCell::paddingTop() const
292{
293 LayoutUnit result = computedCSSPaddingTop();
294 if (!isHorizontalWritingMode())
295 return result;
296 return result + (style().writingMode() == TopToBottomWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter());
297}
298
299LayoutUnit RenderTableCell::paddingBottom() const
300{
301 LayoutUnit result = computedCSSPaddingBottom();
302 if (!isHorizontalWritingMode())
303 return result;
304 return result + (style().writingMode() == TopToBottomWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore());
305}
306
307LayoutUnit RenderTableCell::paddingLeft() const
308{
309 LayoutUnit result = computedCSSPaddingLeft();
310 if (isHorizontalWritingMode())
311 return result;
312 return result + (style().writingMode() == LeftToRightWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter());
313}
314
315LayoutUnit RenderTableCell::paddingRight() const
316{
317 LayoutUnit result = computedCSSPaddingRight();
318 if (isHorizontalWritingMode())
319 return result;
320 return result + (style().writingMode() == LeftToRightWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore());
321}
322
323LayoutUnit RenderTableCell::paddingBefore() const
324{
325 return computedCSSPaddingBefore() + intrinsicPaddingBefore();
326}
327
328LayoutUnit RenderTableCell::paddingAfter() const
329{
330 return computedCSSPaddingAfter() + intrinsicPaddingAfter();
331}
332
333void RenderTableCell::setOverrideContentLogicalHeightFromRowHeight(LayoutUnit rowHeight)
334{
335 clearIntrinsicPadding();
336 setOverrideContentLogicalHeight(std::max<LayoutUnit>(0, rowHeight - borderAndPaddingLogicalHeight()));
337}
338
339LayoutSize RenderTableCell::offsetFromContainer(RenderElement& container, const LayoutPoint& point, bool* offsetDependsOnPoint) const
340{
341 ASSERT(&container == this->container());
342
343 LayoutSize offset = RenderBlockFlow::offsetFromContainer(container, point, offsetDependsOnPoint);
344 if (parent())
345 offset -= parentBox()->locationOffset();
346
347 return offset;
348}
349
350LayoutRect RenderTableCell::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
351{
352 // If the table grid is dirty, we cannot get reliable information about adjoining cells,
353 // so we ignore outside borders. This should not be a problem because it means that
354 // the table is going to recalculate the grid, relayout and repaint its current rect, which
355 // includes any outside borders of this cell.
356 if (!table()->collapseBorders() || table()->needsSectionRecalc())
357 return RenderBlockFlow::clippedOverflowRectForRepaint(repaintContainer);
358
359 bool rtl = !styleForCellFlow().isLeftToRightDirection();
360 LayoutUnit outlineSize = style().outlineSize();
361 LayoutUnit left = std::max(borderHalfLeft(true), outlineSize);
362 LayoutUnit right = std::max(borderHalfRight(true), outlineSize);
363 LayoutUnit top = std::max(borderHalfTop(true), outlineSize);
364 LayoutUnit bottom = std::max(borderHalfBottom(true), outlineSize);
365 if ((left && !rtl) || (right && rtl)) {
366 if (RenderTableCell* before = table()->cellBefore(this)) {
367 top = std::max(top, before->borderHalfTop(true));
368 bottom = std::max(bottom, before->borderHalfBottom(true));
369 }
370 }
371 if ((left && rtl) || (right && !rtl)) {
372 if (RenderTableCell* after = table()->cellAfter(this)) {
373 top = std::max(top, after->borderHalfTop(true));
374 bottom = std::max(bottom, after->borderHalfBottom(true));
375 }
376 }
377 if (top) {
378 if (RenderTableCell* above = table()->cellAbove(this)) {
379 left = std::max(left, above->borderHalfLeft(true));
380 right = std::max(right, above->borderHalfRight(true));
381 }
382 }
383 if (bottom) {
384 if (RenderTableCell* below = table()->cellBelow(this)) {
385 left = std::max(left, below->borderHalfLeft(true));
386 right = std::max(right, below->borderHalfRight(true));
387 }
388 }
389 LayoutPoint location(std::max<LayoutUnit>(left, -visualOverflowRect().x()), std::max<LayoutUnit>(top, -visualOverflowRect().y()));
390 LayoutRect r(-location.x(), -location.y(), location.x() + std::max(width() + right, visualOverflowRect().maxX()), location.y() + std::max(height() + bottom, visualOverflowRect().maxY()));
391
392 // FIXME: layoutDelta needs to be applied in parts before/after transforms and
393 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308
394 r.move(view().frameView().layoutContext().layoutDelta());
395 return computeRectForRepaint(r, repaintContainer);
396}
397
398Optional<LayoutRect> RenderTableCell::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
399{
400 if (container == this)
401 return rect;
402 LayoutRect adjustedRect = rect;
403 if ((!view().frameView().layoutContext().isPaintOffsetCacheEnabled() || container || context.m_options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection)) && parent())
404 adjustedRect.moveBy(-parentBox()->location()); // Rows are in the same coordinate space, so don't add their offset in.
405 return RenderBlockFlow::computeVisibleRectInContainer(adjustedRect, container, context);
406}
407
408LayoutUnit RenderTableCell::cellBaselinePosition() const
409{
410 // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of
411 // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there
412 // is no such line box or table-row, the baseline is the bottom of content edge of the cell box.
413 return firstLineBaseline().valueOr(borderAndPaddingBefore() + contentLogicalHeight());
414}
415
416static inline void markCellDirtyWhenCollapsedBorderChanges(RenderTableCell* cell)
417{
418 if (!cell)
419 return;
420 cell->setNeedsLayoutAndPrefWidthsRecalc();
421}
422
423void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
424{
425 ASSERT(style().display() == DisplayType::TableCell);
426 ASSERT(!row() || row()->rowIndexWasSet());
427
428 RenderBlockFlow::styleDidChange(diff, oldStyle);
429 setHasVisibleBoxDecorations(true); // FIXME: Optimize this to only set to true if necessary.
430
431 if (parent() && section() && oldStyle && style().height() != oldStyle->height())
432 section()->rowLogicalHeightChanged(rowIndex());
433
434 // Our intrinsic padding pushes us down to align with the baseline of other cells on the row. If our vertical-align
435 // has changed then so will the padding needed to align with other cells - clear it so we can recalculate it from scratch.
436 if (oldStyle && style().verticalAlign() != oldStyle->verticalAlign())
437 clearIntrinsicPadding();
438
439 // If border was changed, notify table.
440 RenderTable* table = this->table();
441 if (table && oldStyle && oldStyle->border() != style().border()) {
442 table->invalidateCollapsedBorders(this);
443 if (table->collapseBorders() && diff == StyleDifference::Layout) {
444 markCellDirtyWhenCollapsedBorderChanges(table->cellBelow(this));
445 markCellDirtyWhenCollapsedBorderChanges(table->cellAbove(this));
446 markCellDirtyWhenCollapsedBorderChanges(table->cellBefore(this));
447 markCellDirtyWhenCollapsedBorderChanges(table->cellAfter(this));
448 }
449 }
450}
451
452// The following rules apply for resolving conflicts and figuring out which border
453// to use.
454// (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting
455// borders. Any border with this value suppresses all borders at this location.
456// (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all
457// the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is
458// the default value for the border style.)
459// (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders
460// are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred
461// in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'.
462// (4) If border styles differ only in color, then a style set on a cell wins over one on a row,
463// which wins over a row group, column, column group and, lastly, table. It is undefined which color
464// is used when two elements of the same type disagree.
465static int compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2)
466{
467 // Sanity check the values passed in. The null border have lowest priority.
468 if (!border2.exists()) {
469 if (!border1.exists())
470 return 0;
471 return 1;
472 }
473 if (!border1.exists())
474 return -1;
475
476 // Rule #1 above.
477 if (border2.style() == BorderStyle::Hidden) {
478 if (border1.style() == BorderStyle::Hidden)
479 return 0;
480 return -1;
481 }
482 if (border1.style() == BorderStyle::Hidden)
483 return 1;
484
485 // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border.
486 if (border2.style() == BorderStyle::None) {
487 if (border1.style() == BorderStyle::None)
488 return 0;
489 return 1;
490 }
491 if (border1.style() == BorderStyle::None)
492 return -1;
493
494 // The first part of rule #3 above. Wider borders win.
495 if (border1.width() != border2.width())
496 return border1.width() < border2.width() ? -1 : 1;
497
498 // The borders have equal width. Sort by border style.
499 if (border1.style() != border2.style())
500 return border1.style() < border2.style() ? -1 : 1;
501
502 // The border have the same width and style. Rely on precedence (cell over row over row group, etc.)
503 if (border1.precedence() == border2.precedence())
504 return 0;
505 return border1.precedence() < border2.precedence() ? -1 : 1;
506}
507
508static CollapsedBorderValue chooseBorder(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2)
509{
510 const CollapsedBorderValue& border = compareBorders(border1, border2) < 0 ? border2 : border1;
511 return border.style() == BorderStyle::Hidden ? CollapsedBorderValue() : border;
512}
513
514bool RenderTableCell::hasStartBorderAdjoiningTable() const
515{
516 bool isStartColumn = !col();
517 bool isEndColumn = table()->colToEffCol(col() + colSpan() - 1) == table()->numEffCols() - 1;
518 bool hasSameDirectionAsTable = isDirectionSame(this, section());
519
520 // The table direction determines the row direction. In mixed directionality, we cannot guarantee that
521 // we have a common border with the table (think a ltr table with rtl start cell).
522 return (isStartColumn && hasSameDirectionAsTable) || (isEndColumn && !hasSameDirectionAsTable);
523}
524
525bool RenderTableCell::hasEndBorderAdjoiningTable() const
526{
527 bool isStartColumn = !col();
528 bool isEndColumn = table()->colToEffCol(col() + colSpan() - 1) == table()->numEffCols() - 1;
529 bool hasSameDirectionAsTable = isDirectionSame(this, section());
530
531 // The table direction determines the row direction. In mixed directionality, we cannot guarantee that
532 // we have a common border with the table (think a ltr table with ltr end cell).
533 return (isStartColumn && !hasSameDirectionAsTable) || (isEndColumn && hasSameDirectionAsTable);
534}
535
536static CollapsedBorderValue emptyBorder()
537{
538 return CollapsedBorderValue(BorderValue(), Color(), BorderPrecedence::Cell);
539}
540
541CollapsedBorderValue RenderTableCell::collapsedStartBorder(IncludeBorderColorOrNot includeColor) const
542{
543 if (!table() || !section())
544 return emptyBorder();
545
546 if (m_hasEmptyCollapsedStartBorder)
547 return emptyBorder();
548
549 if (table()->collapsedBordersAreValid())
550 return section()->cachedCollapsedBorder(*this, CBSStart);
551
552 CollapsedBorderValue result = computeCollapsedStartBorder(includeColor);
553 setHasEmptyCollapsedBorder(CBSStart, !result.width());
554 if (includeColor && !m_hasEmptyCollapsedStartBorder)
555 section()->setCachedCollapsedBorder(*this, CBSStart, result);
556 return result;
557}
558
559CollapsedBorderValue RenderTableCell::computeCollapsedStartBorder(IncludeBorderColorOrNot includeColor) const
560{
561 // For the start border, we need to check, in order of precedence:
562 // (1) Our start border.
563 CSSPropertyID startColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderInlineStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
564 CSSPropertyID endColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderInlineEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
565 CollapsedBorderValue result(style().borderStart(), includeColor ? style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Cell);
566
567 RenderTable* table = this->table();
568 if (!table)
569 return result;
570 // (2) The end border of the preceding cell.
571 RenderTableCell* cellBefore = table->cellBefore(this);
572 if (cellBefore) {
573 CollapsedBorderValue cellBeforeAdjoiningBorder = CollapsedBorderValue(cellBefore->borderAdjoiningCellAfter(*this), includeColor ? cellBefore->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Cell);
574 // |result| should be the 2nd argument as |cellBefore| should win in case of equality per CSS 2.1 (Border conflict resolution, point 4).
575 result = chooseBorder(cellBeforeAdjoiningBorder, result);
576 if (!result.exists())
577 return result;
578 }
579
580 bool startBorderAdjoinsTable = hasStartBorderAdjoiningTable();
581 if (startBorderAdjoinsTable) {
582 // (3) Our row's start border.
583 result = chooseBorder(result, CollapsedBorderValue(row()->borderAdjoiningStartCell(*this), includeColor ? parent()->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Row));
584 if (!result.exists())
585 return result;
586
587 // (4) Our row group's start border.
588 result = chooseBorder(result, CollapsedBorderValue(section()->borderAdjoiningStartCell(*this), includeColor ? section()->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::RowGroup));
589 if (!result.exists())
590 return result;
591 }
592
593 // (5) Our column and column group's start borders.
594 bool startColEdge;
595 bool endColEdge;
596 if (RenderTableCol* colElt = table->colElement(col(), &startColEdge, &endColEdge)) {
597 if (colElt->isTableColumnGroup() && startColEdge) {
598 // The |colElt| is a column group and is also the first colgroup (in case of spanned colgroups).
599 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellStartBorder(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::ColumnGroup));
600 if (!result.exists())
601 return result;
602 } else if (!colElt->isTableColumnGroup()) {
603 // We first consider the |colElt| and irrespective of whether it is a spanned col or not, we apply
604 // its start border. This is as per HTML5 which states that: "For the purposes of the CSS table model,
605 // the col element is expected to be treated as if it was present as many times as its span attribute specifies".
606 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellStartBorder(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Column));
607 if (!result.exists())
608 return result;
609 // Next, apply the start border of the enclosing colgroup but only if it is adjacent to the cell's edge.
610 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentBefore()) {
611 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellStartBorder(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::ColumnGroup));
612 if (!result.exists())
613 return result;
614 }
615 }
616 }
617
618 // (6) The end border of the preceding column.
619 if (cellBefore) {
620 if (RenderTableCol* colElt = table->colElement(col() - 1, &startColEdge, &endColEdge)) {
621 if (colElt->isTableColumnGroup() && endColEdge) {
622 // The element is a colgroup and is also the last colgroup (in case of spanned colgroups).
623 result = chooseBorder(CollapsedBorderValue(colElt->borderAdjoiningCellAfter(*this), includeColor ? colElt->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::ColumnGroup), result);
624 if (!result.exists())
625 return result;
626 } else if (colElt->isTableColumn()) {
627 // Resolve the collapsing border against the col's border ignoring any 'span' as per HTML5.
628 result = chooseBorder(CollapsedBorderValue(colElt->borderAdjoiningCellAfter(*this), includeColor ? colElt->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Column), result);
629 if (!result.exists())
630 return result;
631 // Next, if the previous col has a parent colgroup then its end border should be applied
632 // but only if it is adjacent to the cell's edge.
633 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentAfter()) {
634 result = chooseBorder(CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellEndBorder(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::ColumnGroup), result);
635 if (!result.exists())
636 return result;
637 }
638 }
639 }
640 }
641
642 if (startBorderAdjoinsTable) {
643 // (7) The table's start border.
644 result = chooseBorder(result, CollapsedBorderValue(table->tableStartBorderAdjoiningCell(*this), includeColor ? table->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Table));
645 if (!result.exists())
646 return result;
647 }
648
649 return result;
650}
651
652CollapsedBorderValue RenderTableCell::collapsedEndBorder(IncludeBorderColorOrNot includeColor) const
653{
654 if (!table() || !section())
655 return emptyBorder();
656
657 if (m_hasEmptyCollapsedEndBorder)
658 return emptyBorder();
659
660 if (table()->collapsedBordersAreValid())
661 return section()->cachedCollapsedBorder(*this, CBSEnd);
662
663 CollapsedBorderValue result = computeCollapsedEndBorder(includeColor);
664 setHasEmptyCollapsedBorder(CBSEnd, !result.width());
665 if (includeColor && !m_hasEmptyCollapsedEndBorder)
666 section()->setCachedCollapsedBorder(*this, CBSEnd, result);
667 return result;
668}
669
670CollapsedBorderValue RenderTableCell::computeCollapsedEndBorder(IncludeBorderColorOrNot includeColor) const
671{
672 // For end border, we need to check, in order of precedence:
673 // (1) Our end border.
674 CSSPropertyID startColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderInlineStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
675 CSSPropertyID endColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderInlineEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
676 CollapsedBorderValue result = CollapsedBorderValue(style().borderEnd(), includeColor ? style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Cell);
677
678 RenderTable* table = this->table();
679 if (!table)
680 return result;
681 // Note: We have to use the effective column information instead of whether we have a cell after as a table doesn't
682 // have to be regular (any row can have less cells than the total cell count).
683 bool isEndColumn = table->colToEffCol(col() + colSpan() - 1) == table->numEffCols() - 1;
684 // (2) The start border of the following cell.
685 if (!isEndColumn) {
686 if (RenderTableCell* cellAfter = table->cellAfter(this)) {
687 CollapsedBorderValue cellAfterAdjoiningBorder = CollapsedBorderValue(cellAfter->borderAdjoiningCellBefore(*this), includeColor ? cellAfter->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Cell);
688 result = chooseBorder(result, cellAfterAdjoiningBorder);
689 if (!result.exists())
690 return result;
691 }
692 }
693
694 bool endBorderAdjoinsTable = hasEndBorderAdjoiningTable();
695 if (endBorderAdjoinsTable) {
696 // (3) Our row's end border.
697 result = chooseBorder(result, CollapsedBorderValue(row()->borderAdjoiningEndCell(*this), includeColor ? parent()->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Row));
698 if (!result.exists())
699 return result;
700
701 // (4) Our row group's end border.
702 result = chooseBorder(result, CollapsedBorderValue(section()->borderAdjoiningEndCell(*this), includeColor ? section()->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::RowGroup));
703 if (!result.exists())
704 return result;
705 }
706
707 // (5) Our column and column group's end borders.
708 bool startColEdge;
709 bool endColEdge;
710 if (RenderTableCol* colElt = table->colElement(col() + colSpan() - 1, &startColEdge, &endColEdge)) {
711 if (colElt->isTableColumnGroup() && endColEdge) {
712 // The element is a colgroup and is also the last colgroup (in case of spanned colgroups).
713 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellEndBorder(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::ColumnGroup));
714 if (!result.exists())
715 return result;
716 } else if (!colElt->isTableColumnGroup()) {
717 // First apply the end border of the column irrespective of whether it is spanned or not. This is as per
718 // HTML5 which states that: "For the purposes of the CSS table model, the col element is expected to be
719 // treated as if it was present as many times as its span attribute specifies".
720 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellEndBorder(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Column));
721 if (!result.exists())
722 return result;
723 // Next, if it has a parent colgroup then we apply its end border but only if it is adjacent to the cell.
724 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentAfter()) {
725 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellEndBorder(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::ColumnGroup));
726 if (!result.exists())
727 return result;
728 }
729 }
730 }
731
732 // (6) The start border of the next column.
733 if (!isEndColumn) {
734 if (RenderTableCol* colElt = table->colElement(col() + colSpan(), &startColEdge, &endColEdge)) {
735 if (colElt->isTableColumnGroup() && startColEdge) {
736 // This case is a colgroup without any col, we only compute it if it is adjacent to the cell's edge.
737 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellBefore(*this), includeColor ? colElt->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::ColumnGroup));
738 if (!result.exists())
739 return result;
740 } else if (colElt->isTableColumn()) {
741 // Resolve the collapsing border against the col's border ignoring any 'span' as per HTML5.
742 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellBefore(*this), includeColor ? colElt->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::Column));
743 if (!result.exists())
744 return result;
745 // If we have a parent colgroup, resolve the border only if it is adjacent to the cell.
746 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentBefore()) {
747 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellStartBorder(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(startColorProperty) : Color(), BorderPrecedence::ColumnGroup));
748 if (!result.exists())
749 return result;
750 }
751 }
752 }
753 }
754
755 if (endBorderAdjoinsTable) {
756 // (7) The table's end border.
757 result = chooseBorder(result, CollapsedBorderValue(table->tableEndBorderAdjoiningCell(*this), includeColor ? table->style().visitedDependentColorWithColorFilter(endColorProperty) : Color(), BorderPrecedence::Table));
758 if (!result.exists())
759 return result;
760 }
761
762 return result;
763}
764
765CollapsedBorderValue RenderTableCell::collapsedBeforeBorder(IncludeBorderColorOrNot includeColor) const
766{
767 if (!table() || !section())
768 return emptyBorder();
769
770 if (m_hasEmptyCollapsedBeforeBorder)
771 return emptyBorder();
772
773 if (table()->collapsedBordersAreValid())
774 return section()->cachedCollapsedBorder(*this, CBSBefore);
775
776 CollapsedBorderValue result = computeCollapsedBeforeBorder(includeColor);
777 setHasEmptyCollapsedBorder(CBSBefore, !result.width());
778 if (includeColor && !m_hasEmptyCollapsedBeforeBorder)
779 section()->setCachedCollapsedBorder(*this, CBSBefore, result);
780 return result;
781}
782
783CollapsedBorderValue RenderTableCell::computeCollapsedBeforeBorder(IncludeBorderColorOrNot includeColor) const
784{
785 // For before border, we need to check, in order of precedence:
786 // (1) Our before border.
787 CSSPropertyID beforeColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderBlockStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
788 CSSPropertyID afterColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderBlockEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
789 CollapsedBorderValue result = CollapsedBorderValue(style().borderBefore(), includeColor ? style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Cell);
790
791 RenderTable* table = this->table();
792 if (!table)
793 return result;
794 RenderTableCell* prevCell = table->cellAbove(this);
795 if (prevCell) {
796 // (2) A before cell's after border.
797 result = chooseBorder(CollapsedBorderValue(prevCell->style().borderAfter(), includeColor ? prevCell->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Cell), result);
798 if (!result.exists())
799 return result;
800 }
801
802 // (3) Our row's before border.
803 result = chooseBorder(result, CollapsedBorderValue(parent()->style().borderBefore(), includeColor ? parent()->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Row));
804 if (!result.exists())
805 return result;
806
807 // (4) The previous row's after border.
808 if (prevCell) {
809 RenderObject* prevRow = 0;
810 if (prevCell->section() == section())
811 prevRow = parent()->previousSibling();
812 else
813 prevRow = prevCell->section()->lastRow();
814
815 if (prevRow) {
816 result = chooseBorder(CollapsedBorderValue(prevRow->style().borderAfter(), includeColor ? prevRow->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Row), result);
817 if (!result.exists())
818 return result;
819 }
820 }
821
822 // Now check row groups.
823 RenderTableSection* currSection = section();
824 if (!rowIndex()) {
825 // (5) Our row group's before border.
826 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderBefore(), includeColor ? currSection->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::RowGroup));
827 if (!result.exists())
828 return result;
829
830 // (6) Previous row group's after border.
831 currSection = table->sectionAbove(currSection, SkipEmptySections);
832 if (currSection) {
833 result = chooseBorder(CollapsedBorderValue(currSection->style().borderAfter(), includeColor ? currSection->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::RowGroup), result);
834 if (!result.exists())
835 return result;
836 }
837 }
838
839 if (!currSection) {
840 // (8) Our column and column group's before borders.
841 RenderTableCol* colElt = table->colElement(col());
842 if (colElt) {
843 result = chooseBorder(result, CollapsedBorderValue(colElt->style().borderBefore(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Column));
844 if (!result.exists())
845 return result;
846 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroup()) {
847 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->style().borderBefore(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::ColumnGroup));
848 if (!result.exists())
849 return result;
850 }
851 }
852
853 // (9) The table's before border.
854 result = chooseBorder(result, CollapsedBorderValue(table->style().borderBefore(), includeColor ? table->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Table));
855 if (!result.exists())
856 return result;
857 }
858
859 return result;
860}
861
862CollapsedBorderValue RenderTableCell::collapsedAfterBorder(IncludeBorderColorOrNot includeColor) const
863{
864 if (!table() || !section())
865 return emptyBorder();
866
867 if (m_hasEmptyCollapsedAfterBorder)
868 return emptyBorder();
869
870 if (table()->collapsedBordersAreValid())
871 return section()->cachedCollapsedBorder(*this, CBSAfter);
872
873 CollapsedBorderValue result = computeCollapsedAfterBorder(includeColor);
874 setHasEmptyCollapsedBorder(CBSAfter, !result.width());
875 if (includeColor && !m_hasEmptyCollapsedAfterBorder)
876 section()->setCachedCollapsedBorder(*this, CBSAfter, result);
877 return result;
878}
879
880CollapsedBorderValue RenderTableCell::computeCollapsedAfterBorder(IncludeBorderColorOrNot includeColor) const
881{
882 // For after border, we need to check, in order of precedence:
883 // (1) Our after border.
884 CSSPropertyID beforeColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderBlockStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
885 CSSPropertyID afterColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyBorderBlockEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : CSSPropertyInvalid;
886 CollapsedBorderValue result = CollapsedBorderValue(style().borderAfter(), includeColor ? style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Cell);
887
888 RenderTable* table = this->table();
889 if (!table)
890 return result;
891 RenderTableCell* nextCell = table->cellBelow(this);
892 if (nextCell) {
893 // (2) An after cell's before border.
894 result = chooseBorder(result, CollapsedBorderValue(nextCell->style().borderBefore(), includeColor ? nextCell->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Cell));
895 if (!result.exists())
896 return result;
897 }
898
899 // (3) Our row's after border. (FIXME: Deal with rowspan!)
900 result = chooseBorder(result, CollapsedBorderValue(parent()->style().borderAfter(), includeColor ? parent()->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Row));
901 if (!result.exists())
902 return result;
903
904 // (4) The next row's before border.
905 if (nextCell) {
906 result = chooseBorder(result, CollapsedBorderValue(nextCell->parent()->style().borderBefore(), includeColor ? nextCell->parent()->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::Row));
907 if (!result.exists())
908 return result;
909 }
910
911 // Now check row groups.
912 RenderTableSection* currSection = section();
913 if (rowIndex() + rowSpan() >= currSection->numRows()) {
914 // (5) Our row group's after border.
915 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderAfter(), includeColor ? currSection->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::RowGroup));
916 if (!result.exists())
917 return result;
918
919 // (6) Following row group's before border.
920 currSection = table->sectionBelow(currSection, SkipEmptySections);
921 if (currSection) {
922 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderBefore(), includeColor ? currSection->style().visitedDependentColorWithColorFilter(beforeColorProperty) : Color(), BorderPrecedence::RowGroup));
923 if (!result.exists())
924 return result;
925 }
926 }
927
928 if (!currSection) {
929 // (8) Our column and column group's after borders.
930 RenderTableCol* colElt = table->colElement(col());
931 if (colElt) {
932 result = chooseBorder(result, CollapsedBorderValue(colElt->style().borderAfter(), includeColor ? colElt->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Column));
933 if (!result.exists()) return result;
934 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroup()) {
935 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->style().borderAfter(), includeColor ? enclosingColumnGroup->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::ColumnGroup));
936 if (!result.exists())
937 return result;
938 }
939 }
940
941 // (9) The table's after border.
942 result = chooseBorder(result, CollapsedBorderValue(table->style().borderAfter(), includeColor ? table->style().visitedDependentColorWithColorFilter(afterColorProperty) : Color(), BorderPrecedence::Table));
943 if (!result.exists())
944 return result;
945 }
946
947 return result;
948}
949
950inline CollapsedBorderValue RenderTableCell::cachedCollapsedLeftBorder(const RenderStyle& styleForCellFlow) const
951{
952 if (styleForCellFlow.isHorizontalWritingMode())
953 return styleForCellFlow.isLeftToRightDirection() ? section()->cachedCollapsedBorder(*this, CBSStart) : section()->cachedCollapsedBorder(*this, CBSEnd);
954 return styleForCellFlow.isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(*this, CBSAfter) : section()->cachedCollapsedBorder(*this, CBSBefore);
955}
956
957inline CollapsedBorderValue RenderTableCell::cachedCollapsedRightBorder(const RenderStyle& styleForCellFlow) const
958{
959 if (styleForCellFlow.isHorizontalWritingMode())
960 return styleForCellFlow.isLeftToRightDirection() ? section()->cachedCollapsedBorder(*this, CBSEnd) : section()->cachedCollapsedBorder(*this, CBSStart);
961 return styleForCellFlow.isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(*this, CBSBefore) : section()->cachedCollapsedBorder(*this, CBSAfter);
962}
963
964inline CollapsedBorderValue RenderTableCell::cachedCollapsedTopBorder(const RenderStyle& styleForCellFlow) const
965{
966 if (styleForCellFlow.isHorizontalWritingMode())
967 return styleForCellFlow.isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(*this, CBSAfter) : section()->cachedCollapsedBorder(*this, CBSBefore);
968 return styleForCellFlow.isLeftToRightDirection() ? section()->cachedCollapsedBorder(*this, CBSStart) : section()->cachedCollapsedBorder(*this, CBSEnd);
969}
970
971inline CollapsedBorderValue RenderTableCell::cachedCollapsedBottomBorder(const RenderStyle& styleForCellFlow) const
972{
973 if (styleForCellFlow.isHorizontalWritingMode())
974 return styleForCellFlow.isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(*this, CBSBefore) : section()->cachedCollapsedBorder(*this, CBSAfter);
975 return styleForCellFlow.isLeftToRightDirection() ? section()->cachedCollapsedBorder(*this, CBSEnd) : section()->cachedCollapsedBorder(*this, CBSStart);
976}
977
978LayoutUnit RenderTableCell::borderLeft() const
979{
980 RenderTable* table = this->table();
981 if (!table)
982 return RenderBlockFlow::borderLeft();
983 return table->collapseBorders() ? borderHalfLeft(false) : RenderBlockFlow::borderLeft();
984}
985
986LayoutUnit RenderTableCell::borderRight() const
987{
988 RenderTable* table = this->table();
989 if (!table)
990 return RenderBlockFlow::borderRight();
991 return table->collapseBorders() ? borderHalfRight(false) : RenderBlockFlow::borderRight();
992}
993
994LayoutUnit RenderTableCell::borderTop() const
995{
996 RenderTable* table = this->table();
997 if (!table)
998 return RenderBlockFlow::borderTop();
999 return table->collapseBorders() ? borderHalfTop(false) : RenderBlockFlow::borderTop();
1000}
1001
1002LayoutUnit RenderTableCell::borderBottom() const
1003{
1004 RenderTable* table = this->table();
1005 if (!table)
1006 return RenderBlockFlow::borderBottom();
1007 return table->collapseBorders() ? borderHalfBottom(false) : RenderBlockFlow::borderBottom();
1008}
1009
1010// FIXME: https://bugs.webkit.org/show_bug.cgi?id=46191, make the collapsed border drawing
1011// work with different block flow values instead of being hard-coded to top-to-bottom.
1012LayoutUnit RenderTableCell::borderStart() const
1013{
1014 RenderTable* table = this->table();
1015 if (!table)
1016 return RenderBlockFlow::borderStart();
1017 return table->collapseBorders() ? borderHalfStart(false) : RenderBlockFlow::borderStart();
1018}
1019
1020LayoutUnit RenderTableCell::borderEnd() const
1021{
1022 RenderTable* table = this->table();
1023 if (!table)
1024 return RenderBlockFlow::borderEnd();
1025 return table->collapseBorders() ? borderHalfEnd(false) : RenderBlockFlow::borderEnd();
1026}
1027
1028LayoutUnit RenderTableCell::borderBefore() const
1029{
1030 RenderTable* table = this->table();
1031 if (!table)
1032 return RenderBlockFlow::borderBefore();
1033 return table->collapseBorders() ? borderHalfBefore(false) : RenderBlockFlow::borderBefore();
1034}
1035
1036LayoutUnit RenderTableCell::borderAfter() const
1037{
1038 RenderTable* table = this->table();
1039 if (!table)
1040 return RenderBlockFlow::borderAfter();
1041 return table->collapseBorders() ? borderHalfAfter(false) : RenderBlockFlow::borderAfter();
1042}
1043
1044LayoutUnit RenderTableCell::borderHalfLeft(bool outer) const
1045{
1046 const RenderStyle& styleForCellFlow = this->styleForCellFlow();
1047 if (styleForCellFlow.isHorizontalWritingMode())
1048 return styleForCellFlow.isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer);
1049 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer);
1050}
1051
1052LayoutUnit RenderTableCell::borderHalfRight(bool outer) const
1053{
1054 const RenderStyle& styleForCellFlow = this->styleForCellFlow();
1055 if (styleForCellFlow.isHorizontalWritingMode())
1056 return styleForCellFlow.isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer);
1057 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer);
1058}
1059
1060LayoutUnit RenderTableCell::borderHalfTop(bool outer) const
1061{
1062 const RenderStyle& styleForCellFlow = this->styleForCellFlow();
1063 if (styleForCellFlow.isHorizontalWritingMode())
1064 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer);
1065 return styleForCellFlow.isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer);
1066}
1067
1068LayoutUnit RenderTableCell::borderHalfBottom(bool outer) const
1069{
1070 const RenderStyle& styleForCellFlow = this->styleForCellFlow();
1071 if (styleForCellFlow.isHorizontalWritingMode())
1072 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer);
1073 return styleForCellFlow.isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer);
1074}
1075
1076LayoutUnit RenderTableCell::borderHalfStart(bool outer) const
1077{
1078 CollapsedBorderValue border = collapsedStartBorder(DoNotIncludeBorderColor);
1079 if (border.exists())
1080 return CollapsedBorderValue::adjustedCollapsedBorderWidth(border.width(), document().deviceScaleFactor(), styleForCellFlow().isLeftToRightDirection() ^ outer);
1081 return 0;
1082}
1083
1084LayoutUnit RenderTableCell::borderHalfEnd(bool outer) const
1085{
1086 CollapsedBorderValue border = collapsedEndBorder(DoNotIncludeBorderColor);
1087 if (border.exists())
1088 return CollapsedBorderValue::adjustedCollapsedBorderWidth(border.width(), document().deviceScaleFactor(), !(styleForCellFlow().isLeftToRightDirection() ^ outer));
1089 return 0;
1090}
1091
1092LayoutUnit RenderTableCell::borderHalfBefore(bool outer) const
1093{
1094 CollapsedBorderValue border = collapsedBeforeBorder(DoNotIncludeBorderColor);
1095 if (border.exists())
1096 return CollapsedBorderValue::adjustedCollapsedBorderWidth(border.width(), document().deviceScaleFactor(), !(styleForCellFlow().isFlippedBlocksWritingMode() ^ outer));
1097 return 0;
1098}
1099
1100LayoutUnit RenderTableCell::borderHalfAfter(bool outer) const
1101{
1102 CollapsedBorderValue border = collapsedAfterBorder(DoNotIncludeBorderColor);
1103 if (border.exists())
1104 return CollapsedBorderValue::adjustedCollapsedBorderWidth(border.width(), document().deviceScaleFactor(), styleForCellFlow().isFlippedBlocksWritingMode() ^ outer);
1105 return 0;
1106}
1107
1108void RenderTableCell::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1109{
1110 ASSERT(paintInfo.phase != PaintPhase::CollapsedTableBorders);
1111 RenderBlockFlow::paint(paintInfo, paintOffset);
1112}
1113
1114struct CollapsedBorder {
1115 CollapsedBorderValue borderValue;
1116 BoxSide side;
1117 bool shouldPaint;
1118 LayoutUnit x1;
1119 LayoutUnit y1;
1120 LayoutUnit x2;
1121 LayoutUnit y2;
1122 BorderStyle style;
1123};
1124
1125class CollapsedBorders {
1126public:
1127 CollapsedBorders()
1128 : m_count(0)
1129 {
1130 }
1131
1132 void addBorder(const CollapsedBorderValue& borderValue, BoxSide borderSide, bool shouldPaint,
1133 LayoutUnit x1, LayoutUnit y1, LayoutUnit x2, LayoutUnit y2, BorderStyle borderStyle)
1134 {
1135 if (borderValue.exists() && shouldPaint) {
1136 m_borders[m_count].borderValue = borderValue;
1137 m_borders[m_count].side = borderSide;
1138 m_borders[m_count].shouldPaint = shouldPaint;
1139 m_borders[m_count].x1 = x1;
1140 m_borders[m_count].x2 = x2;
1141 m_borders[m_count].y1 = y1;
1142 m_borders[m_count].y2 = y2;
1143 m_borders[m_count].style = borderStyle;
1144 m_count++;
1145 }
1146 }
1147
1148 CollapsedBorder* nextBorder()
1149 {
1150 for (unsigned i = 0; i < m_count; i++) {
1151 if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) {
1152 m_borders[i].shouldPaint = false;
1153 return &m_borders[i];
1154 }
1155 }
1156
1157 return 0;
1158 }
1159
1160 CollapsedBorder m_borders[4];
1161 unsigned m_count;
1162};
1163
1164static void addBorderStyle(RenderTable::CollapsedBorderValues& borderValues,
1165 CollapsedBorderValue borderValue)
1166{
1167 if (!borderValue.exists())
1168 return;
1169 size_t count = borderValues.size();
1170 for (size_t i = 0; i < count; ++i)
1171 if (borderValues[i].isSameIgnoringColor(borderValue))
1172 return;
1173 borderValues.append(borderValue);
1174}
1175
1176void RenderTableCell::collectBorderValues(RenderTable::CollapsedBorderValues& borderValues) const
1177{
1178 addBorderStyle(borderValues, collapsedStartBorder());
1179 addBorderStyle(borderValues, collapsedEndBorder());
1180 addBorderStyle(borderValues, collapsedBeforeBorder());
1181 addBorderStyle(borderValues, collapsedAfterBorder());
1182}
1183
1184static int compareBorderValuesForQSort(const void* pa, const void* pb)
1185{
1186 const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa);
1187 const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb);
1188 if (a->isSameIgnoringColor(*b))
1189 return 0;
1190 return compareBorders(*a, *b);
1191}
1192
1193void RenderTableCell::sortBorderValues(RenderTable::CollapsedBorderValues& borderValues)
1194{
1195 qsort(borderValues.data(), borderValues.size(), sizeof(CollapsedBorderValue),
1196 compareBorderValuesForQSort);
1197}
1198
1199void RenderTableCell::paintCollapsedBorders(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1200{
1201 ASSERT(paintInfo.phase == PaintPhase::CollapsedTableBorders);
1202
1203 if (!paintInfo.shouldPaintWithinRoot(*this) || style().visibility() != Visibility::Visible)
1204 return;
1205
1206 LayoutRect localRepaintRect = paintInfo.rect;
1207 LayoutRect paintRect = LayoutRect(paintOffset + location(), frameRect().size());
1208 if (paintRect.y() - table()->outerBorderTop() >= localRepaintRect.maxY())
1209 return;
1210
1211 if (paintRect.maxY() + table()->outerBorderBottom() <= localRepaintRect.y())
1212 return;
1213
1214 GraphicsContext& graphicsContext = paintInfo.context();
1215 if (!table()->currentBorderValue() || graphicsContext.paintingDisabled())
1216 return;
1217
1218 const RenderStyle& styleForCellFlow = this->styleForCellFlow();
1219 CollapsedBorderValue leftVal = cachedCollapsedLeftBorder(styleForCellFlow);
1220 CollapsedBorderValue rightVal = cachedCollapsedRightBorder(styleForCellFlow);
1221 CollapsedBorderValue topVal = cachedCollapsedTopBorder(styleForCellFlow);
1222 CollapsedBorderValue bottomVal = cachedCollapsedBottomBorder(styleForCellFlow);
1223
1224 // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location.
1225 LayoutUnit topWidth = topVal.width();
1226 LayoutUnit bottomWidth = bottomVal.width();
1227 LayoutUnit leftWidth = leftVal.width();
1228 LayoutUnit rightWidth = rightVal.width();
1229
1230 float deviceScaleFactor = document().deviceScaleFactor();
1231 LayoutUnit leftHalfCollapsedBorder = CollapsedBorderValue::adjustedCollapsedBorderWidth(leftWidth , deviceScaleFactor, false);
1232 LayoutUnit topHalfCollapsedBorder = CollapsedBorderValue::adjustedCollapsedBorderWidth(topWidth, deviceScaleFactor, false);
1233 LayoutUnit righHalftCollapsedBorder = CollapsedBorderValue::adjustedCollapsedBorderWidth(rightWidth, deviceScaleFactor, true);
1234 LayoutUnit bottomHalfCollapsedBorder = CollapsedBorderValue::adjustedCollapsedBorderWidth(bottomWidth, deviceScaleFactor, true);
1235
1236 LayoutRect borderRect = LayoutRect(paintRect.x() - leftHalfCollapsedBorder,
1237 paintRect.y() - topHalfCollapsedBorder,
1238 paintRect.width() + leftHalfCollapsedBorder + righHalftCollapsedBorder,
1239 paintRect.height() + topHalfCollapsedBorder + bottomHalfCollapsedBorder);
1240
1241 BorderStyle topStyle = collapsedBorderStyle(topVal.style());
1242 BorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style());
1243 BorderStyle leftStyle = collapsedBorderStyle(leftVal.style());
1244 BorderStyle rightStyle = collapsedBorderStyle(rightVal.style());
1245
1246 bool renderTop = topStyle > BorderStyle::Hidden && !topVal.isTransparent() && floorToDevicePixel(topWidth, deviceScaleFactor);
1247 bool renderBottom = bottomStyle > BorderStyle::Hidden && !bottomVal.isTransparent() && floorToDevicePixel(bottomWidth, deviceScaleFactor);
1248 bool renderLeft = leftStyle > BorderStyle::Hidden && !leftVal.isTransparent() && floorToDevicePixel(leftWidth, deviceScaleFactor);
1249 bool renderRight = rightStyle > BorderStyle::Hidden && !rightVal.isTransparent() && floorToDevicePixel(rightWidth, deviceScaleFactor);
1250
1251 // We never paint diagonals at the joins. We simply let the border with the highest
1252 // precedence paint on top of borders with lower precedence.
1253 CollapsedBorders borders;
1254 borders.addBorder(topVal, BSTop, renderTop, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, topStyle);
1255 borders.addBorder(bottomVal, BSBottom, renderBottom, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), bottomStyle);
1256 borders.addBorder(leftVal, BSLeft, renderLeft, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), leftStyle);
1257 borders.addBorder(rightVal, BSRight, renderRight, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), rightStyle);
1258
1259 bool antialias = shouldAntialiasLines(graphicsContext);
1260
1261 for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) {
1262 if (border->borderValue.isSameIgnoringColor(*table()->currentBorderValue()))
1263 drawLineForBoxSide(graphicsContext, LayoutRect(LayoutPoint(border->x1, border->y1), LayoutPoint(border->x2, border->y2)), border->side,
1264 border->borderValue.color(), border->style, 0, 0, antialias);
1265 }
1266}
1267
1268void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderElement* backgroundObject)
1269{
1270 if (!paintInfo.shouldPaintWithinRoot(*this))
1271 return;
1272
1273 if (!backgroundObject)
1274 return;
1275
1276 if (style().visibility() != Visibility::Visible)
1277 return;
1278
1279 RenderTable* tableElt = table();
1280 if (!tableElt->collapseBorders() && style().emptyCells() == EmptyCell::Hide && !firstChild())
1281 return;
1282
1283 LayoutPoint adjustedPaintOffset = paintOffset;
1284 if (backgroundObject != this)
1285 adjustedPaintOffset.moveBy(location());
1286
1287 const auto& style = backgroundObject->style();
1288 auto& bgLayer = style.backgroundLayers();
1289
1290 CompositeOperator compositeOp = CompositeSourceOver;
1291 Color color = style.visitedDependentColor(CSSPropertyBackgroundColor);
1292 if (document().settings().punchOutWhiteBackgroundsInDarkMode() && Color::isWhiteColor(color) && useDarkAppearance())
1293 compositeOp = CompositeDestinationOut;
1294
1295 color = style.colorByApplyingColorFilter(color);
1296
1297 if (bgLayer.hasImage() || color.isValid()) {
1298 // We have to clip here because the background would paint
1299 // on top of the borders otherwise. This only matters for cells and rows.
1300 bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders();
1301 GraphicsContextStateSaver stateSaver(paintInfo.context(), shouldClip);
1302 if (shouldClip) {
1303 LayoutRect clipRect(adjustedPaintOffset.x() + borderLeft(), adjustedPaintOffset.y() + borderTop(),
1304 width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom());
1305 paintInfo.context().clip(clipRect);
1306 }
1307 paintFillLayers(paintInfo, color, bgLayer, LayoutRect(adjustedPaintOffset, frameRect().size()), BackgroundBleedNone, compositeOp, backgroundObject);
1308 }
1309}
1310
1311void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1312{
1313 if (!paintInfo.shouldPaintWithinRoot(*this))
1314 return;
1315
1316 RenderTable* table = this->table();
1317 if (!table->collapseBorders() && style().emptyCells() == EmptyCell::Hide && !firstChild())
1318 return;
1319
1320 LayoutRect paintRect = LayoutRect(paintOffset, frameRect().size());
1321 adjustBorderBoxRectForPainting(paintRect);
1322
1323 paintBoxShadow(paintInfo, paintRect, style(), Normal);
1324
1325 // Paint our cell background.
1326 paintBackgroundsBehindCell(paintInfo, paintOffset, this);
1327
1328 paintBoxShadow(paintInfo, paintRect, style(), Inset);
1329
1330 if (!style().hasBorder() || table->collapseBorders())
1331 return;
1332
1333 paintBorder(paintInfo, paintRect, style());
1334}
1335
1336void RenderTableCell::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
1337{
1338 if (style().visibility() != Visibility::Visible || paintInfo.phase != PaintPhase::Mask)
1339 return;
1340
1341 RenderTable* tableElt = table();
1342 if (!tableElt->collapseBorders() && style().emptyCells() == EmptyCell::Hide && !firstChild())
1343 return;
1344
1345 LayoutRect paintRect = LayoutRect(paintOffset, frameRect().size());
1346 adjustBorderBoxRectForPainting(paintRect);
1347
1348 paintMaskImages(paintInfo, paintRect);
1349}
1350
1351bool RenderTableCell::boxShadowShouldBeAppliedToBackground(const LayoutPoint&, BackgroundBleedAvoidance, InlineFlowBox*) const
1352{
1353 return false;
1354}
1355
1356void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged)
1357{
1358 LayoutUnit scrollbarHeight = scrollbarLogicalHeight();
1359 if (!scrollbarHeight)
1360 return; // Not sure if we should be doing something when a scrollbar goes away or not.
1361
1362 // We only care if the scrollbar that affects our intrinsic padding has been added.
1363 if ((isHorizontalWritingMode() && !horizontalScrollbarChanged) ||
1364 (!isHorizontalWritingMode() && !verticalScrollbarChanged))
1365 return;
1366
1367 // Shrink our intrinsic padding as much as possible to accommodate the scrollbar.
1368 if (style().verticalAlign() == VerticalAlign::Middle) {
1369 LayoutUnit totalHeight = logicalHeight();
1370 LayoutUnit heightWithoutIntrinsicPadding = totalHeight - intrinsicPaddingBefore() - intrinsicPaddingAfter();
1371 totalHeight -= scrollbarHeight;
1372 LayoutUnit newBeforePadding = (totalHeight - heightWithoutIntrinsicPadding) / 2;
1373 LayoutUnit newAfterPadding = totalHeight - heightWithoutIntrinsicPadding - newBeforePadding;
1374 setIntrinsicPaddingBefore(newBeforePadding);
1375 setIntrinsicPaddingAfter(newAfterPadding);
1376 } else
1377 setIntrinsicPaddingAfter(intrinsicPaddingAfter() - scrollbarHeight);
1378}
1379
1380RenderPtr<RenderTableCell> RenderTableCell::createTableCellWithStyle(Document& document, const RenderStyle& style)
1381{
1382 auto cell = createRenderer<RenderTableCell>(document, RenderStyle::createAnonymousStyleWithDisplay(style, DisplayType::TableCell));
1383 cell->initializeStyle();
1384 return cell;
1385}
1386
1387RenderPtr<RenderTableCell> RenderTableCell::createAnonymousWithParentRenderer(const RenderTableRow& parent)
1388{
1389 return RenderTableCell::createTableCellWithStyle(parent.document(), parent.style());
1390}
1391
1392bool RenderTableCell::hasLineIfEmpty() const
1393{
1394 if (element() && element()->hasEditableStyle())
1395 return true;
1396
1397 return RenderBlock::hasLineIfEmpty();
1398}
1399
1400} // namespace WebCore
1401