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 "PaintInfo.h"
32#include "RenderTableCell.h"
33#include "RenderView.h"
34#include "StyleInheritedData.h"
35#include <wtf/StackStats.h>
36
37namespace WebCore {
38
39using namespace HTMLNames;
40
41RenderTableRow::RenderTableRow(Element& element, PassRef<RenderStyle> style)
42    : RenderBox(element, WTF::move(style), 0)
43    , m_rowIndex(unsetRowIndex)
44{
45    setInline(false);
46}
47
48RenderTableRow::RenderTableRow(Document& document, PassRef<RenderStyle> style)
49    : RenderBox(document, WTF::move(style), 0)
50    , m_rowIndex(unsetRowIndex)
51{
52    setInline(false);
53}
54
55void RenderTableRow::willBeRemovedFromTree()
56{
57    RenderBox::willBeRemovedFromTree();
58
59    section()->setNeedsCellRecalc();
60}
61
62static bool borderWidthChanged(const RenderStyle* oldStyle, const RenderStyle* newStyle)
63{
64    return oldStyle->borderLeftWidth() != newStyle->borderLeftWidth()
65        || oldStyle->borderTopWidth() != newStyle->borderTopWidth()
66        || oldStyle->borderRightWidth() != newStyle->borderRightWidth()
67        || oldStyle->borderBottomWidth() != newStyle->borderBottomWidth();
68}
69
70void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
71{
72    ASSERT(style().display() == TABLE_ROW);
73
74    RenderBox::styleDidChange(diff, oldStyle);
75    propagateStyleToAnonymousChildren(PropagateToAllChildren);
76
77    if (section() && oldStyle && style().logicalHeight() != oldStyle->logicalHeight())
78        section()->rowLogicalHeightChanged(rowIndex());
79
80    // If border was changed, notify table.
81    if (parent()) {
82        RenderTable* table = this->table();
83        if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style().border())
84            table->invalidateCollapsedBorders();
85
86        if (table && oldStyle && diff == StyleDifferenceLayout && needsLayout() && table->collapseBorders() && borderWidthChanged(oldStyle, &style())) {
87            // If the border width changes on a row, we need to make sure the cells in the row know to lay out again.
88            // This only happens when borders are collapsed, since they end up affecting the border sides of the cell
89            // itself.
90            for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell())
91                cell->setChildNeedsLayout(MarkOnlyThis);
92        }
93    }
94}
95
96const BorderValue& RenderTableRow::borderAdjoiningStartCell(const RenderTableCell* cell) const
97{
98    ASSERT_UNUSED(cell, cell->isFirstOrLastCellInRow());
99    // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level.
100    return style().borderStart();
101}
102
103const BorderValue& RenderTableRow::borderAdjoiningEndCell(const RenderTableCell* cell) const
104{
105    ASSERT_UNUSED(cell, cell->isFirstOrLastCellInRow());
106    // FIXME: https://webkit.org/b/79272 - Add support for mixed directionality at the cell level.
107    return style().borderEnd();
108}
109
110void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild)
111{
112    if (!child->isTableCell()) {
113        RenderObject* last = beforeChild;
114        if (!last)
115            last = lastCell();
116        if (last && last->isAnonymous() && last->isTableCell() && !last->isBeforeOrAfterContent()) {
117            RenderTableCell* cell = toRenderTableCell(last);
118            if (beforeChild == cell)
119                beforeChild = cell->firstChild();
120            cell->addChild(child, beforeChild);
121            return;
122        }
123
124        if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
125            RenderObject* cell = beforeChild->previousSibling();
126            if (cell && cell->isTableCell() && cell->isAnonymous()) {
127                toRenderTableCell(cell)->addChild(child);
128                return;
129            }
130        }
131
132        // If beforeChild is inside an anonymous cell, insert into the cell.
133        if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous() && !last->parent()->isBeforeOrAfterContent()) {
134            last->parent()->addChild(child, beforeChild);
135            return;
136        }
137
138        RenderTableCell* cell = RenderTableCell::createAnonymousWithParentRenderer(this);
139        addChild(cell, beforeChild);
140        cell->addChild(child);
141        return;
142    }
143
144    if (beforeChild && beforeChild->parent() != this)
145        beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
146
147    RenderTableCell* cell = toRenderTableCell(child);
148
149    // Generated content can result in us having a null section so make sure to null check our parent.
150    if (parent())
151        section()->addCell(cell, this);
152
153    ASSERT(!beforeChild || beforeChild->isTableCell());
154    RenderBox::addChild(cell, beforeChild);
155
156    if (beforeChild || nextRow())
157        section()->setNeedsCellRecalc();
158}
159
160void RenderTableRow::layout()
161{
162    StackStats::LayoutCheckPoint layoutCheckPoint;
163    ASSERT(needsLayout());
164
165    // Table rows do not add translation.
166    LayoutStateMaintainer statePusher(view(), *this, LayoutSize(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
167
168    bool paginated = view().layoutState()->isPaginated();
169
170    for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell()) {
171        if (!cell->needsLayout() && paginated && view().layoutState()->pageLogicalHeight() && view().layoutState()->pageLogicalOffset(cell, cell->logicalTop()) != cell->pageLogicalOffset())
172            cell->setChildNeedsLayout(MarkOnlyThis);
173
174        if (cell->needsLayout()) {
175            cell->computeAndSetBlockDirectionMargins(table());
176            cell->layout();
177        }
178    }
179
180    // We only ever need to repaint if our cells didn't, which menas that they didn't need
181    // layout, so we know that our bounds didn't change. This code is just making up for
182    // the fact that we did not repaint in setStyle() because we had a layout hint.
183    // We cannot call repaint() because our clippedOverflowRectForRepaint() is taken from the
184    // parent table, and being mid-layout, that is invalid. Instead, we repaint our cells.
185    if (selfNeedsLayout() && checkForRepaintDuringLayout()) {
186        for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell())
187            cell->repaint();
188    }
189
190    statePusher.pop();
191    // RenderTableSection::layoutRows will set our logical height and width later, so it calls updateLayerTransform().
192    clearNeedsLayout();
193}
194
195LayoutRect RenderTableRow::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const
196{
197    ASSERT(parent());
198
199    if (repaintContainer == this)
200        return RenderBox::clippedOverflowRectForRepaint(repaintContainer);
201
202    // For now, just repaint the whole table.
203    // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we
204    // might have propagated a background color into.
205    // FIXME: do repaintContainer checks here
206    if (RenderTable* parentTable = table())
207        return parentTable->clippedOverflowRectForRepaint(repaintContainer);
208
209    return LayoutRect();
210}
211
212// Hit Testing
213bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
214{
215    // Table rows cannot ever be hit tested.  Effectively they do not exist.
216    // Just forward to our children always.
217    for (RenderTableCell* cell = lastCell(); cell; cell = cell->previousCell()) {
218        // FIXME: We have to skip over inline flows, since they can show up inside table rows
219        // at the moment (a demoted inline <form> for example). If we ever implement a
220        // table-specific hit-test method (which we should do for performance reasons anyway),
221        // then we can remove this check.
222        if (!cell->hasSelfPaintingLayer()) {
223            LayoutPoint cellPoint = flipForWritingModeForChild(cell, accumulatedOffset);
224            if (cell->nodeAtPoint(request, result, locationInContainer, cellPoint, action)) {
225                updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
226                return true;
227            }
228        }
229    }
230
231    return false;
232}
233
234void RenderTableRow::paintOutlineForRowIfNeeded(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
235{
236    LayoutPoint adjustedPaintOffset = paintOffset + location();
237    PaintPhase paintPhase = paintInfo.phase;
238    if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && style().visibility() == VISIBLE)
239        paintOutline(paintInfo, LayoutRect(adjustedPaintOffset, size()));
240}
241
242void RenderTableRow::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
243{
244    ASSERT(hasSelfPaintingLayer());
245
246    paintOutlineForRowIfNeeded(paintInfo, paintOffset);
247    for (RenderTableCell* cell = firstCell(); cell; cell = cell->nextCell()) {
248        // Paint the row background behind the cell.
249        if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground)
250            cell->paintBackgroundsBehindCell(paintInfo, paintOffset, this);
251        if (!cell->hasSelfPaintingLayer())
252            cell->paint(paintInfo, paintOffset);
253    }
254}
255
256void RenderTableRow::imageChanged(WrappedImagePtr, const IntRect*)
257{
258    // FIXME: Examine cells and repaint only the rect the image paints in.
259    repaint();
260}
261
262RenderTableRow* RenderTableRow::createAnonymousWithParentRenderer(const RenderObject* parent)
263{
264    auto newRow = new RenderTableRow(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), TABLE_ROW));
265    newRow->initializeStyle();
266    return newRow;
267}
268
269} // namespace WebCore
270