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