ef223792c5652ea30a57db284a9a09068fe1704c
[WebKit-https.git] / Source / WebCore / rendering / RenderTableRow.cpp
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, 2010 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 "RenderTableRow.h"
27
28 #include "Document.h"
29 #include "HTMLNames.h"
30 #include "HitTestResult.h"
31 #include "LayoutState.h"
32 #include "PaintInfo.h"
33 #include "RenderTableCell.h"
34 #include "RenderView.h"
35 #include "StyleInheritedData.h"
36 #include <wtf/IsoMallocInlines.h>
37 #include <wtf/StackStats.h>
38
39 namespace WebCore {
40
41 using namespace HTMLNames;
42
43 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderTableRow);
44
45 RenderTableRow::RenderTableRow(Element& element, RenderStyle&& style)
46     : RenderBox(element, WTFMove(style), 0)
47     , m_rowIndex(unsetRowIndex)
48 {
49     setInline(false);
50 }
51
52 RenderTableRow::RenderTableRow(Document& document, RenderStyle&& style)
53     : RenderBox(document, WTFMove(style), 0)
54     , m_rowIndex(unsetRowIndex)
55 {
56     setInline(false);
57 }
58
59 void RenderTableRow::willBeRemovedFromTree()
60 {
61     RenderBox::willBeRemovedFromTree();
62
63     section()->setNeedsCellRecalc();
64 }
65
66 static bool borderWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle)
67 {
68     return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth()
69         || oldStyle->borderTopWidth() != newStyle->borderTopWidth()
70         || oldStyle->borderRightWidth() != newStyle->borderRightWidth()
71         || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth();
72 }
73
74 void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
75 {
76     ASSERT(style().display() == TABLE_ROW);
77
78     RenderBox::styleDidChange(diff, oldStyle);
79     propagateStyleToAnonymousChildren(PropagateToAllChildren);
80
81     if (section() && oldStyle && style().logicalHeight() != oldStyle->logicalHeight())
82         section()->rowLogicalHeightChanged(rowIndex());
83
84     // If border was changed, notify table.
85     if (RenderTable* table = this->table()) {
86         if (oldStyle && oldStyle->border() != style().border())
87             table->invalidateCollapsedBorders();
88
89         if (oldStyle && diff == StyleDifferenceLayout && needsLayout() && table->collapseBorders() && borderWidthChanged(oldStyle, &style())) {
90             // If the border width changes on a row, we need to make sure the cells in the row know to lay out again.
91             // This only happens when borders are collapsed, since they end up affecting the border sides of the cell
92             // itself.
93             for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell())
94                 cell->setChildNeedsLayout(MarkOnlyThis);
95         }
96     }
97 }
98
99 const BorderValue& RenderTableRow::borderAdjoiningStartCell(const RenderTableCell& cell) const
100 {
101     ASSERT_UNUSED(cell, cell.isFirstOrLastCellInRow());
102     // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level.
103     return style().borderStart();
104 }
105
106 const BorderValue& RenderTableRow::borderAdjoiningEndCell(const RenderTableCell& cell) const
107 {
108     ASSERT_UNUSED(cell, cell.isFirstOrLastCellInRow());
109     // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level.
110     return style().borderEnd();
111 }
112
113 void RenderTableRow::addChild(RenderPtr<RenderObject> child, RenderObject* beforeChild)
114 {
115     if (!is<RenderTableCell>(*child)) {
116         RenderObject* last = beforeChild;
117         if (!last)
118             last = lastCell();
119         if (last && last->isAnonymous() && is<RenderTableCell>(*last) && !last->isBeforeOrAfterContent()) {
120             RenderTableCell& cell = downcast<RenderTableCell>(*last);
121             if (beforeChild == &cell)
122                 beforeChild = cell.firstChild();
123             cell.addChild(WTFMove(child), beforeChild);
124             return;
125         }
126
127         if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
128             RenderObject* cell = beforeChild->previousSibling();
129             if (is<RenderTableCell>(cell) && cell->isAnonymous()) {
130                 downcast<RenderTableCell>(*cell).addChild(WTFMove(child));
131                 return;
132             }
133         }
134
135         // Try to find an anonymous container for the child.
136         if (last && last->parent() && last->parent()->isAnonymous() && !last->parent()->isBeforeOrAfterContent()) {
137             // If beforeChild is inside an anonymous cell, insert into the cell.
138             if (!is<RenderTableCell>(*last)) {
139                 last->parent()->addChild(WTFMove(child), beforeChild);
140                 return;
141             }
142             // If beforeChild is inside an anonymous row, insert into the row.
143             auto& parent = *last->parent();
144             if (is<RenderTableRow>(parent)) {
145                 auto newCell = RenderTableCell::createAnonymousWithParentRenderer(*this);
146                 auto& cell = *newCell;
147                 parent.addChild(WTFMove(newCell), beforeChild);
148                 cell.addChild(WTFMove(child));
149                 return;
150             }
151         }
152         auto newCell = RenderTableCell::createAnonymousWithParentRenderer(*this);
153         auto& cell = *newCell;
154         addChild(WTFMove(newCell), beforeChild);
155         cell.addChild(WTFMove(child));
156         return;
157     } 
158
159     if (beforeChild && beforeChild->parent() != this)
160         beforeChild = splitAnonymousBoxesAroundChild(beforeChild);    
161
162     RenderTableCell& cell = downcast<RenderTableCell>(*child);
163
164     // Generated content can result in us having a null section so make sure to null check our parent.
165     if (RenderTableSection* section = this->section())
166         section->addCell(&cell, this);
167
168     ASSERT(!beforeChild || is<RenderTableCell>(*beforeChild));
169     RenderBox::addChild(WTFMove(child), beforeChild);
170
171     if (beforeChild || nextRow())
172         section()->setNeedsCellRecalc();
173     if (RenderTable* table = this->table())
174         table->invalidateCollapsedBorders();
175 }
176
177 void RenderTableRow::layout()
178 {
179     StackStats::LayoutCheckPoint layoutCheckPoint;
180     ASSERT(needsLayout());
181
182     // Table rows do not add translation.
183     LayoutStateMaintainer statePusher(*this, LayoutSize(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
184
185     auto* layoutState = view().frameView().layoutContext().layoutState();
186     bool paginated = layoutState->isPaginated();
187                 
188     for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell()) {
189         if (!cell->needsLayout() && paginated && (layoutState->pageLogicalHeightChanged() || (layoutState->pageLogicalHeight() && layoutState->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())))
190             cell->setChildNeedsLayout(MarkOnlyThis);
191
192         if (cell->needsLayout()) {
193             cell->computeAndSetBlockDirectionMargins(*table());
194             cell->layout();
195         }
196     }
197
198     clearOverflow();
199     addVisualEffectOverflow();
200     // We only ever need to repaint if our cells didn't, which menas that they didn't need
201     // layout, so we know that our bounds didn't change. This code is just making up for
202     // the fact that we did not repaint in setStyle() because we had a layout hint.
203     // We cannot call repaint() because our clippedOverflowRectForRepaint() is taken from the
204     // parent table, and being mid-layout, that is invalid. Instead, we repaint our cells.
205     if (selfNeedsLayout() && checkForRepaintDuringLayout()) {
206         for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell())
207             cell->repaint();
208     }
209
210     // RenderTableSection::layoutRows will set our logical height and width later, so it calls updateLayerTransform().
211     clearNeedsLayout();
212 }
213
214 LayoutRect RenderTableRow::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
215 {
216     ASSERT(parent());
217     // Rows and cells are in the same coordinate space. We need to both compute our overflow rect (which
218     // will accommodate a row outline and any visual effects on the row itself), but we also need to add in
219     // the repaint rects of cells.
220     LayoutRect result = RenderBox::clippedOverflowRectForRepaint(repaintContainer);
221     for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell()) {
222         // Even if a cell is a repaint container, it's the row that paints the background behind it.
223         // So we don't care if a cell is a repaintContainer here.
224         result.uniteIfNonZero(cell->clippedOverflowRectForRepaint(repaintContainer));
225     }
226     return result;
227 }
228
229 // Hit Testing
230 bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
231 {
232     // Table rows cannot ever be hit tested.  Effectively they do not exist.
233     // Just forward to our children always.
234     for (RenderTableCell* cell = lastCell(); cell; cell = cell->previousCell()) {
235         // FIXME: We have to skip over inline flows, since they can show up inside table rows
236         // at the moment (a demoted inline <form> for example). If we ever implement a
237         // table-specific hit-test method (which we should do for performance reasons anyway),
238         // then we can remove this check.
239         if (!cell->hasSelfPaintingLayer()) {
240             LayoutPoint cellPoint = flipForWritingModeForChild(cell, accumulatedOffset);
241             if (cell->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
242                 updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
243                 return true;
244             }
245         }
246     }
247
248     return false;
249 }
250
251 void RenderTableRow::paintOutlineForRowIfNeeded(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
252 {
253     LayoutPoint adjustedPaintOffset = paintOffset + location();
254     PaintPhase paintPhase = paintInfo.phase;
255     if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && style().visibility() == VISIBLE)
256         paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
257 }
258
259 void RenderTableRow::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
260 {
261     ASSERT(hasSelfPaintingLayer());
262
263     paintOutlineForRowIfNeeded(paintInfo, paintOffset);
264     for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell()) {
265         // Paint the row background behind the cell.
266         if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground)
267             cell->paintBackgroundsBehindCell(paintInfo, paintOffset, this);
268         if (!cell->hasSelfPaintingLayer())
269             cell->paint(paintInfo, paintOffset);
270     }
271 }
272
273 void RenderTableRow::imageChanged(WrappedImagePtr, const IntRect*)
274 {
275     // FIXME: Examine cells and repaint only the rect the image paints in.
276     repaint();
277 }
278
279 RenderPtr<RenderTableRow> RenderTableRow::createTableRowWithStyle(Document& document, const RenderStyle& style)
280 {
281     auto row = createRenderer<RenderTableRow>(document, RenderStyle::createAnonymousStyleWithDisplay(style, TABLE_ROW));
282     row->initializeStyle();
283     return row;
284 }
285
286 RenderPtr<RenderTableRow> RenderTableRow::createAnonymousWithParentRenderer(const RenderTableSection& parent)
287 {
288     return RenderTableRow::createTableRowWithStyle(parent.document(), parent.style());
289 }
290
291 void RenderTableRow::collapseAndDestroyAnonymousSiblingRows()
292 {
293     auto* section = this->section();
294     if (!section)
295         return;
296
297     // All siblings generated?
298     for (auto* current = section->firstRow(); current; current = current->nextRow()) {
299         if (current == this)
300             continue;
301         if (!current->isAnonymous())
302             return;
303     }
304
305     RenderTableRow* rowToInsertInto = nullptr;
306     auto* currentRow = section->firstRow();
307     while (currentRow) {
308         if (currentRow == this) {
309             currentRow = currentRow->nextRow();
310             continue;
311         }
312         if (!rowToInsertInto) {
313             rowToInsertInto = currentRow;
314             currentRow = currentRow->nextRow();
315             continue;
316         }
317         currentRow->moveAllChildrenTo(rowToInsertInto, RenderBoxModelObject::NormalizeAfterInsertion::No);
318         auto toDestroy = section->takeChild(*currentRow);
319         currentRow = currentRow->nextRow();
320     }
321     if (rowToInsertInto)
322         rowToInsertInto->setNeedsLayout();
323 }
324
325 } // namespace WebCore