Home | History | Annotate | Download | only in rendering
      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