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