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, 2009 Apple Inc. All rights reserved.
8 * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9 *
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.
14 *
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.
19 *
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.
24 */
25
26 #include "config.h"
27 #include "RenderTable.h"
28
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 #ifdef ANDROID_LAYOUT
40 #include "Settings.h"
41 #endif
42 #include "RenderView.h"
43
44 using namespace std;
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
RenderTable(Node * node)50 RenderTable::RenderTable(Node* node)
51 : RenderBlock(node)
52 , m_caption(0)
53 , m_head(0)
54 , m_foot(0)
55 , m_firstBody(0)
56 , m_currentBorder(0)
57 , m_hasColElements(false)
58 , m_needsSectionRecalc(0)
59 , m_hSpacing(0)
60 , m_vSpacing(0)
61 , m_borderLeft(0)
62 , m_borderRight(0)
63 {
64 #ifdef ANDROID_LAYOUT
65 m_singleColumn = false;
66 #endif
67 m_columnPos.fill(0, 2);
68 m_columns.fill(ColumnStruct(), 1);
69 }
70
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)71 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
72 {
73 RenderBlock::styleDidChange(diff, oldStyle);
74
75 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
76
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;
81
82 if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
83 // According to the CSS2 spec, you only use fixed table layout if an
84 // explicit width is specified on the table. Auto width implies auto table layout.
85 if (style()->tableLayout() == TFIXED && !style()->width().isAuto())
86 m_tableLayout.set(new FixedTableLayout(this));
87 else
88 m_tableLayout.set(new AutoTableLayout(this));
89 }
90 }
91
resetSectionPointerIfNotBefore(RenderTableSection * & ptr,RenderObject * before)92 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
93 {
94 if (!before || !ptr)
95 return;
96 RenderObject* o = before->previousSibling();
97 while (o && o != ptr)
98 o = o->previousSibling();
99 if (!o)
100 ptr = 0;
101 }
102
addChild(RenderObject * child,RenderObject * beforeChild)103 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
104 {
105 // Make sure we don't append things after :after-generated content if we have it.
106 if (!beforeChild && isAfterContent(lastChild()))
107 beforeChild = lastChild();
108
109 bool wrapInAnonymousSection = !child->isPositioned();
110
111 if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) {
112 // First caption wins.
113 if (beforeChild && m_caption) {
114 RenderObject* o = beforeChild->previousSibling();
115 while (o && o != m_caption)
116 o = o->previousSibling();
117 if (!o)
118 m_caption = 0;
119 }
120 if (!m_caption)
121 m_caption = toRenderBlock(child);
122 wrapInAnonymousSection = false;
123 } else if (child->isTableCol()) {
124 m_hasColElements = true;
125 wrapInAnonymousSection = false;
126 } else if (child->isTableSection()) {
127 switch (child->style()->display()) {
128 case TABLE_HEADER_GROUP:
129 resetSectionPointerIfNotBefore(m_head, beforeChild);
130 if (!m_head) {
131 m_head = toRenderTableSection(child);
132 } else {
133 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
134 if (!m_firstBody)
135 m_firstBody = toRenderTableSection(child);
136 }
137 wrapInAnonymousSection = false;
138 break;
139 case TABLE_FOOTER_GROUP:
140 resetSectionPointerIfNotBefore(m_foot, beforeChild);
141 if (!m_foot) {
142 m_foot = toRenderTableSection(child);
143 wrapInAnonymousSection = false;
144 break;
145 }
146 // Fall through.
147 case TABLE_ROW_GROUP:
148 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
149 if (!m_firstBody)
150 m_firstBody = toRenderTableSection(child);
151 wrapInAnonymousSection = false;
152 break;
153 default:
154 ASSERT_NOT_REACHED();
155 }
156 } else if (child->isTableCell() || child->isTableRow())
157 wrapInAnonymousSection = true;
158 else
159 wrapInAnonymousSection = true;
160
161 if (!wrapInAnonymousSection) {
162 // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that.
163 while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION)
164 beforeChild = beforeChild->parent();
165
166 RenderBox::addChild(child, beforeChild);
167 return;
168 }
169
170 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) {
171 lastChild()->addChild(child);
172 return;
173 }
174
175 RenderObject* lastBox = beforeChild;
176 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP)
177 lastBox = lastBox->parent();
178 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
179 lastBox->addChild(child, beforeChild);
180 return;
181 }
182
183 if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
184 beforeChild = 0;
185 RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */);
186 RefPtr<RenderStyle> newStyle = RenderStyle::create();
187 newStyle->inheritFrom(style());
188 newStyle->setDisplay(TABLE_ROW_GROUP);
189 section->setStyle(newStyle.release());
190 addChild(section, beforeChild);
191 section->addChild(child);
192 }
193
removeChild(RenderObject * oldChild)194 void RenderTable::removeChild(RenderObject* oldChild)
195 {
196 RenderBox::removeChild(oldChild);
197 setNeedsSectionRecalc();
198 }
199
calcWidth()200 void RenderTable::calcWidth()
201 {
202 #ifdef ANDROID_LAYOUT
203 if (view()->frameView()) {
204 const Settings* settings = document()->settings();
205 ASSERT(settings);
206 if (settings->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen) {
207 m_visibleWidth = view()->frameView()->screenWidth();
208 }
209 }
210 #endif
211
212 if (isPositioned())
213 calcAbsoluteHorizontal();
214
215 RenderBlock* cb = containingBlock();
216 int availableWidth = cb->availableWidth();
217
218 LengthType widthType = style()->width().type();
219 if (widthType > Relative && style()->width().isPositive()) {
220 // Percent or fixed table
221 setWidth(style()->width().calcMinValue(availableWidth));
222 setWidth(max(minPrefWidth(), width()));
223 } else {
224 // An auto width table should shrink to fit within the line width if necessary in order to
225 // avoid overlapping floats.
226 availableWidth = cb->lineWidth(y(), false);
227
228 // Subtract out any fixed margins from our available width for auto width tables.
229 int marginTotal = 0;
230 if (!style()->marginLeft().isAuto())
231 marginTotal += style()->marginLeft().calcValue(availableWidth);
232 if (!style()->marginRight().isAuto())
233 marginTotal += style()->marginRight().calcValue(availableWidth);
234
235 // Subtract out our margins to get the available content width.
236 int availContentWidth = max(0, availableWidth - marginTotal);
237
238 // Ensure we aren't bigger than our max width or smaller than our min width.
239 setWidth(min(availContentWidth, maxPrefWidth()));
240 }
241
242 setWidth(max(width(), minPrefWidth()));
243
244 // Finally, with our true width determined, compute our margins for real.
245 m_marginRight = 0;
246 m_marginLeft = 0;
247 #ifdef ANDROID_LAYOUT
248 // in SSR mode, we ignore left/right margin for table
249 if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR)
250 return;
251 #endif
252 calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth);
253 }
254
layout()255 void RenderTable::layout()
256 {
257 ASSERT(needsLayout());
258
259 if (layoutOnlyPositionedObjects())
260 return;
261
262 recalcSectionsIfNeeded();
263
264 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
265 LayoutStateMaintainer statePusher(view(), this, IntSize(x(), y()));
266
267 setHeight(0);
268 m_overflowHeight = 0;
269 m_overflowTop = 0;
270 initMaxMarginValues();
271
272 #ifdef ANDROID_LAYOUT
273 bool relayoutChildren = false;
274 int oldVisibleWidth = m_visibleWidth;
275 #endif
276
277 int oldWidth = width();
278 calcWidth();
279
280 #ifdef ANDROID_LAYOUT
281 if (oldVisibleWidth != m_visibleWidth
282 && document()->settings()->layoutAlgorithm() == Settings::kLayoutFitColumnToScreen)
283 relayoutChildren = true;
284 else if (document()->settings()->layoutAlgorithm() == Settings::kLayoutSSR) {
285 // if the width of a table is wider than its container width, or it has a nested table,
286 // we will render it with single column.
287 int cw = containingBlockWidthForContent();
288 bool shouldRenderAsSingleColumn = (width() > cw);
289 if (!shouldRenderAsSingleColumn) {
290 RenderObject* child = firstChild();
291 while (child) {
292 if (child->isTable()) {
293 shouldRenderAsSingleColumn = true;
294 break;
295 }
296 child = child->nextInPreOrder();
297 }
298 }
299
300 if (shouldRenderAsSingleColumn) {
301 m_singleColumn = true;
302 if (width() > cw)
303 setWidth(cw);
304 if (m_minPrefWidth > cw)
305 m_minPrefWidth = cw;
306 if (m_maxPrefWidth > cw)
307 m_maxPrefWidth = cw;
308 }
309 }
310 #endif
311 if (m_caption && width() != oldWidth)
312 m_caption->setNeedsLayout(true, false);
313
314 // FIXME: The optimisation below doesn't work since the internal table
315 // layout could have changed. we need to add a flag to the table
316 // layout that tells us if something has changed in the min max
317 // calculations to do it correctly.
318 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
319 m_tableLayout->layout();
320
321 setCellWidths();
322
323 // layout child objects
324 int calculatedHeight = 0;
325 int oldTableTop = m_caption ? m_caption->height() + m_caption->marginTop() + m_caption->marginBottom() : 0;
326
327 bool collapsing = collapseBorders();
328
329 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
330 #ifdef ANDROID_LAYOUT
331 if (relayoutChildren) {
332 child->setNeedsLayout(true, false);
333 if (!child->isTableSection()) {
334 child->layoutIfNeeded();
335 continue;
336 }
337 // fall through
338 }
339 #endif
340 if (child->isTableSection()) {
341 child->layoutIfNeeded();
342 RenderTableSection* section = toRenderTableSection(child);
343 calculatedHeight += section->calcRowHeight();
344 if (collapsing)
345 section->recalcOuterBorder();
346 ASSERT(!section->needsLayout());
347 } else if (child->isTableCol()) {
348 child->layoutIfNeeded();
349 ASSERT(!child->needsLayout());
350 }
351 }
352
353 // Only lay out one caption, since it's the only one we're going to end up painting.
354 if (m_caption)
355 m_caption->layoutIfNeeded();
356
357 m_overflowWidth = width() + (collapsing ? outerBorderRight() - borderRight() : 0);
358 m_overflowLeft = collapsing ? borderLeft() - outerBorderLeft() : 0;
359
360 // If any table section moved vertically, we will just repaint everything from that
361 // section down (it is quite unlikely that any of the following sections
362 // did not shift).
363 bool sectionMoved = false;
364 int movedSectionTop = 0;
365
366 // FIXME: Collapse caption margin.
367 if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) {
368 IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
369
370 m_caption->setLocation(m_caption->marginLeft(), height());
371 if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
372 m_caption->repaintDuringLayoutIfMoved(captionRect);
373
374 setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom());
375 m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false));
376 m_overflowWidth = max(m_overflowWidth, m_caption->x() + m_caption->overflowWidth(false));
377 m_overflowTop = min(m_overflowTop, m_caption->y() + m_caption->overflowTop(false));
378 m_overflowHeight = max(m_overflowHeight, m_caption->y() + m_caption->overflowHeight(false));
379
380 if (height() != oldTableTop) {
381 sectionMoved = true;
382 movedSectionTop = min(height(), oldTableTop);
383 }
384 }
385
386 int bpTop = borderTop() + (collapsing ? 0 : paddingTop());
387 int bpBottom = borderBottom() + (collapsing ? 0 : paddingBottom());
388
389 setHeight(height() + bpTop);
390
391 if (!isPositioned())
392 calcHeight();
393
394 Length h = style()->height();
395 int th = 0;
396 if (h.isFixed())
397 // Tables size as though CSS height includes border/padding.
398 th = h.value() - (bpTop + bpBottom);
399 else if (h.isPercent())
400 th = calcPercentageHeight(h);
401 th = max(0, th);
402
403 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
404 if (child->isTableSection())
405 // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one.
406 toRenderTableSection(child)->layoutRows(child == m_firstBody ? max(0, th - calculatedHeight) : 0);
407 }
408
409 if (!m_firstBody && th > calculatedHeight && !style()->htmlHacks()) {
410 // Completely empty tables (with no sections or anything) should at least honor specified height
411 // in strict mode.
412 setHeight(height() + th);
413 }
414
415 int bl = borderLeft();
416 if (!collapsing)
417 bl += paddingLeft();
418
419 // position the table sections
420 RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
421 while (section) {
422 if (!sectionMoved && section->y() != height()) {
423 sectionMoved = true;
424 movedSectionTop = min(height(), section->y()) + section->overflowTop(false);
425 }
426 section->setLocation(bl, height());
427
428 setHeight(height() + section->height());
429 m_overflowLeft = min(m_overflowLeft, section->x() + section->overflowLeft(false));
430 m_overflowWidth = max(m_overflowWidth, section->x() + section->overflowWidth(false));
431 m_overflowTop = min(m_overflowTop, section->y() + section->overflowTop(false));
432 m_overflowHeight = max(m_overflowHeight, section->y() + section->overflowHeight(false));
433 section = sectionBelow(section);
434 }
435
436 setHeight(height() + bpBottom);
437
438 if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) {
439 IntRect captionRect(m_caption->x(), m_caption->y(), m_caption->width(), m_caption->height());
440
441 m_caption->setLocation(m_caption->marginLeft(), height());
442 if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout())
443 m_caption->repaintDuringLayoutIfMoved(captionRect);
444
445 setHeight(height() + m_caption->height() + m_caption->marginTop() + m_caption->marginBottom());
446 m_overflowLeft = min(m_overflowLeft, m_caption->x() + m_caption->overflowLeft(false));
447 m_overflowWidth = max(m_overflowWidth, m_caption->x() + m_caption->overflowWidth(false));
448 }
449
450 if (isPositioned())
451 calcHeight();
452
453 m_overflowHeight = max(m_overflowHeight, height());
454
455 // table can be containing block of positioned elements.
456 // FIXME: Only pass true if width or height changed.
457 layoutPositionedObjects(true);
458
459 if (!hasOverflowClip()) {
460 int shadowLeft;
461 int shadowRight;
462 int shadowTop;
463 int shadowBottom;
464 style()->getBoxShadowExtent(shadowTop, shadowRight, shadowBottom, shadowLeft);
465
466 m_overflowLeft = min(m_overflowLeft, shadowLeft);
467 m_overflowWidth = max(m_overflowWidth, width() + shadowRight);
468 m_overflowTop = min(m_overflowTop, shadowTop);
469 m_overflowHeight = max(m_overflowHeight, height() + shadowBottom);
470
471 if (hasReflection()) {
472 IntRect reflection(reflectionBox());
473 m_overflowTop = min(m_overflowTop, reflection.y());
474 m_overflowHeight = max(m_overflowHeight, reflection.bottom());
475 m_overflowLeft = min(m_overflowLeft, reflection.x());
476 m_overflowHeight = max(m_overflowWidth, reflection.right());
477 }
478 }
479
480 statePusher.pop();
481
482 bool didFullRepaint = repainter.repaintAfterLayout();
483 // Repaint with our new bounds if they are different from our old bounds.
484 if (!didFullRepaint && sectionMoved)
485 repaintRectangle(IntRect(m_overflowLeft, movedSectionTop, m_overflowWidth - m_overflowLeft, m_overflowHeight - movedSectionTop));
486
487 setNeedsLayout(false);
488 }
489
setCellWidths()490 void RenderTable::setCellWidths()
491 {
492 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
493 if (child->isTableSection())
494 toRenderTableSection(child)->setCellWidths();
495 }
496 }
497
paint(PaintInfo & paintInfo,int tx,int ty)498 void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty)
499 {
500 tx += x();
501 ty += y();
502
503 PaintPhase paintPhase = paintInfo.phase;
504
505 int os = 2 * maximalOutlineSize(paintPhase);
506 if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + overflowHeight(false) <= paintInfo.rect.y() - os)
507 return;
508 if (tx + overflowLeft(false) >= paintInfo.rect.right() + os || tx + overflowWidth(false) <= paintInfo.rect.x() - os)
509 return;
510
511 bool pushedClip = pushContentsClip(paintInfo, tx, ty);
512 paintObject(paintInfo, tx, ty);
513 if (pushedClip)
514 popContentsClip(paintInfo, paintPhase, tx, ty);
515 }
516
paintObject(PaintInfo & paintInfo,int tx,int ty)517 void RenderTable::paintObject(PaintInfo& paintInfo, int tx, int ty)
518 {
519 PaintPhase paintPhase = paintInfo.phase;
520 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
521 paintBoxDecorations(paintInfo, tx, ty);
522
523 if (paintPhase == PaintPhaseMask) {
524 paintMask(paintInfo, tx, ty);
525 return;
526 }
527
528 // We're done. We don't bother painting any children.
529 if (paintPhase == PaintPhaseBlockBackground)
530 return;
531
532 // We don't paint our own background, but we do let the kids paint their backgrounds.
533 if (paintPhase == PaintPhaseChildBlockBackgrounds)
534 paintPhase = PaintPhaseChildBlockBackground;
535
536 PaintInfo info(paintInfo);
537 info.phase = paintPhase;
538 info.paintingRoot = paintingRootForChildren(paintInfo);
539
540 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
541 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption))
542 child->paint(info, tx, ty);
543 }
544
545 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
546 // Collect all the unique border styles that we want to paint in a sorted list. Once we
547 // have all the styles sorted, we then do individual passes, painting each style of border
548 // from lowest precedence to highest precedence.
549 info.phase = PaintPhaseCollapsedTableBorders;
550 RenderTableCell::CollapsedBorderStyles borderStyles;
551 RenderObject* stop = nextInPreOrderAfterChildren();
552 for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder())
553 if (o->isTableCell())
554 toRenderTableCell(o)->collectBorderStyles(borderStyles);
555 RenderTableCell::sortBorderStyles(borderStyles);
556 size_t count = borderStyles.size();
557 for (size_t i = 0; i < count; ++i) {
558 m_currentBorder = &borderStyles[i];
559 for (RenderObject* child = firstChild(); child; child = child->nextSibling())
560 if (child->isTableSection())
561 child->paint(info, tx, ty);
562 }
563 m_currentBorder = 0;
564 }
565 }
566
paintBoxDecorations(PaintInfo & paintInfo,int tx,int ty)567 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty)
568 {
569 int w = width();
570 int h = height();
571
572 // Account for the caption.
573 if (m_caption) {
574 int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop());
575 h -= captionHeight;
576 if (m_caption->style()->captionSide() != CAPBOTTOM)
577 ty += captionHeight;
578 }
579
580 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal);
581
582 paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), tx, ty, w, h);
583 paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset);
584
585 if (style()->hasBorder() && !collapseBorders())
586 paintBorder(paintInfo.context, tx, ty, w, h, style());
587 }
588
paintMask(PaintInfo & paintInfo,int tx,int ty)589 void RenderTable::paintMask(PaintInfo& paintInfo, int tx, int ty)
590 {
591 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
592 return;
593
594 int w = width();
595 int h = height();
596
597 // Account for the caption.
598 if (m_caption) {
599 int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop());
600 h -= captionHeight;
601 if (m_caption->style()->captionSide() != CAPBOTTOM)
602 ty += captionHeight;
603 }
604
605 paintMaskImages(paintInfo, tx, ty, w, h);
606 }
607
calcPrefWidths()608 void RenderTable::calcPrefWidths()
609 {
610 ASSERT(prefWidthsDirty());
611
612 recalcSectionsIfNeeded();
613 recalcHorizontalBorders();
614
615 m_tableLayout->calcPrefWidths(m_minPrefWidth, m_maxPrefWidth);
616
617 if (m_caption)
618 m_minPrefWidth = max(m_minPrefWidth, m_caption->minPrefWidth());
619
620 setPrefWidthsDirty(false);
621 }
622
splitColumn(int pos,int firstSpan)623 void RenderTable::splitColumn(int pos, int firstSpan)
624 {
625 // we need to add a new columnStruct
626 int oldSize = m_columns.size();
627 m_columns.grow(oldSize + 1);
628 int oldSpan = m_columns[pos].span;
629 ASSERT(oldSpan > firstSpan);
630 m_columns[pos].span = firstSpan;
631 memmove(m_columns.data() + pos + 1, m_columns.data() + pos, (oldSize - pos) * sizeof(ColumnStruct));
632 m_columns[pos + 1].span = oldSpan - firstSpan;
633
634 // change width of all rows.
635 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
636 if (child->isTableSection())
637 toRenderTableSection(child)->splitColumn(pos, oldSize + 1);
638 }
639
640 m_columnPos.grow(numEffCols() + 1);
641 setNeedsLayoutAndPrefWidthsRecalc();
642 }
643
appendColumn(int span)644 void RenderTable::appendColumn(int span)
645 {
646 // easy case.
647 int pos = m_columns.size();
648 int newSize = pos + 1;
649 m_columns.grow(newSize);
650 m_columns[pos].span = span;
651
652 // change width of all rows.
653 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
654 if (child->isTableSection())
655 toRenderTableSection(child)->appendColumn(pos);
656 }
657
658 m_columnPos.grow(numEffCols() + 1);
659 setNeedsLayoutAndPrefWidthsRecalc();
660 }
661
colElement(int col,bool * startEdge,bool * endEdge) const662 RenderTableCol* RenderTable::colElement(int col, bool* startEdge, bool* endEdge) const
663 {
664 if (!m_hasColElements)
665 return 0;
666 RenderObject* child = firstChild();
667 int cCol = 0;
668
669 while (child) {
670 if (child->isTableCol()) {
671 RenderTableCol* colElem = toRenderTableCol(child);
672 int span = colElem->span();
673 if (!colElem->firstChild()) {
674 int startCol = cCol;
675 int endCol = cCol + span - 1;
676 cCol += span;
677 if (cCol > col) {
678 if (startEdge)
679 *startEdge = startCol == col;
680 if (endEdge)
681 *endEdge = endCol == col;
682 return colElem;
683 }
684 }
685
686 RenderObject* next = child->firstChild();
687 if (!next)
688 next = child->nextSibling();
689 if (!next && child->parent()->isTableCol())
690 next = child->parent()->nextSibling();
691 child = next;
692 } else if (child == m_caption)
693 child = child->nextSibling();
694 else
695 break;
696 }
697
698 return 0;
699 }
700
recalcSections() const701 void RenderTable::recalcSections() const
702 {
703 m_caption = 0;
704 m_head = 0;
705 m_foot = 0;
706 m_firstBody = 0;
707 m_hasColElements = false;
708
709 // We need to get valid pointers to caption, head, foot and first body again
710 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
711 switch (child->style()->display()) {
712 case TABLE_CAPTION:
713 if (!m_caption && child->isRenderBlock()) {
714 m_caption = toRenderBlock(child);
715 m_caption->setNeedsLayout(true);
716 }
717 break;
718 case TABLE_COLUMN:
719 case TABLE_COLUMN_GROUP:
720 m_hasColElements = true;
721 break;
722 case TABLE_HEADER_GROUP:
723 if (child->isTableSection()) {
724 RenderTableSection* section = toRenderTableSection(child);
725 if (!m_head)
726 m_head = section;
727 else if (!m_firstBody)
728 m_firstBody = section;
729 section->recalcCellsIfNeeded();
730 }
731 break;
732 case TABLE_FOOTER_GROUP:
733 if (child->isTableSection()) {
734 RenderTableSection* section = toRenderTableSection(child);
735 if (!m_foot)
736 m_foot = section;
737 else if (!m_firstBody)
738 m_firstBody = section;
739 section->recalcCellsIfNeeded();
740 }
741 break;
742 case TABLE_ROW_GROUP:
743 if (child->isTableSection()) {
744 RenderTableSection* section = toRenderTableSection(child);
745 if (!m_firstBody)
746 m_firstBody = section;
747 section->recalcCellsIfNeeded();
748 }
749 break;
750 default:
751 break;
752 }
753 }
754
755 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
756 int maxCols = 0;
757 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
758 if (child->isTableSection()) {
759 RenderTableSection* section = toRenderTableSection(child);
760 int sectionCols = section->numColumns();
761 if (sectionCols > maxCols)
762 maxCols = sectionCols;
763 }
764 }
765
766 m_columns.resize(maxCols);
767 m_columnPos.resize(maxCols + 1);
768
769 ASSERT(selfNeedsLayout());
770
771 m_needsSectionRecalc = false;
772 }
773
calcBorderLeft() const774 int RenderTable::calcBorderLeft() const
775 {
776 if (collapseBorders()) {
777 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
778 if (!numEffCols())
779 return 0;
780
781 unsigned borderWidth = 0;
782
783 const BorderValue& tb = style()->borderLeft();
784 if (tb.style() == BHIDDEN)
785 return 0;
786 if (tb.style() > BHIDDEN)
787 borderWidth = tb.width;
788
789 int leftmostColumn = style()->direction() == RTL ? numEffCols() - 1 : 0;
790 RenderTableCol* colGroup = colElement(leftmostColumn);
791 if (colGroup) {
792 const BorderValue& gb = style()->borderLeft();
793 if (gb.style() == BHIDDEN)
794 return 0;
795 if (gb.style() > BHIDDEN)
796 borderWidth = max(borderWidth, static_cast<unsigned>(gb.width));
797 }
798
799 RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
800 if (firstNonEmptySection && !firstNonEmptySection->numRows())
801 firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
802
803 if (firstNonEmptySection) {
804 const BorderValue& sb = firstNonEmptySection->style()->borderLeft();
805 if (sb.style() == BHIDDEN)
806 return 0;
807
808 if (sb.style() > BHIDDEN)
809 borderWidth = max(borderWidth, static_cast<unsigned>(sb.width));
810
811 const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, leftmostColumn);
812
813 if (cs.cell) {
814 const BorderValue& cb = cs.cell->style()->borderLeft();
815 if (cb.style() == BHIDDEN)
816 return 0;
817
818 const BorderValue& rb = cs.cell->parent()->style()->borderLeft();
819 if (rb.style() == BHIDDEN)
820 return 0;
821
822 if (cb.style() > BHIDDEN)
823 borderWidth = max(borderWidth, static_cast<unsigned>(cb.width));
824 if (rb.style() > BHIDDEN)
825 borderWidth = max(borderWidth, static_cast<unsigned>(rb.width));
826 }
827 }
828 return borderWidth / 2;
829 }
830 return RenderBlock::borderLeft();
831 }
832
calcBorderRight() const833 int RenderTable::calcBorderRight() const
834 {
835 if (collapseBorders()) {
836 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
837 if (!numEffCols())
838 return 0;
839
840 unsigned borderWidth = 0;
841
842 const BorderValue& tb = style()->borderRight();
843 if (tb.style() == BHIDDEN)
844 return 0;
845 if (tb.style() > BHIDDEN)
846 borderWidth = tb.width;
847
848 int rightmostColumn = style()->direction() == RTL ? 0 : numEffCols() - 1;
849 RenderTableCol* colGroup = colElement(rightmostColumn);
850 if (colGroup) {
851 const BorderValue& gb = style()->borderRight();
852 if (gb.style() == BHIDDEN)
853 return 0;
854 if (gb.style() > BHIDDEN)
855 borderWidth = max(borderWidth, static_cast<unsigned>(gb.width));
856 }
857
858 RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
859 if (firstNonEmptySection && !firstNonEmptySection->numRows())
860 firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
861
862 if (firstNonEmptySection) {
863 const BorderValue& sb = firstNonEmptySection->style()->borderRight();
864 if (sb.style() == BHIDDEN)
865 return 0;
866
867 if (sb.style() > BHIDDEN)
868 borderWidth = max(borderWidth, static_cast<unsigned>(sb.width));
869
870 const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, rightmostColumn);
871
872 if (cs.cell) {
873 const BorderValue& cb = cs.cell->style()->borderRight();
874 if (cb.style() == BHIDDEN)
875 return 0;
876
877 const BorderValue& rb = cs.cell->parent()->style()->borderRight();
878 if (rb.style() == BHIDDEN)
879 return 0;
880
881 if (cb.style() > BHIDDEN)
882 borderWidth = max(borderWidth, static_cast<unsigned>(cb.width));
883 if (rb.style() > BHIDDEN)
884 borderWidth = max(borderWidth, static_cast<unsigned>(rb.width));
885 }
886 }
887 return (borderWidth + 1) / 2;
888 }
889 return RenderBlock::borderRight();
890 }
891
recalcHorizontalBorders()892 void RenderTable::recalcHorizontalBorders()
893 {
894 m_borderLeft = calcBorderLeft();
895 m_borderRight = calcBorderRight();
896 }
897
borderTop() const898 int RenderTable::borderTop() const
899 {
900 if (collapseBorders())
901 return outerBorderTop();
902 return RenderBlock::borderTop();
903 }
904
borderBottom() const905 int RenderTable::borderBottom() const
906 {
907 if (collapseBorders())
908 return outerBorderBottom();
909 return RenderBlock::borderBottom();
910 }
911
outerBorderTop() const912 int RenderTable::outerBorderTop() const
913 {
914 if (!collapseBorders())
915 return 0;
916 int borderWidth = 0;
917 RenderTableSection* topSection;
918 if (m_head)
919 topSection = m_head;
920 else if (m_firstBody)
921 topSection = m_firstBody;
922 else if (m_foot)
923 topSection = m_foot;
924 else
925 topSection = 0;
926 if (topSection) {
927 borderWidth = topSection->outerBorderTop();
928 if (borderWidth == -1)
929 return 0; // Overridden by hidden
930 }
931 const BorderValue& tb = style()->borderTop();
932 if (tb.style() == BHIDDEN)
933 return 0;
934 if (tb.style() > BHIDDEN)
935 borderWidth = max(borderWidth, static_cast<int>(tb.width / 2));
936 return borderWidth;
937 }
938
outerBorderBottom() const939 int RenderTable::outerBorderBottom() const
940 {
941 if (!collapseBorders())
942 return 0;
943 int borderWidth = 0;
944 RenderTableSection* bottomSection;
945 if (m_foot)
946 bottomSection = m_foot;
947 else {
948 RenderObject* child;
949 for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) { }
950 bottomSection = child ? toRenderTableSection(child) : 0;
951 }
952 if (bottomSection) {
953 borderWidth = bottomSection->outerBorderBottom();
954 if (borderWidth == -1)
955 return 0; // Overridden by hidden
956 }
957 const BorderValue& tb = style()->borderBottom();
958 if (tb.style() == BHIDDEN)
959 return 0;
960 if (tb.style() > BHIDDEN)
961 borderWidth = max(borderWidth, static_cast<int>((tb.width + 1) / 2));
962 return borderWidth;
963 }
964
outerBorderLeft() const965 int RenderTable::outerBorderLeft() const
966 {
967 if (!collapseBorders())
968 return 0;
969
970 int borderWidth = 0;
971
972 const BorderValue& tb = style()->borderLeft();
973 if (tb.style() == BHIDDEN)
974 return 0;
975 if (tb.style() > BHIDDEN)
976 borderWidth = tb.width / 2;
977
978 bool allHidden = true;
979 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
980 if (!child->isTableSection())
981 continue;
982 int sw = toRenderTableSection(child)->outerBorderLeft();
983 if (sw == -1)
984 continue;
985 else
986 allHidden = false;
987 borderWidth = max(borderWidth, sw);
988 }
989 if (allHidden)
990 return 0;
991
992 return borderWidth;
993 }
994
outerBorderRight() const995 int RenderTable::outerBorderRight() const
996 {
997 if (!collapseBorders())
998 return 0;
999
1000 int borderWidth = 0;
1001
1002 const BorderValue& tb = style()->borderRight();
1003 if (tb.style() == BHIDDEN)
1004 return 0;
1005 if (tb.style() > BHIDDEN)
1006 borderWidth = (tb.width + 1) / 2;
1007
1008 bool allHidden = true;
1009 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
1010 if (!child->isTableSection())
1011 continue;
1012 int sw = toRenderTableSection(child)->outerBorderRight();
1013 if (sw == -1)
1014 continue;
1015 else
1016 allHidden = false;
1017 borderWidth = max(borderWidth, sw);
1018 }
1019 if (allHidden)
1020 return 0;
1021
1022 return borderWidth;
1023 }
1024
sectionAbove(const RenderTableSection * section,bool skipEmptySections) const1025 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const
1026 {
1027 recalcSectionsIfNeeded();
1028
1029 if (section == m_head)
1030 return 0;
1031
1032 RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
1033 while (prevSection) {
1034 if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (!skipEmptySections || toRenderTableSection(prevSection)->numRows()))
1035 break;
1036 prevSection = prevSection->previousSibling();
1037 }
1038 if (!prevSection && m_head && (!skipEmptySections || m_head->numRows()))
1039 prevSection = m_head;
1040 return toRenderTableSection(prevSection);
1041 }
1042
sectionBelow(const RenderTableSection * section,bool skipEmptySections) const1043 RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const
1044 {
1045 recalcSectionsIfNeeded();
1046
1047 if (section == m_foot)
1048 return 0;
1049
1050 RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
1051 while (nextSection) {
1052 if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (!skipEmptySections || toRenderTableSection(nextSection)->numRows()))
1053 break;
1054 nextSection = nextSection->nextSibling();
1055 }
1056 if (!nextSection && m_foot && (!skipEmptySections || m_foot->numRows()))
1057 nextSection = m_foot;
1058 return toRenderTableSection(nextSection);
1059 }
1060
cellAbove(const RenderTableCell * cell) const1061 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
1062 {
1063 recalcSectionsIfNeeded();
1064
1065 // Find the section and row to look in
1066 int r = cell->row();
1067 RenderTableSection* section = 0;
1068 int rAbove = 0;
1069 if (r > 0) {
1070 // cell is not in the first row, so use the above row in its own section
1071 section = cell->section();
1072 rAbove = r - 1;
1073 } else {
1074 section = sectionAbove(cell->section(), true);
1075 if (section)
1076 rAbove = section->numRows() - 1;
1077 }
1078
1079 // Look up the cell in the section's grid, which requires effective col index
1080 if (section) {
1081 int effCol = colToEffCol(cell->col());
1082 RenderTableSection::CellStruct aboveCell;
1083 // If we hit a span back up to a real cell.
1084 do {
1085 aboveCell = section->cellAt(rAbove, effCol);
1086 effCol--;
1087 } while (!aboveCell.cell && aboveCell.inColSpan && effCol >= 0);
1088 return aboveCell.cell;
1089 } else
1090 return 0;
1091 }
1092
cellBelow(const RenderTableCell * cell) const1093 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
1094 {
1095 recalcSectionsIfNeeded();
1096
1097 // Find the section and row to look in
1098 int r = cell->row() + cell->rowSpan() - 1;
1099 RenderTableSection* section = 0;
1100 int rBelow = 0;
1101 if (r < cell->section()->numRows() - 1) {
1102 // The cell is not in the last row, so use the next row in the section.
1103 section = cell->section();
1104 rBelow = r + 1;
1105 } else {
1106 section = sectionBelow(cell->section(), true);
1107 if (section)
1108 rBelow = 0;
1109 }
1110
1111 // Look up the cell in the section's grid, which requires effective col index
1112 if (section) {
1113 int effCol = colToEffCol(cell->col());
1114 RenderTableSection::CellStruct belowCell;
1115 // If we hit a colspan back up to a real cell.
1116 do {
1117 belowCell = section->cellAt(rBelow, effCol);
1118 effCol--;
1119 } while (!belowCell.cell && belowCell.inColSpan && effCol >= 0);
1120 return belowCell.cell;
1121 } else
1122 return 0;
1123 }
1124
cellBefore(const RenderTableCell * cell) const1125 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1126 {
1127 recalcSectionsIfNeeded();
1128
1129 RenderTableSection* section = cell->section();
1130 int effCol = colToEffCol(cell->col());
1131 if (!effCol)
1132 return 0;
1133
1134 // If we hit a colspan back up to a real cell.
1135 RenderTableSection::CellStruct prevCell;
1136 do {
1137 prevCell = section->cellAt(cell->row(), effCol - 1);
1138 effCol--;
1139 } while (!prevCell.cell && prevCell.inColSpan && effCol >= 0);
1140 return prevCell.cell;
1141 }
1142
cellAfter(const RenderTableCell * cell) const1143 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
1144 {
1145 recalcSectionsIfNeeded();
1146
1147 int effCol = colToEffCol(cell->col() + cell->colSpan());
1148 if (effCol >= numEffCols())
1149 return 0;
1150 return cell->section()->cellAt(cell->row(), effCol).cell;
1151 }
1152
firstLineBlock() const1153 RenderBlock* RenderTable::firstLineBlock() const
1154 {
1155 return 0;
1156 }
1157
updateFirstLetter()1158 void RenderTable::updateFirstLetter()
1159 {
1160 }
1161
firstLineBoxBaseline() const1162 int RenderTable::firstLineBoxBaseline() const
1163 {
1164 RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot);
1165 if (firstNonEmptySection && !firstNonEmptySection->numRows())
1166 firstNonEmptySection = sectionBelow(firstNonEmptySection, true);
1167
1168 if (!firstNonEmptySection)
1169 return -1;
1170
1171 return firstNonEmptySection->y() + firstNonEmptySection->firstLineBoxBaseline();
1172 }
1173
overflowClipRect(int tx,int ty)1174 IntRect RenderTable::overflowClipRect(int tx, int ty)
1175 {
1176 IntRect rect = RenderBlock::overflowClipRect(tx, ty);
1177
1178 // If we have a caption, expand the clip to include the caption.
1179 // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1180 // for real until captions have been re-written.
1181 // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1182 // supported. When we actually support left/right and stop mapping them to top/bottom,
1183 // we might have to hack this code first (depending on what order we do these bug fixes in).
1184 if (m_caption) {
1185 rect.setHeight(height());
1186 rect.setY(ty);
1187 }
1188
1189 return rect;
1190 }
1191
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int xPos,int yPos,int tx,int ty,HitTestAction action)1192 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int xPos, int yPos, int tx, int ty, HitTestAction action)
1193 {
1194 tx += x();
1195 ty += y();
1196
1197 // Check kids first.
1198 if (!hasOverflowClip() || overflowClipRect(tx, ty).contains(xPos, yPos)) {
1199 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1200 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child == m_caption) &&
1201 child->nodeAtPoint(request, result, xPos, yPos, tx, ty, action)) {
1202 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
1203 return true;
1204 }
1205 }
1206 }
1207
1208 // Check our bounds next.
1209 if (visibleToHitTesting() && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && IntRect(tx, ty, width(), height()).contains(xPos, yPos)) {
1210 updateHitTestResult(result, IntPoint(xPos - tx, yPos - ty));
1211 return true;
1212 }
1213
1214 return false;
1215 }
1216
1217 }
1218