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 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "RenderTable.h"
29 #include "AutoTableLayout.h"
30 #include "DeleteButtonController.h"
32 #include "FixedTableLayout.h"
33 #include "FrameView.h"
34 #include "HTMLNames.h"
35 #include "RenderLayer.h"
36 #include "RenderTableCell.h"
37 #include "RenderTableCol.h"
38 #include "RenderTableSection.h"
39 #include "RenderView.h"
45 using namespace HTMLNames
;
47 RenderTable::RenderTable(Node
* node
)
55 , m_hasColElements(false)
56 , m_needsSectionRecalc(0)
62 m_columnPos
.fill(0, 2);
63 m_columns
.fill(ColumnStruct(), 1);
66 RenderTable::~RenderTable()
71 void RenderTable::styleDidChange(StyleDifference diff
, const RenderStyle
* oldStyle
)
73 RenderBlock::styleDidChange(diff
, oldStyle
);
75 ETableLayout oldTableLayout
= oldStyle
? oldStyle
->tableLayout() : TAUTO
;
77 // In the collapsed border model, there is no cell spacing.
78 m_hSpacing
= collapseBorders() ? 0 : style()->horizontalBorderSpacing();
79 m_vSpacing
= collapseBorders() ? 0 : style()->verticalBorderSpacing();
80 m_columnPos
[0] = m_hSpacing
;
82 if (!m_tableLayout
|| style()->tableLayout() != oldTableLayout
) {
85 // According to the CSS2 spec, you only use fixed table layout if an
86 // explicit width is specified on the table. Auto width implies auto table layout.
87 if (style()->tableLayout() == TFIXED
&& !style()->width().isAuto())
88 m_tableLayout
= new FixedTableLayout(this);
90 m_tableLayout
= new AutoTableLayout(this);
94 static inline void resetSectionPointerIfNotBefore(RenderTableSection
*& ptr
, RenderObject
* before
)
98 RenderObject
* o
= before
->previousSibling();
100 o
= o
->previousSibling();
105 void RenderTable::addChild(RenderObject
* child
, RenderObject
* beforeChild
)
107 // Make sure we don't append things after :after-generated content if we have it.
108 if (!beforeChild
&& isAfterContent(lastChild()))
109 beforeChild
= lastChild();
111 bool wrapInAnonymousSection
= !child
->isPositioned();
113 if (child
->isRenderBlock() && child
->style()->display() == TABLE_CAPTION
) {
114 // First caption wins.
115 if (beforeChild
&& m_caption
) {
116 RenderObject
* o
= beforeChild
->previousSibling();
117 while (o
&& o
!= m_caption
)
118 o
= o
->previousSibling();
123 m_caption
= toRenderBlock(child
);
124 wrapInAnonymousSection
= false;
125 } else if (child
->isTableCol()) {
126 m_hasColElements
= true;
127 wrapInAnonymousSection
= false;
128 } else if (child
->isTableSection()) {
129 switch (child
->style()->display()) {
130 case TABLE_HEADER_GROUP
:
131 resetSectionPointerIfNotBefore(m_head
, beforeChild
);
133 m_head
= static_cast<RenderTableSection
*>(child
);
135 resetSectionPointerIfNotBefore(m_firstBody
, beforeChild
);
137 m_firstBody
= static_cast<RenderTableSection
*>(child
);
139 wrapInAnonymousSection
= false;
141 case TABLE_FOOTER_GROUP
:
142 resetSectionPointerIfNotBefore(m_foot
, beforeChild
);
144 m_foot
= static_cast<RenderTableSection
*>(child
);
145 wrapInAnonymousSection
= false;
149 case TABLE_ROW_GROUP
:
150 resetSectionPointerIfNotBefore(m_firstBody
, beforeChild
);
152 m_firstBody
= static_cast<RenderTableSection
*>(child
);
153 wrapInAnonymousSection
= false;
156 ASSERT_NOT_REACHED();
158 } else if (child
->isTableCell() || child
->isTableRow())
159 wrapInAnonymousSection
= true;
161 wrapInAnonymousSection
= true;
163 if (!wrapInAnonymousSection
) {
164 // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that.
165 while (beforeChild
&& !beforeChild
->isTableSection() && !beforeChild
->isTableCol() && beforeChild
->style()->display() != TABLE_CAPTION
)
166 beforeChild
= beforeChild
->parent();
168 RenderBox::addChild(child
, beforeChild
);
172 if (!beforeChild
&& lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
173 lastChild()->addChild(child
);
177 RenderObject
* lastBox
= beforeChild
;
178 while (lastBox
&& lastBox
->parent()->isAnonymous() && !lastBox
->isTableSection() && lastBox
->style()->display() != TABLE_CAPTION
&& lastBox
->style()->display() != TABLE_COLUMN_GROUP
)
179 lastBox
= lastBox
->parent();
180 if (lastBox
&& lastBox
->isAnonymous() && !isAfterContent(lastBox
)) {
181 lastBox
->addChild(child
, beforeChild
);
185 if (beforeChild
&& !beforeChild
->isTableSection() && beforeChild
->style()->display() != TABLE_CAPTION
&& beforeChild
->style()->display() != TABLE_COLUMN_GROUP
)
187 RenderTableSection
* section
= new (renderArena()) RenderTableSection(document() /* anonymous */);
188 RefPtr
<RenderStyle
> newStyle
= RenderStyle::create();
189 newStyle
->inheritFrom(style());
190 newStyle
->setDisplay(TABLE_ROW_GROUP
);
191 section
->setStyle(newStyle
.release());
192 addChild(section
, beforeChild
);
193 section
->addChild(child
);
196 void RenderTable::removeChild(RenderObject
* oldChild
)
198 RenderBox::removeChild(oldChild
);
199 setNeedsSectionRecalc();
202 void RenderTable::calcWidth()
205 calcAbsoluteHorizontal();
207 RenderBlock
* cb
= containingBlock();
208 int availableWidth
= cb
->availableWidth();
210 LengthType widthType
= style()->width().type();
211 if (widthType
> Relative
&& style()->width().isPositive()) {
212 // Percent or fixed table
213 setWidth(style()->width().calcMinValue(availableWidth
));
214 setWidth(max(minPrefWidth(), width()));
216 // An auto width table should shrink to fit within the line width if necessary in order to
217 // avoid overlapping floats.
218 availableWidth
= cb
->lineWidth(y(), false);
220 // Subtract out any fixed margins from our available width for auto width tables.
222 if (!style()->marginLeft().isAuto())
223 marginTotal
+= style()->marginLeft().calcValue(availableWidth
);
224 if (!style()->marginRight().isAuto())
225 marginTotal
+= style()->marginRight().calcValue(availableWidth
);
227 // Subtract out our margins to get the available content width.
228 int availContentWidth
= max(0, availableWidth
- marginTotal
);
230 // Ensure we aren't bigger than our max width or smaller than our min width.
231 setWidth(min(availContentWidth
, maxPrefWidth()));
234 setWidth(max(width(), minPrefWidth()));
236 // Finally, with our true width determined, compute our margins for real.
239 calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth
);
242 void RenderTable::layout()
244 ASSERT(needsLayout());
246 if (layoutOnlyPositionedObjects())
249 recalcSectionsIfNeeded();
251 LayoutRepainter
repainter(*this, checkForRepaintDuringLayout());
252 LayoutStateMaintainer
statePusher(view(), this, IntSize(x(), y()));
255 m_overflowHeight
= 0;
257 initMaxMarginValues();
259 int oldWidth
= width();
262 if (m_caption
&& width() != oldWidth
)
263 m_caption
->setNeedsLayout(true, false);
265 // FIXME: The optimisation below doesn't work since the internal table
266 // layout could have changed. we need to add a flag to the table
267 // layout that tells us if something has changed in the min max
268 // calculations to do it correctly.
269 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
270 m_tableLayout
->layout();
274 // layout child objects
275 int calculatedHeight
= 0;
276 int oldTableTop
= m_caption
? m_caption
->height() + m_caption
->marginTop() + m_caption
->marginBottom() : 0;
278 bool collapsing
= collapseBorders();
280 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
281 if (child
->isTableSection()) {
282 child
->layoutIfNeeded();
283 RenderTableSection
* section
= static_cast<RenderTableSection
*>(child
);
284 calculatedHeight
+= section
->calcRowHeight();
286 section
->recalcOuterBorder();
287 ASSERT(!section
->needsLayout());
288 } else if (child
->isTableCol()) {
289 child
->layoutIfNeeded();
290 ASSERT(!child
->needsLayout());
294 // Only lay out one caption, since it's the only one we're going to end up painting.
296 m_caption
->layoutIfNeeded();
298 m_overflowWidth
= width() + (collapsing
? outerBorderRight() - borderRight() : 0);
299 m_overflowLeft
= collapsing
? borderLeft() - outerBorderLeft() : 0;
301 // If any table section moved vertically, we will just repaint everything from that
302 // section down (it is quite unlikely that any of the following sections
304 bool sectionMoved
= false;
305 int movedSectionTop
= 0;
307 // FIXME: Collapse caption margin.
308 if (m_caption
&& m_caption
->style()->captionSide() != CAPBOTTOM
) {
309 IntRect
captionRect(m_caption
->x(), m_caption
->y(), m_caption
->width(), m_caption
->height());
311 m_caption
->setLocation(m_caption
->marginLeft(), height());
312 if (!selfNeedsLayout() && m_caption
->checkForRepaintDuringLayout())
313 m_caption
->repaintDuringLayoutIfMoved(captionRect
);
315 setHeight(height() + m_caption
->height() + m_caption
->marginTop() + m_caption
->marginBottom());
316 m_overflowLeft
= min(m_overflowLeft
, m_caption
->x() + m_caption
->overflowLeft(false));
317 m_overflowWidth
= max(m_overflowWidth
, m_caption
->x() + m_caption
->overflowWidth(false));
318 m_overflowTop
= min(m_overflowTop
, m_caption
->y() + m_caption
->overflowTop(false));
319 m_overflowHeight
= max(m_overflowHeight
, m_caption
->y() + m_caption
->overflowHeight(false));
321 if (height() != oldTableTop
) {
323 movedSectionTop
= min(height(), oldTableTop
);
327 int bpTop
= borderTop() + (collapsing
? 0 : paddingTop());
328 int bpBottom
= borderBottom() + (collapsing
? 0 : paddingBottom());
330 setHeight(height() + bpTop
);
335 Length h
= style()->height();
338 // Tables size as though CSS height includes border/padding.
339 th
= h
.value() - (bpTop
+ bpBottom
);
340 else if (h
.isPercent())
341 th
= calcPercentageHeight(h
);
344 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
345 if (child
->isTableSection())
346 // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one.
347 static_cast<RenderTableSection
*>(child
)->layoutRows(child
== m_firstBody
? max(0, th
- calculatedHeight
) : 0);
350 if (!m_firstBody
&& th
> calculatedHeight
&& !style()->htmlHacks()) {
351 // Completely empty tables (with no sections or anything) should at least honor specified height
353 setHeight(height() + th
);
356 int bl
= borderLeft();
360 // position the table sections
361 RenderTableSection
* section
= m_head
? m_head
: (m_firstBody
? m_firstBody
: m_foot
);
363 if (!sectionMoved
&& section
->y() != height()) {
365 movedSectionTop
= min(height(), section
->y()) + section
->overflowTop(false);
367 section
->setLocation(bl
, height());
369 setHeight(height() + section
->height());
370 m_overflowLeft
= min(m_overflowLeft
, section
->x() + section
->overflowLeft(false));
371 m_overflowWidth
= max(m_overflowWidth
, section
->x() + section
->overflowWidth(false));
372 m_overflowTop
= min(m_overflowTop
, section
->y() + section
->overflowTop(false));
373 m_overflowHeight
= max(m_overflowHeight
, section
->y() + section
->overflowHeight(false));
374 section
= sectionBelow(section
);
377 setHeight(height() + bpBottom
);
379 if (m_caption
&& m_caption
->style()->captionSide() == CAPBOTTOM
) {
380 IntRect
captionRect(m_caption
->x(), m_caption
->y(), m_caption
->width(), m_caption
->height());
382 m_caption
->setLocation(m_caption
->marginLeft(), height());
383 if (!selfNeedsLayout() && m_caption
->checkForRepaintDuringLayout())
384 m_caption
->repaintDuringLayoutIfMoved(captionRect
);
386 setHeight(height() + m_caption
->height() + m_caption
->marginTop() + m_caption
->marginBottom());
387 m_overflowLeft
= min(m_overflowLeft
, m_caption
->x() + m_caption
->overflowLeft(false));
388 m_overflowWidth
= max(m_overflowWidth
, m_caption
->x() + m_caption
->overflowWidth(false));
394 m_overflowHeight
= max(m_overflowHeight
, height());
396 // table can be containing block of positioned elements.
397 // FIXME: Only pass true if width or height changed.
398 layoutPositionedObjects(true);
400 if (!hasOverflowClip()) {
401 for (ShadowData
* boxShadow
= style()->boxShadow(); boxShadow
; boxShadow
= boxShadow
->next
) {
402 m_overflowLeft
= min(m_overflowLeft
, boxShadow
->x
- boxShadow
->blur
);
403 m_overflowWidth
= max(m_overflowWidth
, width() + boxShadow
->x
+ boxShadow
->blur
);
404 m_overflowTop
= min(m_overflowTop
, boxShadow
->y
- boxShadow
->blur
);
405 m_overflowHeight
= max(m_overflowHeight
, height() + boxShadow
->y
+ boxShadow
->blur
);
408 if (hasReflection()) {
409 IntRect
reflection(reflectionBox());
410 m_overflowTop
= min(m_overflowTop
, reflection
.y());
411 m_overflowHeight
= max(m_overflowHeight
, reflection
.bottom());
412 m_overflowLeft
= min(m_overflowLeft
, reflection
.x());
413 m_overflowHeight
= max(m_overflowWidth
, reflection
.right());
419 bool didFullRepaint
= repainter
.repaintAfterLayout();
420 // Repaint with our new bounds if they are different from our old bounds.
421 if (!didFullRepaint
&& sectionMoved
)
422 repaintRectangle(IntRect(m_overflowLeft
, movedSectionTop
, m_overflowWidth
- m_overflowLeft
, m_overflowHeight
- movedSectionTop
));
424 setNeedsLayout(false);
427 void RenderTable::setCellWidths()
429 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
430 if (child
->isTableSection())
431 static_cast<RenderTableSection
*>(child
)->setCellWidths();
435 void RenderTable::paint(PaintInfo
& paintInfo
, int tx
, int ty
)
440 PaintPhase paintPhase
= paintInfo
.phase
;
442 int os
= 2 * maximalOutlineSize(paintPhase
);
443 if (ty
+ overflowTop(false) >= paintInfo
.rect
.bottom() + os
|| ty
+ overflowHeight(false) <= paintInfo
.rect
.y() - os
)
445 if (tx
+ overflowLeft(false) >= paintInfo
.rect
.right() + os
|| tx
+ overflowWidth(false) <= paintInfo
.rect
.x() - os
)
448 bool pushedClip
= pushContentsClip(paintInfo
, tx
, ty
);
449 paintObject(paintInfo
, tx
, ty
);
451 popContentsClip(paintInfo
, paintPhase
, tx
, ty
);
454 void RenderTable::paintObject(PaintInfo
& paintInfo
, int tx
, int ty
)
456 PaintPhase paintPhase
= paintInfo
.phase
;
457 if ((paintPhase
== PaintPhaseBlockBackground
|| paintPhase
== PaintPhaseChildBlockBackground
) && hasBoxDecorations() && style()->visibility() == VISIBLE
)
458 paintBoxDecorations(paintInfo
, tx
, ty
);
460 if (paintPhase
== PaintPhaseMask
) {
461 paintMask(paintInfo
, tx
, ty
);
465 // We're done. We don't bother painting any children.
466 if (paintPhase
== PaintPhaseBlockBackground
)
469 // We don't paint our own background, but we do let the kids paint their backgrounds.
470 if (paintPhase
== PaintPhaseChildBlockBackgrounds
)
471 paintPhase
= PaintPhaseChildBlockBackground
;
473 PaintInfo
info(paintInfo
);
474 info
.phase
= paintPhase
;
475 info
.paintingRoot
= paintingRootForChildren(paintInfo
);
477 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
478 if (child
->isBox() && !toRenderBox(child
)->hasSelfPaintingLayer() && (child
->isTableSection() || child
== m_caption
))
479 child
->paint(info
, tx
, ty
);
482 if (collapseBorders() && paintPhase
== PaintPhaseChildBlockBackground
&& style()->visibility() == VISIBLE
) {
483 // Collect all the unique border styles that we want to paint in a sorted list. Once we
484 // have all the styles sorted, we then do individual passes, painting each style of border
485 // from lowest precedence to highest precedence.
486 info
.phase
= PaintPhaseCollapsedTableBorders
;
487 RenderTableCell::CollapsedBorderStyles borderStyles
;
488 RenderObject
* stop
= nextInPreOrderAfterChildren();
489 for (RenderObject
* o
= firstChild(); o
&& o
!= stop
; o
= o
->nextInPreOrder())
490 if (o
->isTableCell())
491 static_cast<RenderTableCell
*>(o
)->collectBorderStyles(borderStyles
);
492 RenderTableCell::sortBorderStyles(borderStyles
);
493 size_t count
= borderStyles
.size();
494 for (size_t i
= 0; i
< count
; ++i
) {
495 m_currentBorder
= &borderStyles
[i
];
496 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling())
497 if (child
->isTableSection())
498 child
->paint(info
, tx
, ty
);
504 void RenderTable::paintBoxDecorations(PaintInfo
& paintInfo
, int tx
, int ty
)
509 // Account for the caption.
511 int captionHeight
= (m_caption
->height() + m_caption
->marginBottom() + m_caption
->marginTop());
513 if (m_caption
->style()->captionSide() != CAPBOTTOM
)
517 paintBoxShadow(paintInfo
.context
, tx
, ty
, w
, h
, style());
519 paintFillLayers(paintInfo
, style()->backgroundColor(), style()->backgroundLayers(), tx
, ty
, w
, h
);
521 if (style()->hasBorder() && !collapseBorders())
522 paintBorder(paintInfo
.context
, tx
, ty
, w
, h
, style());
525 void RenderTable::paintMask(PaintInfo
& paintInfo
, int tx
, int ty
)
527 if (style()->visibility() != VISIBLE
|| paintInfo
.phase
!= PaintPhaseMask
)
533 // Account for the caption.
535 int captionHeight
= (m_caption
->height() + m_caption
->marginBottom() + m_caption
->marginTop());
537 if (m_caption
->style()->captionSide() != CAPBOTTOM
)
541 paintMaskImages(paintInfo
, tx
, ty
, w
, h
);
544 void RenderTable::calcPrefWidths()
546 ASSERT(prefWidthsDirty());
548 recalcSectionsIfNeeded();
549 recalcHorizontalBorders();
551 m_tableLayout
->calcPrefWidths(m_minPrefWidth
, m_maxPrefWidth
);
554 m_minPrefWidth
= max(m_minPrefWidth
, m_caption
->minPrefWidth());
556 setPrefWidthsDirty(false);
559 void RenderTable::splitColumn(int pos
, int firstSpan
)
561 // we need to add a new columnStruct
562 int oldSize
= m_columns
.size();
563 m_columns
.grow(oldSize
+ 1);
564 int oldSpan
= m_columns
[pos
].span
;
565 ASSERT(oldSpan
> firstSpan
);
566 m_columns
[pos
].span
= firstSpan
;
567 memmove(m_columns
.data() + pos
+ 1, m_columns
.data() + pos
, (oldSize
- pos
) * sizeof(ColumnStruct
));
568 m_columns
[pos
+ 1].span
= oldSpan
- firstSpan
;
570 // change width of all rows.
571 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
572 if (child
->isTableSection())
573 static_cast<RenderTableSection
*>(child
)->splitColumn(pos
, oldSize
+ 1);
576 m_columnPos
.grow(numEffCols() + 1);
577 setNeedsLayoutAndPrefWidthsRecalc();
580 void RenderTable::appendColumn(int span
)
583 int pos
= m_columns
.size();
584 int newSize
= pos
+ 1;
585 m_columns
.grow(newSize
);
586 m_columns
[pos
].span
= span
;
588 // change width of all rows.
589 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
590 if (child
->isTableSection())
591 static_cast<RenderTableSection
*>(child
)->appendColumn(pos
);
594 m_columnPos
.grow(numEffCols() + 1);
595 setNeedsLayoutAndPrefWidthsRecalc();
598 RenderTableCol
* RenderTable::colElement(int col
, bool* startEdge
, bool* endEdge
) const
600 if (!m_hasColElements
)
602 RenderObject
* child
= firstChild();
606 if (child
->isTableCol()) {
607 RenderTableCol
* colElem
= static_cast<RenderTableCol
*>(child
);
608 int span
= colElem
->span();
609 if (!colElem
->firstChild()) {
611 int endCol
= cCol
+ span
- 1;
615 *startEdge
= startCol
== col
;
617 *endEdge
= endCol
== col
;
622 RenderObject
* next
= child
->firstChild();
624 next
= child
->nextSibling();
625 if (!next
&& child
->parent()->isTableCol())
626 next
= child
->parent()->nextSibling();
628 } else if (child
== m_caption
)
629 child
= child
->nextSibling();
637 void RenderTable::recalcSections() const
643 m_hasColElements
= false;
645 // We need to get valid pointers to caption, head, foot and first body again
646 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
647 switch (child
->style()->display()) {
649 if (!m_caption
&& child
->isRenderBlock()) {
650 m_caption
= toRenderBlock(child
);
651 m_caption
->setNeedsLayout(true);
655 case TABLE_COLUMN_GROUP
:
656 m_hasColElements
= true;
658 case TABLE_HEADER_GROUP
:
659 if (child
->isTableSection()) {
660 RenderTableSection
* section
= static_cast<RenderTableSection
*>(child
);
663 else if (!m_firstBody
)
664 m_firstBody
= section
;
665 section
->recalcCellsIfNeeded();
668 case TABLE_FOOTER_GROUP
:
669 if (child
->isTableSection()) {
670 RenderTableSection
* section
= static_cast<RenderTableSection
*>(child
);
673 else if (!m_firstBody
)
674 m_firstBody
= section
;
675 section
->recalcCellsIfNeeded();
678 case TABLE_ROW_GROUP
:
679 if (child
->isTableSection()) {
680 RenderTableSection
* section
= static_cast<RenderTableSection
*>(child
);
682 m_firstBody
= section
;
683 section
->recalcCellsIfNeeded();
691 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
693 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
694 if (child
->isTableSection()) {
695 RenderTableSection
* section
= static_cast<RenderTableSection
*>(child
);
696 int sectionCols
= section
->numColumns();
697 if (sectionCols
> maxCols
)
698 maxCols
= sectionCols
;
702 m_columns
.resize(maxCols
);
703 m_columnPos
.resize(maxCols
+ 1);
705 ASSERT(selfNeedsLayout());
707 m_needsSectionRecalc
= false;
710 int RenderTable::calcBorderLeft() const
712 if (collapseBorders()) {
713 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
717 unsigned borderWidth
= 0;
719 const BorderValue
& tb
= style()->borderLeft();
720 if (tb
.style() == BHIDDEN
)
722 if (tb
.style() > BHIDDEN
)
723 borderWidth
= tb
.width
;
725 int leftmostColumn
= style()->direction() == RTL
? numEffCols() - 1 : 0;
726 RenderTableCol
* colGroup
= colElement(leftmostColumn
);
728 const BorderValue
& gb
= style()->borderLeft();
729 if (gb
.style() == BHIDDEN
)
731 if (gb
.style() > BHIDDEN
)
732 borderWidth
= max(borderWidth
, static_cast<unsigned>(gb
.width
));
735 RenderTableSection
* firstNonEmptySection
= m_head
? m_head
: (m_firstBody
? m_firstBody
: m_foot
);
736 if (firstNonEmptySection
&& !firstNonEmptySection
->numRows())
737 firstNonEmptySection
= sectionBelow(firstNonEmptySection
, true);
739 if (firstNonEmptySection
) {
740 const BorderValue
& sb
= firstNonEmptySection
->style()->borderLeft();
741 if (sb
.style() == BHIDDEN
)
744 if (sb
.style() > BHIDDEN
)
745 borderWidth
= max(borderWidth
, static_cast<unsigned>(sb
.width
));
747 const RenderTableSection::CellStruct
& cs
= firstNonEmptySection
->cellAt(0, leftmostColumn
);
750 const BorderValue
& cb
= cs
.cell
->style()->borderLeft();
751 if (cb
.style() == BHIDDEN
)
754 const BorderValue
& rb
= cs
.cell
->parent()->style()->borderLeft();
755 if (rb
.style() == BHIDDEN
)
758 if (cb
.style() > BHIDDEN
)
759 borderWidth
= max(borderWidth
, static_cast<unsigned>(cb
.width
));
760 if (rb
.style() > BHIDDEN
)
761 borderWidth
= max(borderWidth
, static_cast<unsigned>(rb
.width
));
764 return borderWidth
/ 2;
766 return RenderBlock::borderLeft();
769 int RenderTable::calcBorderRight() const
771 if (collapseBorders()) {
772 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
776 unsigned borderWidth
= 0;
778 const BorderValue
& tb
= style()->borderRight();
779 if (tb
.style() == BHIDDEN
)
781 if (tb
.style() > BHIDDEN
)
782 borderWidth
= tb
.width
;
784 int rightmostColumn
= style()->direction() == RTL
? 0 : numEffCols() - 1;
785 RenderTableCol
* colGroup
= colElement(rightmostColumn
);
787 const BorderValue
& gb
= style()->borderRight();
788 if (gb
.style() == BHIDDEN
)
790 if (gb
.style() > BHIDDEN
)
791 borderWidth
= max(borderWidth
, static_cast<unsigned>(gb
.width
));
794 RenderTableSection
* firstNonEmptySection
= m_head
? m_head
: (m_firstBody
? m_firstBody
: m_foot
);
795 if (firstNonEmptySection
&& !firstNonEmptySection
->numRows())
796 firstNonEmptySection
= sectionBelow(firstNonEmptySection
, true);
798 if (firstNonEmptySection
) {
799 const BorderValue
& sb
= firstNonEmptySection
->style()->borderRight();
800 if (sb
.style() == BHIDDEN
)
803 if (sb
.style() > BHIDDEN
)
804 borderWidth
= max(borderWidth
, static_cast<unsigned>(sb
.width
));
806 const RenderTableSection::CellStruct
& cs
= firstNonEmptySection
->cellAt(0, rightmostColumn
);
809 const BorderValue
& cb
= cs
.cell
->style()->borderRight();
810 if (cb
.style() == BHIDDEN
)
813 const BorderValue
& rb
= cs
.cell
->parent()->style()->borderRight();
814 if (rb
.style() == BHIDDEN
)
817 if (cb
.style() > BHIDDEN
)
818 borderWidth
= max(borderWidth
, static_cast<unsigned>(cb
.width
));
819 if (rb
.style() > BHIDDEN
)
820 borderWidth
= max(borderWidth
, static_cast<unsigned>(rb
.width
));
823 return (borderWidth
+ 1) / 2;
825 return RenderBlock::borderRight();
828 void RenderTable::recalcHorizontalBorders()
830 m_borderLeft
= calcBorderLeft();
831 m_borderRight
= calcBorderRight();
834 int RenderTable::borderTop() const
836 if (collapseBorders())
837 return outerBorderTop();
838 return RenderBlock::borderTop();
841 int RenderTable::borderBottom() const
843 if (collapseBorders())
844 return outerBorderBottom();
845 return RenderBlock::borderBottom();
848 int RenderTable::outerBorderTop() const
850 if (!collapseBorders())
853 RenderTableSection
* topSection
;
856 else if (m_firstBody
)
857 topSection
= m_firstBody
;
863 borderWidth
= topSection
->outerBorderTop();
864 if (borderWidth
== -1)
865 return 0; // Overridden by hidden
867 const BorderValue
& tb
= style()->borderTop();
868 if (tb
.style() == BHIDDEN
)
870 if (tb
.style() > BHIDDEN
)
871 borderWidth
= max(borderWidth
, static_cast<int>(tb
.width
/ 2));
875 int RenderTable::outerBorderBottom() const
877 if (!collapseBorders())
880 RenderTableSection
* bottomSection
;
882 bottomSection
= m_foot
;
885 for (child
= lastChild(); child
&& !child
->isTableSection(); child
= child
->previousSibling())
887 bottomSection
= child
? static_cast<RenderTableSection
*>(child
) : 0;
890 borderWidth
= bottomSection
->outerBorderBottom();
891 if (borderWidth
== -1)
892 return 0; // Overridden by hidden
894 const BorderValue
& tb
= style()->borderBottom();
895 if (tb
.style() == BHIDDEN
)
897 if (tb
.style() > BHIDDEN
)
898 borderWidth
= max(borderWidth
, static_cast<int>((tb
.width
+ 1) / 2));
902 int RenderTable::outerBorderLeft() const
904 if (!collapseBorders())
909 const BorderValue
& tb
= style()->borderLeft();
910 if (tb
.style() == BHIDDEN
)
912 if (tb
.style() > BHIDDEN
)
913 borderWidth
= tb
.width
/ 2;
915 bool allHidden
= true;
916 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
917 if (!child
->isTableSection())
919 int sw
= static_cast<RenderTableSection
*>(child
)->outerBorderLeft();
924 borderWidth
= max(borderWidth
, sw
);
932 int RenderTable::outerBorderRight() const
934 if (!collapseBorders())
939 const BorderValue
& tb
= style()->borderRight();
940 if (tb
.style() == BHIDDEN
)
942 if (tb
.style() > BHIDDEN
)
943 borderWidth
= (tb
.width
+ 1) / 2;
945 bool allHidden
= true;
946 for (RenderObject
* child
= firstChild(); child
; child
= child
->nextSibling()) {
947 if (!child
->isTableSection())
949 int sw
= static_cast<RenderTableSection
*>(child
)->outerBorderRight();
954 borderWidth
= max(borderWidth
, sw
);
962 RenderTableSection
* RenderTable::sectionAbove(const RenderTableSection
* section
, bool skipEmptySections
) const
964 recalcSectionsIfNeeded();
966 if (section
== m_head
)
969 RenderObject
* prevSection
= section
== m_foot
? lastChild() : section
->previousSibling();
970 while (prevSection
) {
971 if (prevSection
->isTableSection() && prevSection
!= m_head
&& prevSection
!= m_foot
&& (!skipEmptySections
|| static_cast<RenderTableSection
*>(prevSection
)->numRows()))
973 prevSection
= prevSection
->previousSibling();
975 if (!prevSection
&& m_head
&& (!skipEmptySections
|| m_head
->numRows()))
976 prevSection
= m_head
;
977 return static_cast<RenderTableSection
*>(prevSection
);
980 RenderTableSection
* RenderTable::sectionBelow(const RenderTableSection
* section
, bool skipEmptySections
) const
982 recalcSectionsIfNeeded();
984 if (section
== m_foot
)
987 RenderObject
* nextSection
= section
== m_head
? firstChild() : section
->nextSibling();
988 while (nextSection
) {
989 if (nextSection
->isTableSection() && nextSection
!= m_head
&& nextSection
!= m_foot
&& (!skipEmptySections
|| static_cast<RenderTableSection
*>(nextSection
)->numRows()))
991 nextSection
= nextSection
->nextSibling();
993 if (!nextSection
&& m_foot
&& (!skipEmptySections
|| m_foot
->numRows()))
994 nextSection
= m_foot
;
995 return static_cast<RenderTableSection
*>(nextSection
);
998 RenderTableCell
* RenderTable::cellAbove(const RenderTableCell
* cell
) const
1000 recalcSectionsIfNeeded();
1002 // Find the section and row to look in
1003 int r
= cell
->row();
1004 RenderTableSection
* section
= 0;
1007 // cell is not in the first row, so use the above row in its own section
1008 section
= cell
->section();
1011 section
= sectionAbove(cell
->section(), true);
1013 rAbove
= section
->numRows() - 1;
1016 // Look up the cell in the section's grid, which requires effective col index
1018 int effCol
= colToEffCol(cell
->col());
1019 RenderTableSection::CellStruct aboveCell
;
1020 // If we hit a span back up to a real cell.
1022 aboveCell
= section
->cellAt(rAbove
, effCol
);
1024 } while (!aboveCell
.cell
&& aboveCell
.inColSpan
&& effCol
>= 0);
1025 return aboveCell
.cell
;
1030 RenderTableCell
* RenderTable::cellBelow(const RenderTableCell
* cell
) const
1032 recalcSectionsIfNeeded();
1034 // Find the section and row to look in
1035 int r
= cell
->row() + cell
->rowSpan() - 1;
1036 RenderTableSection
* section
= 0;
1038 if (r
< cell
->section()->numRows() - 1) {
1039 // The cell is not in the last row, so use the next row in the section.
1040 section
= cell
->section();
1043 section
= sectionBelow(cell
->section(), true);
1048 // Look up the cell in the section's grid, which requires effective col index
1050 int effCol
= colToEffCol(cell
->col());
1051 RenderTableSection::CellStruct belowCell
;
1052 // If we hit a colspan back up to a real cell.
1054 belowCell
= section
->cellAt(rBelow
, effCol
);
1056 } while (!belowCell
.cell
&& belowCell
.inColSpan
&& effCol
>= 0);
1057 return belowCell
.cell
;
1062 RenderTableCell
* RenderTable::cellBefore(const RenderTableCell
* cell
) const
1064 recalcSectionsIfNeeded();
1066 RenderTableSection
* section
= cell
->section();
1067 int effCol
= colToEffCol(cell
->col());
1071 // If we hit a colspan back up to a real cell.
1072 RenderTableSection::CellStruct prevCell
;
1074 prevCell
= section
->cellAt(cell
->row(), effCol
- 1);
1076 } while (!prevCell
.cell
&& prevCell
.inColSpan
&& effCol
>= 0);
1077 return prevCell
.cell
;
1080 RenderTableCell
* RenderTable::cellAfter(const RenderTableCell
* cell
) const
1082 recalcSectionsIfNeeded();
1084 int effCol
= colToEffCol(cell
->col() + cell
->colSpan());
1085 if (effCol
>= numEffCols())
1087 return cell
->section()->cellAt(cell
->row(), effCol
).cell
;
1090 RenderBlock
* RenderTable::firstLineBlock() const
1095 void RenderTable::updateFirstLetter()
1099 int RenderTable::firstLineBoxBaseline() const
1101 RenderTableSection
* firstNonEmptySection
= m_head
? m_head
: (m_firstBody
? m_firstBody
: m_foot
);
1102 if (firstNonEmptySection
&& !firstNonEmptySection
->numRows())
1103 firstNonEmptySection
= sectionBelow(firstNonEmptySection
, true);
1105 if (!firstNonEmptySection
)
1108 return firstNonEmptySection
->y() + firstNonEmptySection
->firstLineBoxBaseline();
1111 IntRect
RenderTable::overflowClipRect(int tx
, int ty
)
1113 IntRect rect
= RenderBlock::overflowClipRect(tx
, ty
);
1115 // If we have a caption, expand the clip to include the caption.
1116 // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1117 // for real until captions have been re-written.
1118 // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1119 // supported. When we actually support left/right and stop mapping them to top/bottom,
1120 // we might have to hack this code first (depending on what order we do these bug fixes in).
1122 rect
.setHeight(height());
1129 bool RenderTable::nodeAtPoint(const HitTestRequest
& request
, HitTestResult
& result
, int xPos
, int yPos
, int tx
, int ty
, HitTestAction action
)
1134 // Check kids first.
1135 if (!hasOverflowClip() || overflowClipRect(tx
, ty
).contains(xPos
, yPos
)) {
1136 for (RenderObject
* child
= lastChild(); child
; child
= child
->previousSibling()) {
1137 if (child
->isBox() && !toRenderBox(child
)->hasSelfPaintingLayer() && (child
->isTableSection() || child
== m_caption
) &&
1138 child
->nodeAtPoint(request
, result
, xPos
, yPos
, tx
, ty
, action
)) {
1139 updateHitTestResult(result
, IntPoint(xPos
- tx
, yPos
- ty
));
1145 // Check our bounds next.
1146 if (visibleToHitTesting() && (action
== HitTestBlockBackground
|| action
== HitTestChildBlockBackground
) && IntRect(tx
, ty
, width(), height()).contains(xPos
, yPos
)) {
1147 updateHitTestResult(result
, IntPoint(xPos
- tx
, yPos
- ty
));