Updated WebKit from /home/shausman/src/webkit/trunk to qtwebkit-4.6-snapshot-29062009...
[qt-netbsd.git] / src / 3rdparty / webkit / WebCore / rendering / RenderTable.cpp
blob48b0d1c7496981923bac4e54c18f7d871213c0dc
1 /*
2 * Copyright (C) 1997 Martin Jones (mjones@kde.org)
3 * (C) 1997 Torben Weis (weis@kde.org)
4 * (C) 1998 Waldo Bastian (bastian@kde.org)
5 * (C) 1999 Lars Knoll (knoll@kde.org)
6 * (C) 1999 Antti Koivisto (koivisto@kde.org)
7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 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.
26 #include "config.h"
27 #include "RenderTable.h"
29 #include "AutoTableLayout.h"
30 #include "DeleteButtonController.h"
31 #include "Document.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"
41 using namespace std;
43 namespace WebCore {
45 using namespace HTMLNames;
47 RenderTable::RenderTable(Node* node)
48 : RenderBlock(node)
49 , m_caption(0)
50 , m_head(0)
51 , m_foot(0)
52 , m_firstBody(0)
53 , m_tableLayout(0)
54 , m_currentBorder(0)
55 , m_hasColElements(false)
56 , m_needsSectionRecalc(0)
57 , m_hSpacing(0)
58 , m_vSpacing(0)
59 , m_borderLeft(0)
60 , m_borderRight(0)
62 m_columnPos.fill(0, 2);
63 m_columns.fill(ColumnStruct(), 1);
66 RenderTable::~RenderTable()
68 delete m_tableLayout;
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) {
83 delete m_tableLayout;
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);
89 else
90 m_tableLayout = new AutoTableLayout(this);
94 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
96 if (!before || !ptr)
97 return;
98 RenderObject* o = before->previousSibling();
99 while (o && o != ptr)
100 o = o->previousSibling();
101 if (!o)
102 ptr = 0;
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();
119 if (!o)
120 m_caption = 0;
122 if (!m_caption)
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);
132 if (!m_head) {
133 m_head = static_cast<RenderTableSection*>(child);
134 } else {
135 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
136 if (!m_firstBody)
137 m_firstBody = static_cast<RenderTableSection*>(child);
139 wrapInAnonymousSection = false;
140 break;
141 case TABLE_FOOTER_GROUP:
142 resetSectionPointerIfNotBefore(m_foot, beforeChild);
143 if (!m_foot) {
144 m_foot = static_cast<RenderTableSection*>(child);
145 wrapInAnonymousSection = false;
146 break;
148 // Fall through.
149 case TABLE_ROW_GROUP:
150 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
151 if (!m_firstBody)
152 m_firstBody = static_cast<RenderTableSection*>(child);
153 wrapInAnonymousSection = false;
154 break;
155 default:
156 ASSERT_NOT_REACHED();
158 } else if (child->isTableCell() || child->isTableRow())
159 wrapInAnonymousSection = true;
160 else
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);
169 return;
172 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
173 lastChild()->addChild(child);
174 return;
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);
182 return;
185 if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
186 beforeChild = 0;
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()
204 if (isPositioned())
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()));
215 } else {
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.
221 int marginTotal = 0;
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.
237 m_marginRight = 0;
238 m_marginLeft = 0;
239 calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth);
242 void RenderTable::layout()
244 ASSERT(needsLayout());
246 if (layoutOnlyPositionedObjects())
247 return;
249 recalcSectionsIfNeeded();
251 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
252 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
254 setHeight(0);
255 m_overflowHeight = 0;
256 m_overflowTop = 0;
257 initMaxMarginValues();
259 int oldWidth = width();
260 calcWidth();
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();
272 setCellWidths();
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();
285 if (collapsing)
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.
295 if (m_caption)
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
303 // did not shift).
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) {
322 sectionMoved = true;
323 movedSectionTop = min(height(), oldTableTop);
327 int bpTop = borderTop() + (collapsing ? 0 : paddingTop());
328 int bpBottom = borderBottom() + (collapsing ? 0 : paddingBottom());
330 setHeight(height() + bpTop);
332 if (!isPositioned())
333 calcHeight();
335 Length h = style()->height();
336 int th = 0;
337 if (h.isFixed())
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);
342 th = max(0, th);
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
352 // in strict mode.
353 setHeight(height() + th);
356 int bl = borderLeft();
357 if (!collapsing)
358 bl += paddingLeft();
360 // position the table sections
361 RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
362 while (section) {
363 if (!sectionMoved && section->y() != height()) {
364 sectionMoved = true;
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));
391 if (isPositioned())
392 calcHeight();
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());
417 statePusher.pop();
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)
437 tx += x();
438 ty += y();
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)
444 return;
445 if (tx + overflowLeft(false) >= paintInfo.rect.right() + os || tx + overflowWidth(false) <= paintInfo.rect.x() - os)
446 return;
448 bool pushedClip = pushContentsClip(paintInfo, tx, ty);
449 paintObject(paintInfo, tx, ty);
450 if (pushedClip)
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);
462 return;
465 // We're done. We don't bother painting any children.
466 if (paintPhase == PaintPhaseBlockBackground)
467 return;
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);
500 m_currentBorder = 0;
504 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
506 int w = width();
507 int h = height();
509 // Account for the caption.
510 if (m_caption) {
511 int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop());
512 h -= captionHeight;
513 if (m_caption->style()->captionSide() != CAPBOTTOM)
514 ty += captionHeight;
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)
528 return;
530 int w = width();
531 int h = height();
533 // Account for the caption.
534 if (m_caption) {
535 int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop());
536 h -= captionHeight;
537 if (m_caption->style()->captionSide() != CAPBOTTOM)
538 ty += captionHeight;
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);
553 if (m_caption)
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)
582 // easy case.
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)
601 return 0;
602 RenderObject* child = firstChild();
603 int cCol = 0;
605 while (child) {
606 if (child->isTableCol()) {
607 RenderTableCol* colElem = static_cast<RenderTableCol*>(child);
608 int span = colElem->span();
609 if (!colElem->firstChild()) {
610 int startCol = cCol;
611 int endCol = cCol + span - 1;
612 cCol += span;
613 if (cCol > col) {
614 if (startEdge)
615 *startEdge = startCol == col;
616 if (endEdge)
617 *endEdge = endCol == col;
618 return colElem;
622 RenderObject* next = child->firstChild();
623 if (!next)
624 next = child->nextSibling();
625 if (!next && child->parent()->isTableCol())
626 next = child->parent()->nextSibling();
627 child = next;
628 } else if (child == m_caption)
629 child = child->nextSibling();
630 else
631 break;
634 return 0;
637 void RenderTable::recalcSections() const
639 m_caption = 0;
640 m_head = 0;
641 m_foot = 0;
642 m_firstBody = 0;
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()) {
648 case TABLE_CAPTION:
649 if (!m_caption && child->isRenderBlock()) {
650 m_caption = toRenderBlock(child);
651 m_caption->setNeedsLayout(true);
653 break;
654 case TABLE_COLUMN:
655 case TABLE_COLUMN_GROUP:
656 m_hasColElements = true;
657 break;
658 case TABLE_HEADER_GROUP:
659 if (child->isTableSection()) {
660 RenderTableSection* section = static_cast<RenderTableSection*>(child);
661 if (!m_head)
662 m_head = section;
663 else if (!m_firstBody)
664 m_firstBody = section;
665 section->recalcCellsIfNeeded();
667 break;
668 case TABLE_FOOTER_GROUP:
669 if (child->isTableSection()) {
670 RenderTableSection* section = static_cast<RenderTableSection*>(child);
671 if (!m_foot)
672 m_foot = section;
673 else if (!m_firstBody)
674 m_firstBody = section;
675 section->recalcCellsIfNeeded();
677 break;
678 case TABLE_ROW_GROUP:
679 if (child->isTableSection()) {
680 RenderTableSection* section = static_cast<RenderTableSection*>(child);
681 if (!m_firstBody)
682 m_firstBody = section;
683 section->recalcCellsIfNeeded();
685 break;
686 default:
687 break;
691 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
692 int maxCols = 0;
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.
714 if (!numEffCols())
715 return 0;
717 unsigned borderWidth = 0;
719 const BorderValue& tb = style()->borderLeft();
720 if (tb.style() == BHIDDEN)
721 return 0;
722 if (tb.style() > BHIDDEN)
723 borderWidth = tb.width;
725 int leftmostColumn = style()->direction() == RTL ? numEffCols() - 1 : 0;
726 RenderTableCol* colGroup = colElement(leftmostColumn);
727 if (colGroup) {
728 const BorderValue& gb = style()->borderLeft();
729 if (gb.style() == BHIDDEN)
730 return 0;
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)
742 return 0;
744 if (sb.style() > BHIDDEN)
745 borderWidth = max(borderWidth, static_cast<unsigned>(sb.width));
747 const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, leftmostColumn);
749 if (cs.cell) {
750 const BorderValue& cb = cs.cell->style()->borderLeft();
751 if (cb.style() == BHIDDEN)
752 return 0;
754 const BorderValue& rb = cs.cell->parent()->style()->borderLeft();
755 if (rb.style() == BHIDDEN)
756 return 0;
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.
773 if (!numEffCols())
774 return 0;
776 unsigned borderWidth = 0;
778 const BorderValue& tb = style()->borderRight();
779 if (tb.style() == BHIDDEN)
780 return 0;
781 if (tb.style() > BHIDDEN)
782 borderWidth = tb.width;
784 int rightmostColumn = style()->direction() == RTL ? 0 : numEffCols() - 1;
785 RenderTableCol* colGroup = colElement(rightmostColumn);
786 if (colGroup) {
787 const BorderValue& gb = style()->borderRight();
788 if (gb.style() == BHIDDEN)
789 return 0;
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)
801 return 0;
803 if (sb.style() > BHIDDEN)
804 borderWidth = max(borderWidth, static_cast<unsigned>(sb.width));
806 const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, rightmostColumn);
808 if (cs.cell) {
809 const BorderValue& cb = cs.cell->style()->borderRight();
810 if (cb.style() == BHIDDEN)
811 return 0;
813 const BorderValue& rb = cs.cell->parent()->style()->borderRight();
814 if (rb.style() == BHIDDEN)
815 return 0;
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())
851 return 0;
852 int borderWidth = 0;
853 RenderTableSection* topSection;
854 if (m_head)
855 topSection = m_head;
856 else if (m_firstBody)
857 topSection = m_firstBody;
858 else if (m_foot)
859 topSection = m_foot;
860 else
861 topSection = 0;
862 if (topSection) {
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)
869 return 0;
870 if (tb.style() > BHIDDEN)
871 borderWidth = max(borderWidth, static_cast<int>(tb.width / 2));
872 return borderWidth;
875 int RenderTable::outerBorderBottom() const
877 if (!collapseBorders())
878 return 0;
879 int borderWidth = 0;
880 RenderTableSection* bottomSection;
881 if (m_foot)
882 bottomSection = m_foot;
883 else {
884 RenderObject* child;
885 for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling())
887 bottomSection = child ? static_cast<RenderTableSection*>(child) : 0;
889 if (bottomSection) {
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)
896 return 0;
897 if (tb.style() > BHIDDEN)
898 borderWidth = max(borderWidth, static_cast<int>((tb.width + 1) / 2));
899 return borderWidth;
902 int RenderTable::outerBorderLeft() const
904 if (!collapseBorders())
905 return 0;
907 int borderWidth = 0;
909 const BorderValue& tb = style()->borderLeft();
910 if (tb.style() == BHIDDEN)
911 return 0;
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())
918 continue;
919 int sw = static_cast<RenderTableSection*>(child)->outerBorderLeft();
920 if (sw == -1)
921 continue;
922 else
923 allHidden = false;
924 borderWidth = max(borderWidth, sw);
926 if (allHidden)
927 return 0;
929 return borderWidth;
932 int RenderTable::outerBorderRight() const
934 if (!collapseBorders())
935 return 0;
937 int borderWidth = 0;
939 const BorderValue& tb = style()->borderRight();
940 if (tb.style() == BHIDDEN)
941 return 0;
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())
948 continue;
949 int sw = static_cast<RenderTableSection*>(child)->outerBorderRight();
950 if (sw == -1)
951 continue;
952 else
953 allHidden = false;
954 borderWidth = max(borderWidth, sw);
956 if (allHidden)
957 return 0;
959 return borderWidth;
962 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const
964 recalcSectionsIfNeeded();
966 if (section == m_head)
967 return 0;
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()))
972 break;
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)
985 return 0;
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()))
990 break;
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;
1005 int rAbove = 0;
1006 if (r > 0) {
1007 // cell is not in the first row, so use the above row in its own section
1008 section = cell->section();
1009 rAbove = r - 1;
1010 } else {
1011 section = sectionAbove(cell->section(), true);
1012 if (section)
1013 rAbove = section->numRows() - 1;
1016 // Look up the cell in the section's grid, which requires effective col index
1017 if (section) {
1018 int effCol = colToEffCol(cell->col());
1019 RenderTableSection::CellStruct aboveCell;
1020 // If we hit a span back up to a real cell.
1021 do {
1022 aboveCell = section->cellAt(rAbove, effCol);
1023 effCol--;
1024 } while (!aboveCell.cell && aboveCell.inColSpan && effCol >= 0);
1025 return aboveCell.cell;
1026 } else
1027 return 0;
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;
1037 int rBelow = 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();
1041 rBelow = r + 1;
1042 } else {
1043 section = sectionBelow(cell->section(), true);
1044 if (section)
1045 rBelow = 0;
1048 // Look up the cell in the section's grid, which requires effective col index
1049 if (section) {
1050 int effCol = colToEffCol(cell->col());
1051 RenderTableSection::CellStruct belowCell;
1052 // If we hit a colspan back up to a real cell.
1053 do {
1054 belowCell = section->cellAt(rBelow, effCol);
1055 effCol--;
1056 } while (!belowCell.cell && belowCell.inColSpan && effCol >= 0);
1057 return belowCell.cell;
1058 } else
1059 return 0;
1062 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1064 recalcSectionsIfNeeded();
1066 RenderTableSection* section = cell->section();
1067 int effCol = colToEffCol(cell->col());
1068 if (!effCol)
1069 return 0;
1071 // If we hit a colspan back up to a real cell.
1072 RenderTableSection::CellStruct prevCell;
1073 do {
1074 prevCell = section->cellAt(cell->row(), effCol - 1);
1075 effCol--;
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())
1086 return 0;
1087 return cell->section()->cellAt(cell->row(), effCol).cell;
1090 RenderBlock* RenderTable::firstLineBlock() const
1092 return 0;
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)
1106 return -1;
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).
1121 if (m_caption) {
1122 rect.setHeight(height());
1123 rect.setY(ty);
1126 return rect;
1129 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
1131 tx += x();
1132 ty += y();
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));
1140 return true;
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));
1148 return true;
1151 return false;