WebCore/rendering/RenderTable.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/rendering/RenderTable.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (C) 1997 Martin Jones (mjones@kde.org)
+ *           (C) 1997 Torben Weis (weis@kde.org)
+ *           (C) 1998 Waldo Bastian (bastian@kde.org)
+ *           (C) 1999 Lars Knoll (knoll@kde.org)
+ *           (C) 1999 Antti Koivisto (koivisto@kde.org)
+ * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB.  If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "config.h"
+#include "RenderTable.h"
+
+#include "AutoTableLayout.h"
+#include "DeleteButtonController.h"
+#include "Document.h"
+#include "FixedTableLayout.h"
+#include "FrameView.h"
+#include "HitTestResult.h"
+#include "HTMLNames.h"
+#include "RenderLayer.h"
+#include "RenderTableCell.h"
+#include "RenderTableCol.h"
+#include "RenderTableSection.h"
+#include "RenderView.h"
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace HTMLNames;
+
+RenderTable::RenderTable(Node* node)
+    : RenderBlock(node)
+    , m_caption(0)
+    , m_head(0)
+    , m_foot(0)
+    , m_firstBody(0)
+    , m_currentBorder(0)
+    , m_hasColElements(false)
+    , m_needsSectionRecalc(0)
+    , m_hSpacing(0)
+    , m_vSpacing(0)
+    , m_borderLeft(0)
+    , m_borderRight(0)
+{
+    m_columnPos.fill(0, 2);
+    m_columns.fill(ColumnStruct(), 1);
+}
+
+void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
+{
+    RenderBlock::styleDidChange(diff, oldStyle);
+
+    ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
+
+    // In the collapsed border model, there is no cell spacing.
+    m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
+    m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
+    m_columnPos[0] = m_hSpacing;
+
+    if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
+        // According to the CSS2 spec, you only use fixed table layout if an
+        // explicit width is specified on the table.  Auto width implies auto table layout.
+        if (style()->tableLayout() == TFIXED && !style()->width().isAuto())
+            m_tableLayout.set(new FixedTableLayout(this));
+        else
+            m_tableLayout.set(new AutoTableLayout(this));
+    }
+}
+
+static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
+{
+    if (!before || !ptr)
+        return;
+    RenderObject* o = before->previousSibling();
+    while (o && o != ptr)
+        o = o->previousSibling();
+    if (!o)
+        ptr = 0;
+}
+
+void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
+{
+    // Make sure we don't append things after :after-generated content if we have it.
+    if (!beforeChild && isAfterContent(lastChild()))
+        beforeChild = lastChild();
+
+    bool wrapInAnonymousSection = !child->isPositioned();
+
+    if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) {
+        // First caption wins.
+        if (beforeChild && m_caption) {
+            RenderObject* o = beforeChild->previousSibling();
+            while (o && o != m_caption)
+                o = o->previousSibling();
+            if (!o)
+                m_caption = 0;
+        }
+        if (!m_caption)
+            m_caption = toRenderBlock(child);
+        wrapInAnonymousSection = false;
+    } else if (child->isTableCol()) {
+        m_hasColElements = true;
+        wrapInAnonymousSection = false;
+    } else if (child->isTableSection()) {
+        switch (child->style()->display()) {
+            case TABLE_HEADER_GROUP:
+                resetSectionPointerIfNotBefore(m_head, beforeChild);
+                if (!m_head) {
+                    m_head = toRenderTableSection(child);
+                } else {
+                    resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
+                    if (!m_firstBody) 
+                        m_firstBody = toRenderTableSection(child);
+                }
+                wrapInAnonymousSection = false;
+                break;
+            case TABLE_FOOTER_GROUP:
+                resetSectionPointerIfNotBefore(m_foot, beforeChild);
+                if (!m_foot) {
+                    m_foot = toRenderTableSection(child);
+                    wrapInAnonymousSection = false;
+                    break;
+                }
+                // Fall through.
+            case TABLE_ROW_GROUP:
+                resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
+                if (!m_firstBody)
+                    m_firstBody = toRenderTableSection(child);
+                wrapInAnonymousSection = false;
+                break;
+            default:
+                ASSERT_NOT_REACHED();
+        }
+    } else if (child->isTableCell() || child->isTableRow())
+        wrapInAnonymousSection = true;
+    else
+        wrapInAnonymousSection = true;
+
+    if (!wrapInAnonymousSection) {
+        // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that.
+        while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION)
+            beforeChild = beforeChild->parent();
+
+        RenderBox::addChild(child, beforeChild);
+        return;
+    }
+
+    if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
+        lastChild()->addChild(child);
+        return;
+    }
+
+    RenderObject* lastBox = beforeChild;
+    while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP)
+        lastBox = lastBox->parent();
+    if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
+        lastBox->addChild(child, beforeChild);
+        return;
+    }
+
+    if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
+        beforeChild = 0;
+    RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
+    RefPtr<RenderStyle> newStyle = RenderStyle::create();
+    newStyle->inheritFrom(style());
+    newStyle->setDisplay(TABLE_ROW_GROUP);
+    section->setStyle(newStyle.release());
+    addChild(section, beforeChild);
+    section->addChild(child);
+}
+
+void RenderTable::removeChild(RenderObject* oldChild)
+{
+    RenderBox::removeChild(oldChild);
+    setNeedsSectionRecalc();
+}
+
+void RenderTable::calcWidth()
+{
+    if (isPositioned())
+        calcAbsoluteHorizontal();
+
+    RenderBlock* cb = containingBlock();
+    int availableWidth = cb->availableWidth();
+
+    LengthType widthType = style()->width().type();
+    if (widthType > Relative && style()->width().isPositive()) {
+        // Percent or fixed table
+        setWidth(style()->width().calcMinValue(availableWidth));
+        setWidth(max(minPrefWidth(), width()));
+    } else {
+        // An auto width table should shrink to fit within the line width if necessary in order to 
+        // avoid overlapping floats.
+        availableWidth = cb->lineWidth(y(), false);
+        
+        // Subtract out any fixed margins from our available width for auto width tables.
+        int marginTotal = 0;
+        if (!style()->marginLeft().isAuto())
+            marginTotal += style()->marginLeft().calcValue(availableWidth);
+        if (!style()->marginRight().isAuto())
+            marginTotal += style()->marginRight().calcValue(availableWidth);
+            
+        // Subtract out our margins to get the available content width.
+        int availContentWidth = max(0, availableWidth - marginTotal);
+        
+        // Ensure we aren't bigger than our max width or smaller than our min width.
+        setWidth(min(availContentWidth, maxPrefWidth()));
+    }
+    
+    setWidth(max(width(), minPrefWidth()));
+
+    // Finally, with our true width determined, compute our margins for real.
+    m_marginRight = 0;
+    m_marginLeft = 0;
+    calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth);
+}
+
+void RenderTable::layout()
+{
+    ASSERT(needsLayout());
+
+    if (layoutOnlyPositionedObjects())
+        return;
+
+    recalcSectionsIfNeeded();
+        
+    LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
+    LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
+
+    setHeight(0);
+    m_overflow.clear();
+
+    initMaxMarginValues();
+    
+    int oldWidth = width();
+    calcWidth();
+
+    if (m_caption && width() != oldWidth)
+        m_caption->setNeedsLayout(true, false);
+
+    // FIXME: The optimisation below doesn't work since the internal table
+    // layout could have changed.  we need to add a flag to the table
+    // layout that tells us if something has changed in the min max
+    // calculations to do it correctly.
+//     if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
+    m_tableLayout->layout();
+
+    setCellWidths();
+
+    // layout child objects
+    int calculatedHeight = 0;
+    int oldTableTop = m_caption ? m_caption->height() + m_caption->marginTop() + m_caption->marginBottom() : 0;
+
+    bool collapsing = collapseBorders();
+
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection()) {
+            child->layoutIfNeeded();
+            RenderTableSection* section = toRenderTableSection(child);
+            calculatedHeight += section->calcRowHeight();
+            if (collapsing)
+                section->recalcOuterBorder();
+            ASSERT(!section->needsLayout());
+        } else if (child->isTableCol()) {
+            child->layoutIfNeeded();
+            ASSERT(!child->needsLayout());
+        }
+    }
+
+    // Only lay out one caption, since it's the only one we're going to end up painting.
+    if (m_caption)
+        m_caption->layoutIfNeeded();
+
+    // If any table section moved vertically, we will just repaint everything from that
+    // section down (it is quite unlikely that any of the following sections
+    // did not shift).
+    bool sectionMoved = false;
+    int movedSectionTop = 0;
+
+    // FIXME: Collapse caption margin.
+    if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) {
+        IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
+
+        m_caption->setLocation(m_caption->marginLeft(), height());
+        if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
+            m_caption->repaintDuringLayoutIfMoved(captionRect);
+
+        setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom());
+
+        if (height() != oldTableTop) {
+            sectionMoved = true;
+            movedSectionTop = min(height(), oldTableTop);
+        }
+    }
+
+    int bpTop = borderTop() + (collapsing ? 0 : paddingTop());
+    int bpBottom = borderBottom() + (collapsing ? 0 : paddingBottom());
+    
+    setHeight(height() + bpTop);
+
+    if (!isPositioned())
+        calcHeight();
+
+    Length h = style()->height();
+    int th = 0;
+    if (h.isFixed())
+        // Tables size as though CSS height includes border/padding.
+        th = h.value() - (bpTop + bpBottom);
+    else if (h.isPercent())
+        th = calcPercentageHeight(h);
+    th = max(0, th);
+
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection())
+            // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one.
+            toRenderTableSection(child)->layoutRows(child == m_firstBody ? max(0, th - calculatedHeight) : 0);
+    }
+
+    if (!m_firstBody && th > calculatedHeight && !style()->htmlHacks()) {
+        // Completely empty tables (with no sections or anything) should at least honor specified height
+        // in strict mode.
+        setHeight(height() + th);
+    }
+    
+    int bl = borderLeft();
+    if (!collapsing)
+        bl += paddingLeft();
+
+    // position the table sections
+    RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+    while (section) {
+        if (!sectionMoved && section->y() != height()) {
+            sectionMoved = true;
+            movedSectionTop = min(height(), section->y()) + section->topVisibleOverflow();
+        }
+        section->setLocation(bl, height());
+
+        setHeight(height() + section->height());
+        section = sectionBelow(section);
+    }
+
+    setHeight(height() + bpBottom);
+
+    if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) {
+        IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
+
+        m_caption->setLocation(m_caption->marginLeft(), height());
+        if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
+            m_caption->repaintDuringLayoutIfMoved(captionRect);
+
+        setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom());
+    }
+
+    if (isPositioned())
+        calcHeight();
+
+    // table can be containing block of positioned elements.
+    // FIXME: Only pass true if width or height changed.
+    layoutPositionedObjects(true);
+
+    // Add overflow from borders.
+    int rightBorderOverflow = width() + (collapsing ? outerBorderRight() - borderRight() : 0);
+    int leftBorderOverflow = collapsing ? borderLeft() - outerBorderLeft() : 0;
+    int bottomBorderOverflow = height() + (collapsing ? outerBorderBottom() - borderBottom() : 0);
+    int topBorderOverflow = collapsing ? borderTop() - outerBorderTop() : 0;
+    addLayoutOverflow(IntRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow));
+    
+    // Add visual overflow from box-shadow and reflections.
+    addShadowOverflow();
+    
+    // Add overflow from our caption.
+    if (m_caption)
+        addOverflowFromChild(m_caption);
+
+    // Add overflow from our sections.
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection()) {
+            RenderTableSection* section = toRenderTableSection(child);
+            addOverflowFromChild(section);
+        }
+    }
+
+    statePusher.pop();
+
+    bool didFullRepaint = repainter.repaintAfterLayout();
+    // Repaint with our new bounds if they are different from our old bounds.
+    if (!didFullRepaint && sectionMoved)
+        repaintRectangle(IntRect(leftVisibleOverflow(), movedSectionTop, rightVisibleOverflow() - leftVisibleOverflow(), bottomVisibleOverflow() - movedSectionTop));
+    
+    setNeedsLayout(false);
+}
+
+void RenderTable::setCellWidths()
+{
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection())
+            toRenderTableSection(child)->setCellWidths();
+    }
+}
+
+void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty)
+{
+    tx += x();
+    ty += y();
+
+    PaintPhase paintPhase = paintInfo.phase;
+
+    int os = 2 * maximalOutlineSize(paintPhase);
+    if (ty + topVisibleOverflow() >= paintInfo.rect.bottom() + os || ty + bottomVisibleOverflow() <= paintInfo.rect.y() - os)
+        return;
+    if (tx + leftVisibleOverflow() >= paintInfo.rect.right() + os || tx + rightVisibleOverflow() <= paintInfo.rect.x() - os)
+        return;
+
+    bool pushedClip = pushContentsClip(paintInfo, tx, ty);    
+    paintObject(paintInfo, tx, ty);
+    if (pushedClip)
+        popContentsClip(paintInfo, paintPhase, tx, ty);
+}
+
+void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty)
+{
+    PaintPhase paintPhase = paintInfo.phase;
+    if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
+        paintBoxDecorations(paintInfo, tx, ty);
+
+    if (paintPhase == PaintPhaseMask) {
+        paintMask(paintInfo, tx, ty);
+        return;
+    }
+
+    // We're done.  We don't bother painting any children.
+    if (paintPhase == PaintPhaseBlockBackground)
+        return;
+    
+    // We don't paint our own background, but we do let the kids paint their backgrounds.
+    if (paintPhase == PaintPhaseChildBlockBackgrounds)
+        paintPhase = PaintPhaseChildBlockBackground;
+
+    PaintInfo info(paintInfo);
+    info.phase = paintPhase;
+    info.updatePaintingRootForChildren(this);
+
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption))
+            child->paint(info, tx, ty);
+    }
+    
+    if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
+        // Collect all the unique border styles that we want to paint in a sorted list.  Once we
+        // have all the styles sorted, we then do individual passes, painting each style of border
+        // from lowest precedence to highest precedence.
+        info.phase = PaintPhaseCollapsedTableBorders;
+        RenderTableCell::CollapsedBorderStyles borderStyles;
+        RenderObject* stop = nextInPreOrderAfterChildren();
+        for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder())
+            if (o->isTableCell())
+                toRenderTableCell(o)->collectBorderStyles(borderStyles);
+        RenderTableCell::sortBorderStyles(borderStyles);
+        size_t count = borderStyles.size();
+        for (size_t i = 0; i < count; ++i) {
+            m_currentBorder = &borderStyles[i];
+            for (RenderObject* child = firstChild(); child; child = child->nextSibling())
+                if (child->isTableSection())
+                    child->paint(info, tx, ty);
+        }
+        m_currentBorder = 0;
+    }
+}
+
+void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
+{
+    if (!paintInfo.shouldPaintWithinRoot(this))
+        return;
+
+    int w = width();
+    int h = height();
+
+    // Account for the caption.
+    if (m_caption) {
+        int captionHeight = (m_caption->height() + m_caption->marginBottom() +  m_caption->marginTop());
+        h -= captionHeight;
+        if (m_caption->style()->captionSide() != CAPBOTTOM)
+            ty += captionHeight;
+    }
+
+    paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
+    
+    paintFillLayers(paintInfo, style()->visitedDependentColor(CSSPropertyBackgroundColor), style()->backgroundLayers(), tx, ty, w, h);
+    paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
+
+    if (style()->hasBorder() && !collapseBorders())
+        paintBorder(paintInfo.context, tx, ty, w, h, style());
+}
+
+void RenderTable::paintMask(PaintInfo& paintInfo, int tx, int ty)
+{
+    if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
+        return;
+
+    int w = width();
+    int h = height();
+
+    // Account for the caption.
+    if (m_caption) {
+        int captionHeight = (m_caption->height() + m_caption->marginBottom() +  m_caption->marginTop());
+        h -= captionHeight;
+        if (m_caption->style()->captionSide() != CAPBOTTOM)
+            ty += captionHeight;
+    }
+
+    paintMaskImages(paintInfo, tx, ty, w, h);
+}
+
+void RenderTable::calcPrefWidths()
+{
+    ASSERT(prefWidthsDirty());
+
+    recalcSectionsIfNeeded();
+    recalcHorizontalBorders();
+
+    m_tableLayout->calcPrefWidths(m_minPrefWidth, m_maxPrefWidth);
+
+    if (m_caption)
+        m_minPrefWidth = max(m_minPrefWidth, m_caption->minPrefWidth());
+
+    setPrefWidthsDirty(false);
+}
+
+void RenderTable::splitColumn(int pos, int firstSpan)
+{
+    // we need to add a new columnStruct
+    int oldSize = m_columns.size();
+    m_columns.grow(oldSize + 1);
+    int oldSpan = m_columns[pos].span;
+    ASSERT(oldSpan > firstSpan);
+    m_columns[pos].span = firstSpan;
+    memmove(m_columns.data() + pos + 1, m_columns.data() + pos, (oldSize - pos) * sizeof(ColumnStruct));
+    m_columns[pos + 1].span = oldSpan - firstSpan;
+
+    // change width of all rows.
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection())
+            toRenderTableSection(child)->splitColumn(pos, oldSize + 1);
+    }
+
+    m_columnPos.grow(numEffCols() + 1);
+    setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+void RenderTable::appendColumn(int span)
+{
+    // easy case.
+    int pos = m_columns.size();
+    int newSize = pos + 1;
+    m_columns.grow(newSize);
+    m_columns[pos].span = span;
+
+    // change width of all rows.
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection())
+            toRenderTableSection(child)->appendColumn(pos);
+    }
+
+    m_columnPos.grow(numEffCols() + 1);
+    setNeedsLayoutAndPrefWidthsRecalc();
+}
+
+RenderTableCol* RenderTable::nextColElement(RenderTableCol* current) const
+{
+    RenderObject* next = current->firstChild();
+    if (!next)
+        next = current->nextSibling();
+    if (!next && current->parent()->isTableCol())
+        next = current->parent()->nextSibling();
+
+    while (next) {
+        if (next->isTableCol())
+            return toRenderTableCol(next);
+        if (next != m_caption)
+            return 0;
+        next = next->nextSibling();
+    }
+    
+    return 0;
+}
+
+RenderTableCol* RenderTable::colElement(int col, bool* startEdge, bool* endEdge) const
+{
+    if (!m_hasColElements)
+        return 0;
+    RenderObject* child = firstChild();
+    int cCol = 0;
+
+    while (child) {
+        if (child->isTableCol())
+            break;
+        if (child != m_caption)
+            return 0;
+        child = child->nextSibling();
+    }
+    if (!child)
+        return 0;
+
+    RenderTableCol* colElem = toRenderTableCol(child);
+    while (colElem) {
+        int span = colElem->span();
+        if (!colElem->firstChild()) {
+            int startCol = cCol;
+            int endCol = cCol + span - 1;
+            cCol += span;
+            if (cCol > col) {
+                if (startEdge)
+                    *startEdge = startCol == col;
+                if (endEdge)
+                    *endEdge = endCol == col;
+                return colElem;
+            }
+        }
+        colElem = nextColElement(colElem);
+    }
+
+    return 0;
+}
+
+void RenderTable::recalcSections() const
+{
+    m_caption = 0;
+    m_head = 0;
+    m_foot = 0;
+    m_firstBody = 0;
+    m_hasColElements = false;
+
+    // We need to get valid pointers to caption, head, foot and first body again
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        switch (child->style()->display()) {
+            case TABLE_CAPTION:
+                if (!m_caption && child->isRenderBlock()) {
+                    m_caption = toRenderBlock(child);
+                    m_caption->setNeedsLayout(true);
+                }
+                break;
+            case TABLE_COLUMN:
+            case TABLE_COLUMN_GROUP:
+                m_hasColElements = true;
+                break;
+            case TABLE_HEADER_GROUP:
+                if (child->isTableSection()) {
+                    RenderTableSection* section = toRenderTableSection(child);
+                    if (!m_head)
+                        m_head = section;
+                    else if (!m_firstBody)
+                        m_firstBody = section;
+                    section->recalcCellsIfNeeded();
+                }
+                break;
+            case TABLE_FOOTER_GROUP:
+                if (child->isTableSection()) {
+                    RenderTableSection* section = toRenderTableSection(child);
+                    if (!m_foot)
+                        m_foot = section;
+                    else if (!m_firstBody)
+                        m_firstBody = section;
+                    section->recalcCellsIfNeeded();
+                }
+                break;
+            case TABLE_ROW_GROUP:
+                if (child->isTableSection()) {
+                    RenderTableSection* section = toRenderTableSection(child);
+                    if (!m_firstBody)
+                        m_firstBody = section;
+                    section->recalcCellsIfNeeded();
+                }
+                break;
+            default:
+                break;
+        }
+    }
+
+    // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
+    int maxCols = 0;
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (child->isTableSection()) {
+            RenderTableSection* section = toRenderTableSection(child);
+            int sectionCols = section->numColumns();
+            if (sectionCols > maxCols)
+                maxCols = sectionCols;
+        }
+    }
+    
+    m_columns.resize(maxCols);
+    m_columnPos.resize(maxCols + 1);
+
+    ASSERT(selfNeedsLayout());
+
+    m_needsSectionRecalc = false;
+}
+
+int RenderTable::calcBorderLeft() const
+{
+    if (collapseBorders()) {
+        // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
+        if (!numEffCols())
+            return 0;
+
+        unsigned borderWidth = 0;
+
+        const BorderValue& tb = style()->borderLeft();
+        if (tb.style() == BHIDDEN)
+            return 0;
+        if (tb.style() > BHIDDEN)
+            borderWidth = tb.width();
+
+        int leftmostColumn = style()->direction() == RTL ? numEffCols() - 1 : 0;
+        RenderTableCol* colGroup = colElement(leftmostColumn);
+        if (colGroup) {
+            const BorderValue& gb = style()->borderLeft();
+            if (gb.style() == BHIDDEN)
+                return 0;
+            if (gb.style() > BHIDDEN)
+                borderWidth = max(borderWidth, static_cast<unsigned>(gb.width()));
+        }
+        
+        RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+        if (firstNonEmptySection && !firstNonEmptySection->numRows())
+            firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+        
+        if (firstNonEmptySection) {
+            const BorderValue& sb = firstNonEmptySection->style()->borderLeft();
+            if (sb.style() == BHIDDEN)
+                return 0;
+
+            if (sb.style() > BHIDDEN)
+                borderWidth = max(borderWidth, static_cast<unsigned>(sb.width()));
+
+            const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, leftmostColumn);
+            
+            if (cs.cell) {
+                const BorderValue& cb = cs.cell->style()->borderLeft();
+                if (cb.style() == BHIDDEN)
+                    return 0;
+
+                const BorderValue& rb = cs.cell->parent()->style()->borderLeft();
+                if (rb.style() == BHIDDEN)
+                    return 0;
+
+                if (cb.style() > BHIDDEN)
+                    borderWidth = max(borderWidth, static_cast<unsigned>(cb.width()));
+                if (rb.style() > BHIDDEN)
+                    borderWidth = max(borderWidth, static_cast<unsigned>(rb.width()));
+            }
+        }
+        return borderWidth / 2;
+    }
+    return RenderBlock::borderLeft();
+}
+    
+int RenderTable::calcBorderRight() const
+{
+    if (collapseBorders()) {
+        // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
+        if (!numEffCols())
+            return 0;
+
+        unsigned borderWidth = 0;
+
+        const BorderValue& tb = style()->borderRight();
+        if (tb.style() == BHIDDEN)
+            return 0;
+        if (tb.style() > BHIDDEN)
+            borderWidth = tb.width();
+
+        int rightmostColumn = style()->direction() == RTL ? 0 : numEffCols() - 1;
+        RenderTableCol* colGroup = colElement(rightmostColumn);
+        if (colGroup) {
+            const BorderValue& gb = style()->borderRight();
+            if (gb.style() == BHIDDEN)
+                return 0;
+            if (gb.style() > BHIDDEN)
+                borderWidth = max(borderWidth, static_cast<unsigned>(gb.width()));
+        }
+        
+        RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+        if (firstNonEmptySection && !firstNonEmptySection->numRows())
+            firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+        
+        if (firstNonEmptySection) {
+            const BorderValue& sb = firstNonEmptySection->style()->borderRight();
+            if (sb.style() == BHIDDEN)
+                return 0;
+
+            if (sb.style() > BHIDDEN)
+                borderWidth = max(borderWidth, static_cast<unsigned>(sb.width()));
+
+            const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, rightmostColumn);
+            
+            if (cs.cell) {
+                const BorderValue& cb = cs.cell->style()->borderRight();
+                if (cb.style() == BHIDDEN)
+                    return 0;
+
+                const BorderValue& rb = cs.cell->parent()->style()->borderRight();
+                if (rb.style() == BHIDDEN)
+                    return 0;
+
+                if (cb.style() > BHIDDEN)
+                    borderWidth = max(borderWidth, static_cast<unsigned>(cb.width()));
+                if (rb.style() > BHIDDEN)
+                    borderWidth = max(borderWidth, static_cast<unsigned>(rb.width()));
+            }
+        }
+        return (borderWidth + 1) / 2;
+    }
+    return RenderBlock::borderRight();
+}
+
+void RenderTable::recalcHorizontalBorders()
+{
+    m_borderLeft = calcBorderLeft();
+    m_borderRight = calcBorderRight();
+}
+
+int RenderTable::borderTop() const
+{
+    if (collapseBorders())
+        return outerBorderTop();
+    return RenderBlock::borderTop();
+}
+
+int RenderTable::borderBottom() const
+{
+    if (collapseBorders())
+        return outerBorderBottom();
+    return RenderBlock::borderBottom();
+}
+
+int RenderTable::outerBorderTop() const
+{
+    if (!collapseBorders())
+        return 0;
+    int borderWidth = 0;
+    RenderTableSection* topSection;
+    if (m_head)
+        topSection = m_head;
+    else if (m_firstBody)
+        topSection = m_firstBody;
+    else if (m_foot)
+        topSection = m_foot;
+    else
+        topSection = 0;
+    if (topSection) {
+        borderWidth = topSection->outerBorderTop();
+        if (borderWidth == -1)
+            return 0;   // Overridden by hidden
+    }
+    const BorderValue& tb = style()->borderTop();
+    if (tb.style() == BHIDDEN)
+        return 0;
+    if (tb.style() > BHIDDEN)
+        borderWidth = max(borderWidth, static_cast<int>(tb.width() / 2));
+    return borderWidth;
+}
+
+int RenderTable::outerBorderBottom() const
+{
+    if (!collapseBorders())
+        return 0;
+    int borderWidth = 0;
+    RenderTableSection* bottomSection;
+    if (m_foot)
+        bottomSection = m_foot;
+    else {
+        RenderObject* child;
+        for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) { }
+        bottomSection = child ? toRenderTableSection(child) : 0;
+    }
+    if (bottomSection) {
+        borderWidth = bottomSection->outerBorderBottom();
+        if (borderWidth == -1)
+            return 0;   // Overridden by hidden
+    }
+    const BorderValue& tb = style()->borderBottom();
+    if (tb.style() == BHIDDEN)
+        return 0;
+    if (tb.style() > BHIDDEN)
+        borderWidth = max(borderWidth, static_cast<int>((tb.width() + 1) / 2));
+    return borderWidth;
+}
+
+int RenderTable::outerBorderLeft() const
+{
+    if (!collapseBorders())
+        return 0;
+
+    int borderWidth = 0;
+
+    const BorderValue& tb = style()->borderLeft();
+    if (tb.style() == BHIDDEN)
+        return 0;
+    if (tb.style() > BHIDDEN)
+        borderWidth = tb.width() / 2;
+
+    bool allHidden = true;
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (!child->isTableSection())
+            continue;
+        int sw = toRenderTableSection(child)->outerBorderLeft();
+        if (sw == -1)
+            continue;
+        else
+            allHidden = false;
+        borderWidth = max(borderWidth, sw);
+    }
+    if (allHidden)
+        return 0;
+
+    return borderWidth;
+}
+
+int RenderTable::outerBorderRight() const
+{
+    if (!collapseBorders())
+        return 0;
+
+    int borderWidth = 0;
+
+    const BorderValue& tb = style()->borderRight();
+    if (tb.style() == BHIDDEN)
+        return 0;
+    if (tb.style() > BHIDDEN)
+        borderWidth = (tb.width() + 1) / 2;
+
+    bool allHidden = true;
+    for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
+        if (!child->isTableSection())
+            continue;
+        int sw = toRenderTableSection(child)->outerBorderRight();
+        if (sw == -1)
+            continue;
+        else
+            allHidden = false;
+        borderWidth = max(borderWidth, sw);
+    }
+    if (allHidden)
+        return 0;
+
+    return borderWidth;
+}
+
+RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const
+{
+    recalcSectionsIfNeeded();
+
+    if (section == m_head)
+        return 0;
+
+    RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
+    while (prevSection) {
+        if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (!skipEmptySections || toRenderTableSection(prevSection)->numRows()))
+            break;
+        prevSection = prevSection->previousSibling();
+    }
+    if (!prevSection && m_head && (!skipEmptySections || m_head->numRows()))
+        prevSection = m_head;
+    return toRenderTableSection(prevSection);
+}
+
+RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const
+{
+    recalcSectionsIfNeeded();
+
+    if (section == m_foot)
+        return 0;
+
+    RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
+    while (nextSection) {
+        if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (!skipEmptySections || toRenderTableSection(nextSection)->numRows()))
+            break;
+        nextSection = nextSection->nextSibling();
+    }
+    if (!nextSection && m_foot && (!skipEmptySections || m_foot->numRows()))
+        nextSection = m_foot;
+    return toRenderTableSection(nextSection);
+}
+
+RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
+{
+    recalcSectionsIfNeeded();
+
+    // Find the section and row to look in
+    int r = cell->row();
+    RenderTableSection* section = 0;
+    int rAbove = 0;
+    if (r > 0) {
+        // cell is not in the first row, so use the above row in its own section
+        section = cell->section();
+        rAbove = r - 1;
+    } else {
+        section = sectionAbove(cell->section(), true);
+        if (section)
+            rAbove = section->numRows() - 1;
+    }
+
+    // Look up the cell in the section's grid, which requires effective col index
+    if (section) {
+        int effCol = colToEffCol(cell->col());
+        RenderTableSection::CellStruct aboveCell;
+        // If we hit a span back up to a real cell.
+        do {
+            aboveCell = section->cellAt(rAbove, effCol);
+            effCol--;
+        } while (!aboveCell.cell && aboveCell.inColSpan && effCol >= 0);
+        return aboveCell.cell;
+    } else
+        return 0;
+}
+
+RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
+{
+    recalcSectionsIfNeeded();
+
+    // Find the section and row to look in
+    int r = cell->row() + cell->rowSpan() - 1;
+    RenderTableSection* section = 0;
+    int rBelow = 0;
+    if (r < cell->section()->numRows() - 1) {
+        // The cell is not in the last row, so use the next row in the section.
+        section = cell->section();
+        rBelow = r + 1;
+    } else {
+        section = sectionBelow(cell->section(), true);
+        if (section)
+            rBelow = 0;
+    }
+
+    // Look up the cell in the section's grid, which requires effective col index
+    if (section) {
+        int effCol = colToEffCol(cell->col());
+        RenderTableSection::CellStruct belowCell;
+        // If we hit a colspan back up to a real cell.
+        do {
+            belowCell = section->cellAt(rBelow, effCol);
+            effCol--;
+        } while (!belowCell.cell && belowCell.inColSpan && effCol >= 0);
+        return belowCell.cell;
+    } else
+        return 0;
+}
+
+RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
+{
+    recalcSectionsIfNeeded();
+
+    RenderTableSection* section = cell->section();
+    int effCol = colToEffCol(cell->col());
+    if (!effCol)
+        return 0;
+    
+    // If we hit a colspan back up to a real cell.
+    RenderTableSection::CellStruct prevCell;
+    do {
+        prevCell = section->cellAt(cell->row(), effCol - 1);
+        effCol--;
+    } while (!prevCell.cell && prevCell.inColSpan && effCol >= 0);
+    return prevCell.cell;
+}
+
+RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
+{
+    recalcSectionsIfNeeded();
+
+    int effCol = colToEffCol(cell->col() + cell->colSpan());
+    if (effCol >= numEffCols())
+        return 0;
+    return cell->section()->cellAt(cell->row(), effCol).cell;
+}
+
+RenderBlock* RenderTable::firstLineBlock() const
+{
+    return 0;
+}
+
+void RenderTable::updateFirstLetter()
+{
+}
+
+int RenderTable::firstLineBoxBaseline() const
+{
+    RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
+    if (firstNonEmptySection && !firstNonEmptySection->numRows())
+        firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
+
+    if (!firstNonEmptySection)
+        return -1;
+
+    return firstNonEmptySection->y() + firstNonEmptySection->firstLineBoxBaseline();
+}
+
+IntRect RenderTable::overflowClipRect(int tx, int ty)
+{
+    IntRect rect = RenderBlock::overflowClipRect(tx, ty);
+    
+    // If we have a caption, expand the clip to include the caption.
+    // FIXME: Technically this is wrong, but it's virtually impossible to fix this
+    // for real until captions have been re-written.
+    // FIXME: This code assumes (like all our other caption code) that only top/bottom are
+    // supported.  When we actually support left/right and stop mapping them to top/bottom,
+    // we might have to hack this code first (depending on what order we do these bug fixes in).
+    if (m_caption) {
+        rect.setHeight(height());
+        rect.setY(ty);
+    }
+
+    return rect;
+}
+
+bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
+{
+    tx += x();
+    ty += y();
+
+    // Check kids first.
+    if (!hasOverflowClip() || overflowClipRect(tx, ty).intersects(result.rectFromPoint(xPos, yPos))) {
+        for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
+            if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption) &&
+                child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
+                updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
+                return true;
+            }
+        }
+    }
+
+    // Check our bounds next.
+    IntRect boundsRect = IntRect(tx, ty, width(), height());
+    if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && boundsRect.intersects(result.rectFromPoint(xPos, yPos))) {
+        updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
+        if (!result.addNodeToRectBasedTestResult(node(), xPos, yPos, boundsRect))
+            return true;
+    }
+
+    return false;
+}
+
+}