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, 2010 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 "core/rendering/RenderTable.h"
28
29 #include "HTMLNames.h"
30 #include "core/dom/Document.h"
31 #include "core/html/HTMLTableElement.h"
32 #include "core/frame/FrameView.h"
33 #include "core/rendering/AutoTableLayout.h"
34 #include "core/rendering/FixedTableLayout.h"
35 #include "core/rendering/GraphicsContextAnnotator.h"
36 #include "core/rendering/HitTestResult.h"
37 #include "core/rendering/LayoutRectRecorder.h"
38 #include "core/rendering/LayoutRepainter.h"
39 #include "core/rendering/RenderLayer.h"
40 #include "core/rendering/RenderTableCaption.h"
41 #include "core/rendering/RenderTableCell.h"
42 #include "core/rendering/RenderTableCol.h"
43 #include "core/rendering/RenderTableSection.h"
44 #include "core/rendering/RenderView.h"
45 #include "core/rendering/SubtreeLayoutScope.h"
46 #include "core/rendering/style/StyleInheritedData.h"
47 #include "platform/graphics/GraphicsContextStateSaver.h"
48
49 using namespace std;
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
RenderTable(Element * element)55 RenderTable::RenderTable(Element* element)
56 : RenderBlock(element)
57 , m_head(0)
58 , m_foot(0)
59 , m_firstBody(0)
60 , m_currentBorder(0)
61 , m_collapsedBordersValid(false)
62 , m_hasColElements(false)
63 , m_needsSectionRecalc(false)
64 , m_columnLogicalWidthChanged(false)
65 , m_columnRenderersValid(false)
66 , m_hSpacing(0)
67 , m_vSpacing(0)
68 , m_borderStart(0)
69 , m_borderEnd(0)
70 {
71 setChildrenInline(false);
72 m_columnPos.fill(0, 1);
73
74 }
75
~RenderTable()76 RenderTable::~RenderTable()
77 {
78 }
79
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)80 void RenderTable::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
81 {
82 RenderBlock::styleDidChange(diff, oldStyle);
83 propagateStyleToAnonymousChildren();
84
85 ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO;
86
87 // In the collapsed border model, there is no cell spacing.
88 m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing();
89 m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing();
90 m_columnPos[0] = m_hSpacing;
91
92 if (!m_tableLayout || style()->tableLayout() != oldTableLayout) {
93 if (m_tableLayout)
94 m_tableLayout->willChangeTableLayout();
95
96 // According to the CSS2 spec, you only use fixed table layout if an
97 // explicit width is specified on the table. Auto width implies auto table layout.
98 if (style()->tableLayout() == TFIXED && !style()->logicalWidth().isAuto())
99 m_tableLayout = adoptPtr(new FixedTableLayout(this));
100 else
101 m_tableLayout = adoptPtr(new AutoTableLayout(this));
102 }
103
104 // If border was changed, invalidate collapsed borders cache.
105 if (!needsLayout() && oldStyle && oldStyle->border() != style()->border())
106 invalidateCollapsedBorders();
107 }
108
resetSectionPointerIfNotBefore(RenderTableSection * & ptr,RenderObject * before)109 static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before)
110 {
111 if (!before || !ptr)
112 return;
113 RenderObject* o = before->previousSibling();
114 while (o && o != ptr)
115 o = o->previousSibling();
116 if (!o)
117 ptr = 0;
118 }
119
addChild(RenderObject * child,RenderObject * beforeChild)120 void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild)
121 {
122 bool wrapInAnonymousSection = !child->isOutOfFlowPositioned();
123
124 if (child->isTableCaption())
125 wrapInAnonymousSection = false;
126 else if (child->isRenderTableCol()) {
127 m_hasColElements = true;
128 wrapInAnonymousSection = false;
129 } else if (child->isTableSection()) {
130 switch (child->style()->display()) {
131 case TABLE_HEADER_GROUP:
132 resetSectionPointerIfNotBefore(m_head, beforeChild);
133 if (!m_head) {
134 m_head = toRenderTableSection(child);
135 } else {
136 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
137 if (!m_firstBody)
138 m_firstBody = toRenderTableSection(child);
139 }
140 wrapInAnonymousSection = false;
141 break;
142 case TABLE_FOOTER_GROUP:
143 resetSectionPointerIfNotBefore(m_foot, beforeChild);
144 if (!m_foot) {
145 m_foot = toRenderTableSection(child);
146 wrapInAnonymousSection = false;
147 break;
148 }
149 // Fall through.
150 case TABLE_ROW_GROUP:
151 resetSectionPointerIfNotBefore(m_firstBody, beforeChild);
152 if (!m_firstBody)
153 m_firstBody = toRenderTableSection(child);
154 wrapInAnonymousSection = false;
155 break;
156 default:
157 ASSERT_NOT_REACHED();
158 }
159 } else if (child->isTableCell() || child->isTableRow())
160 wrapInAnonymousSection = true;
161 else
162 wrapInAnonymousSection = true;
163
164 if (child->isTableSection())
165 setNeedsSectionRecalc();
166
167 if (!wrapInAnonymousSection) {
168 if (beforeChild && beforeChild->parent() != this)
169 beforeChild = splitAnonymousBoxesAroundChild(beforeChild);
170
171 RenderBox::addChild(child, beforeChild);
172 return;
173 }
174
175 if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous() && !lastChild()->isBeforeContent()) {
176 lastChild()->addChild(child);
177 return;
178 }
179
180 if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) {
181 RenderObject* section = beforeChild->previousSibling();
182 if (section && section->isTableSection() && section->isAnonymous()) {
183 section->addChild(child);
184 return;
185 }
186 }
187
188 RenderObject* lastBox = beforeChild;
189 while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP)
190 lastBox = lastBox->parent();
191 if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) {
192 if (beforeChild == lastBox)
193 beforeChild = lastBox->firstChild();
194 lastBox->addChild(child, beforeChild);
195 return;
196 }
197
198 if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP)
199 beforeChild = 0;
200
201 RenderTableSection* section = RenderTableSection::createAnonymousWithParentRenderer(this);
202 addChild(section, beforeChild);
203 section->addChild(child);
204 }
205
addCaption(const RenderTableCaption * caption)206 void RenderTable::addCaption(const RenderTableCaption* caption)
207 {
208 ASSERT(m_captions.find(caption) == kNotFound);
209 m_captions.append(const_cast<RenderTableCaption*>(caption));
210 }
211
removeCaption(const RenderTableCaption * oldCaption)212 void RenderTable::removeCaption(const RenderTableCaption* oldCaption)
213 {
214 size_t index = m_captions.find(oldCaption);
215 ASSERT(index != kNotFound);
216 if (index == kNotFound)
217 return;
218
219 m_captions.remove(index);
220 }
221
invalidateCachedColumns()222 void RenderTable::invalidateCachedColumns()
223 {
224 m_columnRenderersValid = false;
225 m_columnRenderers.resize(0);
226 }
227
addColumn(const RenderTableCol *)228 void RenderTable::addColumn(const RenderTableCol*)
229 {
230 invalidateCachedColumns();
231 }
232
removeColumn(const RenderTableCol *)233 void RenderTable::removeColumn(const RenderTableCol*)
234 {
235 invalidateCachedColumns();
236 // We don't really need to recompute our sections, but we need to update our
237 // column count and whether we have a column. Currently, we only have one
238 // size-fit-all flag but we may have to consider splitting it.
239 setNeedsSectionRecalc();
240 }
241
updateLogicalWidth()242 void RenderTable::updateLogicalWidth()
243 {
244 recalcSectionsIfNeeded();
245
246 if (isOutOfFlowPositioned()) {
247 LogicalExtentComputedValues computedValues;
248 computePositionedLogicalWidth(computedValues);
249 setLogicalWidth(computedValues.m_extent);
250 setLogicalLeft(computedValues.m_position);
251 setMarginStart(computedValues.m_margins.m_start);
252 setMarginEnd(computedValues.m_margins.m_end);
253 }
254
255 RenderBlock* cb = containingBlock();
256 RenderView* renderView = view();
257
258 LayoutUnit availableLogicalWidth = containingBlockLogicalWidthForContent();
259 bool hasPerpendicularContainingBlock = cb->style()->isHorizontalWritingMode() != style()->isHorizontalWritingMode();
260 LayoutUnit containerWidthInInlineDirection = hasPerpendicularContainingBlock ? perpendicularContainingBlockLogicalHeight() : availableLogicalWidth;
261
262 Length styleLogicalWidth = style()->logicalWidth();
263 if ((styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive()) || styleLogicalWidth.isIntrinsic())
264 setLogicalWidth(convertStyleLogicalWidthToComputedWidth(styleLogicalWidth, containerWidthInInlineDirection));
265 else {
266 // Subtract out any fixed margins from our available width for auto width tables.
267 LayoutUnit marginStart = minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView);
268 LayoutUnit marginEnd = minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView);
269 LayoutUnit marginTotal = marginStart + marginEnd;
270
271 // Subtract out our margins to get the available content width.
272 LayoutUnit availableContentLogicalWidth = max<LayoutUnit>(0, containerWidthInInlineDirection - marginTotal);
273 if (shrinkToAvoidFloats() && cb->containsFloats() && !hasPerpendicularContainingBlock) {
274 // FIXME: Work with regions someday.
275 availableContentLogicalWidth = shrinkLogicalWidthToAvoidFloats(marginStart, marginEnd, toRenderBlockFlow(cb), 0);
276 }
277
278 // Ensure we aren't bigger than our available width.
279 setLogicalWidth(min<int>(availableContentLogicalWidth, maxPreferredLogicalWidth()));
280 }
281
282 // Ensure we aren't bigger than our max-width style.
283 Length styleMaxLogicalWidth = style()->logicalMaxWidth();
284 if ((styleMaxLogicalWidth.isSpecified() && !styleMaxLogicalWidth.isNegative()) || styleMaxLogicalWidth.isIntrinsic()) {
285 LayoutUnit computedMaxLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMaxLogicalWidth, availableLogicalWidth);
286 setLogicalWidth(min<int>(logicalWidth(), computedMaxLogicalWidth));
287 }
288
289 // Ensure we aren't smaller than our min preferred width. This MUST be done after 'max-width' as
290 // we ignore it if it means we wouldn't accomodate our content.
291 setLogicalWidth(max<int>(logicalWidth(), minPreferredLogicalWidth()));
292
293 // Ensure we aren't smaller than our min-width style.
294 Length styleMinLogicalWidth = style()->logicalMinWidth();
295 if ((styleMinLogicalWidth.isSpecified() && !styleMinLogicalWidth.isNegative()) || styleMinLogicalWidth.isIntrinsic()) {
296 LayoutUnit computedMinLogicalWidth = convertStyleLogicalWidthToComputedWidth(styleMinLogicalWidth, availableLogicalWidth);
297 setLogicalWidth(max<int>(logicalWidth(), computedMinLogicalWidth));
298 }
299
300 // Finally, with our true width determined, compute our margins for real.
301 setMarginStart(0);
302 setMarginEnd(0);
303 if (!hasPerpendicularContainingBlock) {
304 LayoutUnit containerLogicalWidthForAutoMargins = availableLogicalWidth;
305 if (avoidsFloats() && cb->containsFloats())
306 containerLogicalWidthForAutoMargins = containingBlockAvailableLineWidthInRegion(0); // FIXME: Work with regions someday.
307 ComputedMarginValues marginValues;
308 bool hasInvertedDirection = cb->style()->isLeftToRightDirection() == style()->isLeftToRightDirection();
309 computeInlineDirectionMargins(cb, containerLogicalWidthForAutoMargins, logicalWidth(),
310 hasInvertedDirection ? marginValues.m_start : marginValues.m_end,
311 hasInvertedDirection ? marginValues.m_end : marginValues.m_start);
312 setMarginStart(marginValues.m_start);
313 setMarginEnd(marginValues.m_end);
314 } else {
315 setMarginStart(minimumValueForLength(style()->marginStart(), availableLogicalWidth, renderView));
316 setMarginEnd(minimumValueForLength(style()->marginEnd(), availableLogicalWidth, renderView));
317 }
318
319 // We should NEVER shrink the table below the min-content logical width, or else the table can't accomodate
320 // its own content which doesn't match CSS nor what authors expect.
321 // FIXME: When we convert to sub-pixel layout for tables we can remove the int conversion
322 // https://code.google.com/p/chromium/issues/detail?id=241198
323 ASSERT(logicalWidth().toInt() >= minPreferredLogicalWidth().toInt());
324 }
325
326 // This method takes a RenderStyle's logical width, min-width, or max-width length and computes its actual value.
convertStyleLogicalWidthToComputedWidth(const Length & styleLogicalWidth,LayoutUnit availableWidth)327 LayoutUnit RenderTable::convertStyleLogicalWidthToComputedWidth(const Length& styleLogicalWidth, LayoutUnit availableWidth)
328 {
329 if (styleLogicalWidth.isIntrinsic())
330 return computeIntrinsicLogicalWidthUsing(styleLogicalWidth, availableWidth, bordersPaddingAndSpacingInRowDirection());
331
332 // HTML tables' width styles already include borders and paddings, but CSS tables' width styles do not.
333 LayoutUnit borders = 0;
334 bool isCSSTable = !node() || !isHTMLTableElement(node());
335 if (isCSSTable && styleLogicalWidth.isSpecified() && styleLogicalWidth.isPositive() && style()->boxSizing() == CONTENT_BOX)
336 borders = borderStart() + borderEnd() + (collapseBorders() ? LayoutUnit() : paddingStart() + paddingEnd());
337
338 return minimumValueForLength(styleLogicalWidth, availableWidth, view()) + borders;
339 }
340
convertStyleLogicalHeightToComputedHeight(const Length & styleLogicalHeight)341 LayoutUnit RenderTable::convertStyleLogicalHeightToComputedHeight(const Length& styleLogicalHeight)
342 {
343 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapseBorders() ? LayoutUnit() : paddingBefore());
344 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapseBorders() ? LayoutUnit() : paddingAfter());
345 LayoutUnit borderAndPadding = borderAndPaddingBefore + borderAndPaddingAfter;
346 LayoutUnit computedLogicalHeight = 0;
347 if (styleLogicalHeight.isFixed()) {
348 // HTML tables size as though CSS height includes border/padding, CSS tables do not.
349 LayoutUnit borders = LayoutUnit();
350 // FIXME: We cannot apply box-sizing: content-box on <table> which other browsers allow.
351 if ((node() && isHTMLTableElement(node())) || style()->boxSizing() == BORDER_BOX) {
352 borders = borderAndPadding;
353 }
354 computedLogicalHeight = styleLogicalHeight.value() - borders;
355 } else if (styleLogicalHeight.isPercent())
356 computedLogicalHeight = computePercentageLogicalHeight(styleLogicalHeight);
357 else if (styleLogicalHeight.isViewportPercentage())
358 computedLogicalHeight = minimumValueForLength(styleLogicalHeight, 0, view());
359 else if (styleLogicalHeight.isIntrinsic())
360 computedLogicalHeight = computeIntrinsicLogicalContentHeightUsing(styleLogicalHeight, logicalHeight() - borderAndPadding, borderAndPadding);
361 else
362 ASSERT_NOT_REACHED();
363 return max<LayoutUnit>(0, computedLogicalHeight);
364 }
365
layoutCaption(RenderTableCaption * caption)366 void RenderTable::layoutCaption(RenderTableCaption* caption)
367 {
368 LayoutRect captionRect(caption->frameRect());
369
370 if (caption->needsLayout()) {
371 // The margins may not be available but ensure the caption is at least located beneath any previous sibling caption
372 // so that it does not mistakenly think any floats in the previous caption intrude into it.
373 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight()));
374 // If RenderTableCaption ever gets a layout() function, use it here.
375 caption->layoutIfNeeded();
376 }
377 // Apply the margins to the location now that they are definitely available from layout
378 caption->setLogicalLocation(LayoutPoint(caption->marginStart(), collapsedMarginBeforeForChild(caption) + logicalHeight()));
379
380 if (!selfNeedsLayout() && caption->checkForRepaintDuringLayout())
381 caption->repaintDuringLayoutIfMoved(captionRect);
382
383 setLogicalHeight(logicalHeight() + caption->logicalHeight() + collapsedMarginBeforeForChild(caption) + collapsedMarginAfterForChild(caption));
384 }
385
distributeExtraLogicalHeight(int extraLogicalHeight)386 void RenderTable::distributeExtraLogicalHeight(int extraLogicalHeight)
387 {
388 if (extraLogicalHeight <= 0)
389 return;
390
391 // FIXME: Distribute the extra logical height between all table sections instead of giving it all to the first one.
392 if (RenderTableSection* section = firstBody())
393 extraLogicalHeight -= section->distributeExtraLogicalHeightToRows(extraLogicalHeight);
394
395 // FIXME: We really would like to enable this ASSERT to ensure that all the extra space has been distributed.
396 // However our current distribution algorithm does not round properly and thus we can have some remaining height.
397 // ASSERT(!topSection() || !extraLogicalHeight);
398 }
399
simplifiedNormalFlowLayout()400 void RenderTable::simplifiedNormalFlowLayout()
401 {
402 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
403 section->layoutIfNeeded();
404 section->computeOverflowFromCells();
405 }
406 }
407
layout()408 void RenderTable::layout()
409 {
410 ASSERT(needsLayout());
411
412 LayoutRectRecorder recorder(*this);
413
414 if (simplifiedLayout())
415 return;
416
417 recalcSectionsIfNeeded();
418 // FIXME: We should do this recalc lazily in borderStart/borderEnd so that we don't have to make sure
419 // to call this before we call borderStart/borderEnd to avoid getting a stale value.
420 recalcBordersInRowDirection();
421
422 LayoutRepainter repainter(*this, checkForRepaintDuringLayout());
423 LayoutStateMaintainer statePusher(view(), this, locationOffset(), style()->isFlippedBlocksWritingMode());
424
425 setLogicalHeight(0);
426
427 LayoutUnit oldLogicalWidth = logicalWidth();
428 updateLogicalWidth();
429
430 SubtreeLayoutScope layouter(this);
431
432 if (logicalWidth() != oldLogicalWidth) {
433 for (unsigned i = 0; i < m_captions.size(); i++)
434 layouter.setNeedsLayout(m_captions[i]);
435 }
436 // FIXME: The optimisation below doesn't work since the internal table
437 // layout could have changed. we need to add a flag to the table
438 // layout that tells us if something has changed in the min max
439 // calculations to do it correctly.
440 // if ( oldWidth != width() || columns.size() + 1 != columnPos.size() )
441 m_tableLayout->layout();
442
443 LayoutUnit totalSectionLogicalHeight = 0;
444 LayoutUnit oldTableLogicalTop = 0;
445 for (unsigned i = 0; i < m_captions.size(); i++)
446 oldTableLogicalTop += m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
447
448 bool collapsing = collapseBorders();
449
450 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
451 if (child->isTableSection()) {
452 RenderTableSection* section = toRenderTableSection(child);
453 if (m_columnLogicalWidthChanged)
454 layouter.setChildNeedsLayout(section);
455 section->layoutIfNeeded();
456 totalSectionLogicalHeight += section->calcRowLogicalHeight();
457 if (collapsing)
458 section->recalcOuterBorder();
459 ASSERT(!section->needsLayout());
460 } else if (child->isRenderTableCol()) {
461 child->layoutIfNeeded();
462 ASSERT(!child->needsLayout());
463 } else {
464 // FIXME: We should never have other type of children (they should be wrapped in an
465 // anonymous table section) but our code is too crazy and this can happen in practice.
466 // Until this is fixed, let's make sure we don't leave non laid out children in the tree.
467 child->layoutIfNeeded();
468 }
469 }
470
471 // If any table section moved vertically, we will just repaint everything from that
472 // section down (it is quite unlikely that any of the following sections
473 // did not shift).
474 bool sectionMoved = false;
475 LayoutUnit movedSectionLogicalTop = 0;
476
477 // FIXME: Collapse caption margin.
478 if (!m_captions.isEmpty()) {
479 for (unsigned i = 0; i < m_captions.size(); i++) {
480 if (m_captions[i]->style()->captionSide() == CAPBOTTOM)
481 continue;
482 layoutCaption(m_captions[i]);
483 }
484 if (logicalHeight() != oldTableLogicalTop) {
485 sectionMoved = true;
486 movedSectionLogicalTop = min(logicalHeight(), oldTableLogicalTop);
487 }
488 }
489
490 LayoutUnit borderAndPaddingBefore = borderBefore() + (collapsing ? LayoutUnit() : paddingBefore());
491 LayoutUnit borderAndPaddingAfter = borderAfter() + (collapsing ? LayoutUnit() : paddingAfter());
492
493 setLogicalHeight(logicalHeight() + borderAndPaddingBefore);
494
495 if (!isOutOfFlowPositioned())
496 updateLogicalHeight();
497
498 LayoutUnit computedLogicalHeight = 0;
499
500 Length logicalHeightLength = style()->logicalHeight();
501 if (logicalHeightLength.isIntrinsic() || (logicalHeightLength.isSpecified() && logicalHeightLength.isPositive()))
502 computedLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalHeightLength);
503
504 Length logicalMaxHeightLength = style()->logicalMaxHeight();
505 if (logicalMaxHeightLength.isIntrinsic() || (logicalMaxHeightLength.isSpecified() && !logicalMaxHeightLength.isNegative())) {
506 LayoutUnit computedMaxLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMaxHeightLength);
507 computedLogicalHeight = min(computedLogicalHeight, computedMaxLogicalHeight);
508 }
509
510 Length logicalMinHeightLength = style()->logicalMinHeight();
511 if (logicalMinHeightLength.isIntrinsic() || (logicalMinHeightLength.isSpecified() && !logicalMinHeightLength.isNegative())) {
512 LayoutUnit computedMinLogicalHeight = convertStyleLogicalHeightToComputedHeight(logicalMinHeightLength);
513 computedLogicalHeight = max(computedLogicalHeight, computedMinLogicalHeight);
514 }
515
516 distributeExtraLogicalHeight(floorToInt(computedLogicalHeight - totalSectionLogicalHeight));
517
518 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
519 section->layoutRows();
520
521 if (!topSection() && computedLogicalHeight > totalSectionLogicalHeight && !document().inQuirksMode()) {
522 // Completely empty tables (with no sections or anything) should at least honor specified height
523 // in strict mode.
524 setLogicalHeight(logicalHeight() + computedLogicalHeight);
525 }
526
527 LayoutUnit sectionLogicalLeft = style()->isLeftToRightDirection() ? borderStart() : borderEnd();
528 if (!collapsing)
529 sectionLogicalLeft += style()->isLeftToRightDirection() ? paddingStart() : paddingEnd();
530
531 // position the table sections
532 RenderTableSection* section = topSection();
533 while (section) {
534 if (!sectionMoved && section->logicalTop() != logicalHeight()) {
535 sectionMoved = true;
536 movedSectionLogicalTop = min(logicalHeight(), section->logicalTop()) + (style()->isHorizontalWritingMode() ? section->visualOverflowRect().y() : section->visualOverflowRect().x());
537 }
538 section->setLogicalLocation(LayoutPoint(sectionLogicalLeft, logicalHeight()));
539
540 setLogicalHeight(logicalHeight() + section->logicalHeight());
541 section = sectionBelow(section);
542 }
543
544 setLogicalHeight(logicalHeight() + borderAndPaddingAfter);
545
546 for (unsigned i = 0; i < m_captions.size(); i++) {
547 if (m_captions[i]->style()->captionSide() != CAPBOTTOM)
548 continue;
549 layoutCaption(m_captions[i]);
550 }
551
552 if (isOutOfFlowPositioned())
553 updateLogicalHeight();
554
555 // table can be containing block of positioned elements.
556 // FIXME: Only pass true if width or height changed.
557 layoutPositionedObjects(true);
558
559 updateLayerTransform();
560
561 // Layout was changed, so probably borders too.
562 invalidateCollapsedBorders();
563
564 computeOverflow(clientLogicalBottom());
565
566 statePusher.pop();
567
568 if (view()->layoutState()->pageLogicalHeight())
569 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop()));
570
571 bool didFullRepaint = repainter.repaintAfterLayout();
572 // Repaint with our new bounds if they are different from our old bounds.
573 if (!didFullRepaint && sectionMoved) {
574 if (style()->isHorizontalWritingMode())
575 repaintRectangle(LayoutRect(visualOverflowRect().x(), movedSectionLogicalTop, visualOverflowRect().width(), visualOverflowRect().maxY() - movedSectionLogicalTop));
576 else
577 repaintRectangle(LayoutRect(movedSectionLogicalTop, visualOverflowRect().y(), visualOverflowRect().maxX() - movedSectionLogicalTop, visualOverflowRect().height()));
578 }
579
580 m_columnLogicalWidthChanged = false;
581 clearNeedsLayout();
582 }
583
584 // Collect all the unique border values that we want to paint in a sorted list.
recalcCollapsedBorders()585 void RenderTable::recalcCollapsedBorders()
586 {
587 if (m_collapsedBordersValid)
588 return;
589 m_collapsedBordersValid = true;
590 m_collapsedBorders.clear();
591 for (RenderObject* section = firstChild(); section; section = section->nextSibling()) {
592 if (!section->isTableSection())
593 continue;
594 for (RenderObject* row = section->firstChild(); row; row = row->nextSibling()) {
595 if (!row->isTableRow())
596 continue;
597 for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) {
598 if (!cell->isTableCell())
599 continue;
600 ASSERT(toRenderTableCell(cell)->table() == this);
601 toRenderTableCell(cell)->collectBorderValues(m_collapsedBorders);
602 }
603 }
604 }
605 RenderTableCell::sortBorderValues(m_collapsedBorders);
606 }
607
608
addOverflowFromChildren()609 void RenderTable::addOverflowFromChildren()
610 {
611 // Add overflow from borders.
612 // Technically it's odd that we are incorporating the borders into layout overflow, which is only supposed to be about overflow from our
613 // descendant objects, but since tables don't support overflow:auto, this works out fine.
614 if (collapseBorders()) {
615 int rightBorderOverflow = width() + outerBorderRight() - borderRight();
616 int leftBorderOverflow = borderLeft() - outerBorderLeft();
617 int bottomBorderOverflow = height() + outerBorderBottom() - borderBottom();
618 int topBorderOverflow = borderTop() - outerBorderTop();
619 IntRect borderOverflowRect(leftBorderOverflow, topBorderOverflow, rightBorderOverflow - leftBorderOverflow, bottomBorderOverflow - topBorderOverflow);
620 if (borderOverflowRect != pixelSnappedBorderBoxRect()) {
621 addLayoutOverflow(borderOverflowRect);
622 addVisualOverflow(borderOverflowRect);
623 }
624 }
625
626 // Add overflow from our caption.
627 for (unsigned i = 0; i < m_captions.size(); i++)
628 addOverflowFromChild(m_captions[i]);
629
630 // Add overflow from our sections.
631 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section))
632 addOverflowFromChild(section);
633 }
634
paint(PaintInfo & paintInfo,const LayoutPoint & paintOffset)635 void RenderTable::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
636 {
637 ANNOTATE_GRAPHICS_CONTEXT(paintInfo, this);
638
639 LayoutPoint adjustedPaintOffset = paintOffset + location();
640
641 PaintPhase paintPhase = paintInfo.phase;
642
643 if (!isRoot()) {
644 LayoutRect overflowBox = visualOverflowRect();
645 flipForWritingMode(overflowBox);
646 overflowBox.inflate(maximalOutlineSize(paintInfo.phase));
647 overflowBox.moveBy(adjustedPaintOffset);
648 if (!overflowBox.intersects(paintInfo.rect))
649 return;
650 }
651
652 bool pushedClip = pushContentsClip(paintInfo, adjustedPaintOffset, ForceContentsClip);
653 paintObject(paintInfo, adjustedPaintOffset);
654 if (pushedClip)
655 popContentsClip(paintInfo, paintPhase, adjustedPaintOffset);
656 }
657
paintObject(PaintInfo & paintInfo,const LayoutPoint & paintOffset)658 void RenderTable::paintObject(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
659 {
660 PaintPhase paintPhase = paintInfo.phase;
661 if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE)
662 paintBoxDecorations(paintInfo, paintOffset);
663
664 if (paintPhase == PaintPhaseMask) {
665 paintMask(paintInfo, paintOffset);
666 return;
667 }
668
669 // We're done. We don't bother painting any children.
670 if (paintPhase == PaintPhaseBlockBackground)
671 return;
672
673 // We don't paint our own background, but we do let the kids paint their backgrounds.
674 if (paintPhase == PaintPhaseChildBlockBackgrounds)
675 paintPhase = PaintPhaseChildBlockBackground;
676
677 PaintInfo info(paintInfo);
678 info.phase = paintPhase;
679 info.updatePaintingRootForChildren(this);
680
681 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
682 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
683 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), paintOffset);
684 child->paint(info, childPoint);
685 }
686 }
687
688 if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) {
689 recalcCollapsedBorders();
690 // Using our cached sorted styles, we then do individual passes,
691 // painting each style of border from lowest precedence to highest precedence.
692 info.phase = PaintPhaseCollapsedTableBorders;
693 size_t count = m_collapsedBorders.size();
694 for (size_t i = 0; i < count; ++i) {
695 m_currentBorder = &m_collapsedBorders[i];
696 for (RenderTableSection* section = bottomSection(); section; section = sectionAbove(section)) {
697 LayoutPoint childPoint = flipForWritingModeForChild(section, paintOffset);
698 section->paint(info, childPoint);
699 }
700 }
701 m_currentBorder = 0;
702 }
703
704 // Paint outline.
705 if ((paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE)
706 paintOutline(paintInfo, LayoutRect(paintOffset, size()));
707 }
708
subtractCaptionRect(LayoutRect & rect) const709 void RenderTable::subtractCaptionRect(LayoutRect& rect) const
710 {
711 for (unsigned i = 0; i < m_captions.size(); i++) {
712 LayoutUnit captionLogicalHeight = m_captions[i]->logicalHeight() + m_captions[i]->marginBefore() + m_captions[i]->marginAfter();
713 bool captionIsBefore = (m_captions[i]->style()->captionSide() != CAPBOTTOM) ^ style()->isFlippedBlocksWritingMode();
714 if (style()->isHorizontalWritingMode()) {
715 rect.setHeight(rect.height() - captionLogicalHeight);
716 if (captionIsBefore)
717 rect.move(0, captionLogicalHeight);
718 } else {
719 rect.setWidth(rect.width() - captionLogicalHeight);
720 if (captionIsBefore)
721 rect.move(captionLogicalHeight, 0);
722 }
723 }
724 }
725
paintBoxDecorations(PaintInfo & paintInfo,const LayoutPoint & paintOffset)726 void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
727 {
728 if (!paintInfo.shouldPaintWithinRoot(this))
729 return;
730
731 LayoutRect rect(paintOffset, size());
732 subtractCaptionRect(rect);
733 paintBoxDecorationsWithRect(paintInfo, paintOffset, rect);
734 }
735
paintMask(PaintInfo & paintInfo,const LayoutPoint & paintOffset)736 void RenderTable::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
737 {
738 if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
739 return;
740
741 LayoutRect rect(paintOffset, size());
742 subtractCaptionRect(rect);
743
744 paintMaskImages(paintInfo, rect);
745 }
746
computeIntrinsicLogicalWidths(LayoutUnit & minWidth,LayoutUnit & maxWidth) const747 void RenderTable::computeIntrinsicLogicalWidths(LayoutUnit& minWidth, LayoutUnit& maxWidth) const
748 {
749 recalcSectionsIfNeeded();
750 // FIXME: Do the recalc in borderStart/borderEnd and make those const_cast this call.
751 // Then m_borderStart/m_borderEnd will be transparent a cache and it removes the possibility
752 // of reading out stale values.
753 const_cast<RenderTable*>(this)->recalcBordersInRowDirection();
754 // FIXME: Restructure the table layout code so that we can make this method const.
755 const_cast<RenderTable*>(this)->m_tableLayout->computeIntrinsicLogicalWidths(minWidth, maxWidth);
756
757 // FIXME: We should include captions widths here like we do in computePreferredLogicalWidths.
758 }
759
computePreferredLogicalWidths()760 void RenderTable::computePreferredLogicalWidths()
761 {
762 ASSERT(preferredLogicalWidthsDirty());
763
764 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
765
766 int bordersPaddingAndSpacing = bordersPaddingAndSpacingInRowDirection();
767 m_minPreferredLogicalWidth += bordersPaddingAndSpacing;
768 m_maxPreferredLogicalWidth += bordersPaddingAndSpacing;
769
770 m_tableLayout->applyPreferredLogicalWidthQuirks(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
771
772 for (unsigned i = 0; i < m_captions.size(); i++)
773 m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, m_captions[i]->minPreferredLogicalWidth());
774
775 RenderStyle* styleToUse = style();
776 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for min-width.
777 if (styleToUse->logicalMinWidth().isFixed() && styleToUse->logicalMinWidth().value() > 0) {
778 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
779 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMinWidth().value()));
780 }
781
782 // FIXME: This should probably be checking for isSpecified since you should be able to use percentage, calc or viewport relative values for maxWidth.
783 if (styleToUse->logicalMaxWidth().isFixed()) {
784 // We don't constrain m_minPreferredLogicalWidth as the table should be at least the size of its min-content, regardless of 'max-width'.
785 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->logicalMaxWidth().value()));
786 m_maxPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth);
787 }
788
789 // FIXME: We should be adding borderAndPaddingLogicalWidth here, but m_tableLayout->computePreferredLogicalWidths already does,
790 // so a bunch of tests break doing this naively.
791 clearPreferredLogicalWidthsDirty();
792 }
793
topNonEmptySection() const794 RenderTableSection* RenderTable::topNonEmptySection() const
795 {
796 RenderTableSection* section = topSection();
797 if (section && !section->numRows())
798 section = sectionBelow(section, SkipEmptySections);
799 return section;
800 }
801
splitColumn(unsigned position,unsigned firstSpan)802 void RenderTable::splitColumn(unsigned position, unsigned firstSpan)
803 {
804 // We split the column at "position", taking "firstSpan" cells from the span.
805 ASSERT(m_columns[position].span > firstSpan);
806 m_columns.insert(position, ColumnStruct(firstSpan));
807 m_columns[position + 1].span -= firstSpan;
808
809 // Propagate the change in our columns representation to the sections that don't need
810 // cell recalc. If they do, they will be synced up directly with m_columns later.
811 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
812 if (!child->isTableSection())
813 continue;
814
815 RenderTableSection* section = toRenderTableSection(child);
816 if (section->needsCellRecalc())
817 continue;
818
819 section->splitColumn(position, firstSpan);
820 }
821
822 m_columnPos.grow(numEffCols() + 1);
823 }
824
appendColumn(unsigned span)825 void RenderTable::appendColumn(unsigned span)
826 {
827 unsigned newColumnIndex = m_columns.size();
828 m_columns.append(ColumnStruct(span));
829
830 // Propagate the change in our columns representation to the sections that don't need
831 // cell recalc. If they do, they will be synced up directly with m_columns later.
832 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
833 if (!child->isTableSection())
834 continue;
835
836 RenderTableSection* section = toRenderTableSection(child);
837 if (section->needsCellRecalc())
838 continue;
839
840 section->appendColumn(newColumnIndex);
841 }
842
843 m_columnPos.grow(numEffCols() + 1);
844 }
845
firstColumn() const846 RenderTableCol* RenderTable::firstColumn() const
847 {
848 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
849 if (child->isRenderTableCol())
850 return toRenderTableCol(child);
851 }
852
853 return 0;
854 }
855
updateColumnCache() const856 void RenderTable::updateColumnCache() const
857 {
858 ASSERT(m_hasColElements);
859 ASSERT(m_columnRenderers.isEmpty());
860 ASSERT(!m_columnRenderersValid);
861
862 for (RenderTableCol* columnRenderer = firstColumn(); columnRenderer; columnRenderer = columnRenderer->nextColumn()) {
863 if (columnRenderer->isTableColumnGroupWithColumnChildren())
864 continue;
865 m_columnRenderers.append(columnRenderer);
866 }
867 m_columnRenderersValid = true;
868 }
869
slowColElement(unsigned col,bool * startEdge,bool * endEdge) const870 RenderTableCol* RenderTable::slowColElement(unsigned col, bool* startEdge, bool* endEdge) const
871 {
872 ASSERT(m_hasColElements);
873
874 if (!m_columnRenderersValid)
875 updateColumnCache();
876
877 unsigned columnCount = 0;
878 for (unsigned i = 0; i < m_columnRenderers.size(); i++) {
879 RenderTableCol* columnRenderer = m_columnRenderers[i];
880 unsigned span = columnRenderer->span();
881 unsigned startCol = columnCount;
882 ASSERT(span >= 1);
883 unsigned endCol = columnCount + span - 1;
884 columnCount += span;
885 if (columnCount > col) {
886 if (startEdge)
887 *startEdge = startCol == col;
888 if (endEdge)
889 *endEdge = endCol == col;
890 return columnRenderer;
891 }
892 }
893 return 0;
894 }
895
recalcSections() const896 void RenderTable::recalcSections() const
897 {
898 ASSERT(m_needsSectionRecalc);
899
900 m_head = 0;
901 m_foot = 0;
902 m_firstBody = 0;
903 m_hasColElements = false;
904
905 // We need to get valid pointers to caption, head, foot and first body again
906 RenderObject* nextSibling;
907 for (RenderObject* child = firstChild(); child; child = nextSibling) {
908 nextSibling = child->nextSibling();
909 switch (child->style()->display()) {
910 case TABLE_COLUMN:
911 case TABLE_COLUMN_GROUP:
912 m_hasColElements = true;
913 break;
914 case TABLE_HEADER_GROUP:
915 if (child->isTableSection()) {
916 RenderTableSection* section = toRenderTableSection(child);
917 if (!m_head)
918 m_head = section;
919 else if (!m_firstBody)
920 m_firstBody = section;
921 section->recalcCellsIfNeeded();
922 }
923 break;
924 case TABLE_FOOTER_GROUP:
925 if (child->isTableSection()) {
926 RenderTableSection* section = toRenderTableSection(child);
927 if (!m_foot)
928 m_foot = section;
929 else if (!m_firstBody)
930 m_firstBody = section;
931 section->recalcCellsIfNeeded();
932 }
933 break;
934 case TABLE_ROW_GROUP:
935 if (child->isTableSection()) {
936 RenderTableSection* section = toRenderTableSection(child);
937 if (!m_firstBody)
938 m_firstBody = section;
939 section->recalcCellsIfNeeded();
940 }
941 break;
942 default:
943 break;
944 }
945 }
946
947 // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section)
948 unsigned maxCols = 0;
949 for (RenderObject* child = firstChild(); child; child = child->nextSibling()) {
950 if (child->isTableSection()) {
951 RenderTableSection* section = toRenderTableSection(child);
952 unsigned sectionCols = section->numColumns();
953 if (sectionCols > maxCols)
954 maxCols = sectionCols;
955 }
956 }
957
958 m_columns.resize(maxCols);
959 m_columnPos.resize(maxCols + 1);
960
961 ASSERT(selfNeedsLayout());
962
963 m_needsSectionRecalc = false;
964 }
965
calcBorderStart() const966 int RenderTable::calcBorderStart() const
967 {
968 if (!collapseBorders())
969 return RenderBlock::borderStart();
970
971 // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2.
972 if (!numEffCols())
973 return 0;
974
975 unsigned borderWidth = 0;
976
977 const BorderValue& tableStartBorder = style()->borderStart();
978 if (tableStartBorder.style() == BHIDDEN)
979 return 0;
980 if (tableStartBorder.style() > BHIDDEN)
981 borderWidth = tableStartBorder.width();
982
983 if (RenderTableCol* column = colElement(0)) {
984 // FIXME: We don't account for direction on columns and column groups.
985 const BorderValue& columnAdjoiningBorder = column->style()->borderStart();
986 if (columnAdjoiningBorder.style() == BHIDDEN)
987 return 0;
988 if (columnAdjoiningBorder.style() > BHIDDEN)
989 borderWidth = max(borderWidth, columnAdjoiningBorder.width());
990 // FIXME: This logic doesn't properly account for the first column in the first column-group case.
991 }
992
993 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
994 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableStart();
995 if (sectionAdjoiningBorder.style() == BHIDDEN)
996 return 0;
997
998 if (sectionAdjoiningBorder.style() > BHIDDEN)
999 borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
1000
1001 if (const RenderTableCell* adjoiningStartCell = topNonEmptySection->firstRowCellAdjoiningTableStart()) {
1002 // FIXME: Make this work with perpendicular and flipped cells.
1003 const BorderValue& startCellAdjoiningBorder = adjoiningStartCell->borderAdjoiningTableStart();
1004 if (startCellAdjoiningBorder.style() == BHIDDEN)
1005 return 0;
1006
1007 const BorderValue& firstRowAdjoiningBorder = adjoiningStartCell->row()->borderAdjoiningTableStart();
1008 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1009 return 0;
1010
1011 if (startCellAdjoiningBorder.style() > BHIDDEN)
1012 borderWidth = max(borderWidth, startCellAdjoiningBorder.width());
1013 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1014 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
1015 }
1016 }
1017 return (borderWidth + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1018 }
1019
calcBorderEnd() const1020 int RenderTable::calcBorderEnd() const
1021 {
1022 if (!collapseBorders())
1023 return RenderBlock::borderEnd();
1024
1025 // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2.
1026 if (!numEffCols())
1027 return 0;
1028
1029 unsigned borderWidth = 0;
1030
1031 const BorderValue& tableEndBorder = style()->borderEnd();
1032 if (tableEndBorder.style() == BHIDDEN)
1033 return 0;
1034 if (tableEndBorder.style() > BHIDDEN)
1035 borderWidth = tableEndBorder.width();
1036
1037 unsigned endColumn = numEffCols() - 1;
1038 if (RenderTableCol* column = colElement(endColumn)) {
1039 // FIXME: We don't account for direction on columns and column groups.
1040 const BorderValue& columnAdjoiningBorder = column->style()->borderEnd();
1041 if (columnAdjoiningBorder.style() == BHIDDEN)
1042 return 0;
1043 if (columnAdjoiningBorder.style() > BHIDDEN)
1044 borderWidth = max(borderWidth, columnAdjoiningBorder.width());
1045 // FIXME: This logic doesn't properly account for the last column in the last column-group case.
1046 }
1047
1048 if (const RenderTableSection* topNonEmptySection = this->topNonEmptySection()) {
1049 const BorderValue& sectionAdjoiningBorder = topNonEmptySection->borderAdjoiningTableEnd();
1050 if (sectionAdjoiningBorder.style() == BHIDDEN)
1051 return 0;
1052
1053 if (sectionAdjoiningBorder.style() > BHIDDEN)
1054 borderWidth = max(borderWidth, sectionAdjoiningBorder.width());
1055
1056 if (const RenderTableCell* adjoiningEndCell = topNonEmptySection->firstRowCellAdjoiningTableEnd()) {
1057 // FIXME: Make this work with perpendicular and flipped cells.
1058 const BorderValue& endCellAdjoiningBorder = adjoiningEndCell->borderAdjoiningTableEnd();
1059 if (endCellAdjoiningBorder.style() == BHIDDEN)
1060 return 0;
1061
1062 const BorderValue& firstRowAdjoiningBorder = adjoiningEndCell->row()->borderAdjoiningTableEnd();
1063 if (firstRowAdjoiningBorder.style() == BHIDDEN)
1064 return 0;
1065
1066 if (endCellAdjoiningBorder.style() > BHIDDEN)
1067 borderWidth = max(borderWidth, endCellAdjoiningBorder.width());
1068 if (firstRowAdjoiningBorder.style() > BHIDDEN)
1069 borderWidth = max(borderWidth, firstRowAdjoiningBorder.width());
1070 }
1071 }
1072 return (borderWidth + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1073 }
1074
recalcBordersInRowDirection()1075 void RenderTable::recalcBordersInRowDirection()
1076 {
1077 // FIXME: We need to compute the collapsed before / after borders in the same fashion.
1078 m_borderStart = calcBorderStart();
1079 m_borderEnd = calcBorderEnd();
1080 }
1081
borderBefore() const1082 int RenderTable::borderBefore() const
1083 {
1084 if (collapseBorders()) {
1085 recalcSectionsIfNeeded();
1086 return outerBorderBefore();
1087 }
1088 return RenderBlock::borderBefore();
1089 }
1090
borderAfter() const1091 int RenderTable::borderAfter() const
1092 {
1093 if (collapseBorders()) {
1094 recalcSectionsIfNeeded();
1095 return outerBorderAfter();
1096 }
1097 return RenderBlock::borderAfter();
1098 }
1099
outerBorderBefore() const1100 int RenderTable::outerBorderBefore() const
1101 {
1102 if (!collapseBorders())
1103 return 0;
1104 int borderWidth = 0;
1105 if (RenderTableSection* topSection = this->topSection()) {
1106 borderWidth = topSection->outerBorderBefore();
1107 if (borderWidth < 0)
1108 return 0; // Overridden by hidden
1109 }
1110 const BorderValue& tb = style()->borderBefore();
1111 if (tb.style() == BHIDDEN)
1112 return 0;
1113 if (tb.style() > BHIDDEN)
1114 borderWidth = max<int>(borderWidth, tb.width() / 2);
1115 return borderWidth;
1116 }
1117
outerBorderAfter() const1118 int RenderTable::outerBorderAfter() const
1119 {
1120 if (!collapseBorders())
1121 return 0;
1122 int borderWidth = 0;
1123
1124 if (RenderTableSection* section = bottomSection()) {
1125 borderWidth = section->outerBorderAfter();
1126 if (borderWidth < 0)
1127 return 0; // Overridden by hidden
1128 }
1129 const BorderValue& tb = style()->borderAfter();
1130 if (tb.style() == BHIDDEN)
1131 return 0;
1132 if (tb.style() > BHIDDEN)
1133 borderWidth = max<int>(borderWidth, (tb.width() + 1) / 2);
1134 return borderWidth;
1135 }
1136
outerBorderStart() const1137 int RenderTable::outerBorderStart() const
1138 {
1139 if (!collapseBorders())
1140 return 0;
1141
1142 int borderWidth = 0;
1143
1144 const BorderValue& tb = style()->borderStart();
1145 if (tb.style() == BHIDDEN)
1146 return 0;
1147 if (tb.style() > BHIDDEN)
1148 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 0 : 1)) / 2;
1149
1150 bool allHidden = true;
1151 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1152 int sw = section->outerBorderStart();
1153 if (sw < 0)
1154 continue;
1155 allHidden = false;
1156 borderWidth = max(borderWidth, sw);
1157 }
1158 if (allHidden)
1159 return 0;
1160
1161 return borderWidth;
1162 }
1163
outerBorderEnd() const1164 int RenderTable::outerBorderEnd() const
1165 {
1166 if (!collapseBorders())
1167 return 0;
1168
1169 int borderWidth = 0;
1170
1171 const BorderValue& tb = style()->borderEnd();
1172 if (tb.style() == BHIDDEN)
1173 return 0;
1174 if (tb.style() > BHIDDEN)
1175 borderWidth = (tb.width() + (style()->isLeftToRightDirection() ? 1 : 0)) / 2;
1176
1177 bool allHidden = true;
1178 for (RenderTableSection* section = topSection(); section; section = sectionBelow(section)) {
1179 int sw = section->outerBorderEnd();
1180 if (sw < 0)
1181 continue;
1182 allHidden = false;
1183 borderWidth = max(borderWidth, sw);
1184 }
1185 if (allHidden)
1186 return 0;
1187
1188 return borderWidth;
1189 }
1190
sectionAbove(const RenderTableSection * section,SkipEmptySectionsValue skipEmptySections) const1191 RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1192 {
1193 recalcSectionsIfNeeded();
1194
1195 if (section == m_head)
1196 return 0;
1197
1198 RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling();
1199 while (prevSection) {
1200 if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(prevSection)->numRows()))
1201 break;
1202 prevSection = prevSection->previousSibling();
1203 }
1204 if (!prevSection && m_head && (skipEmptySections == DoNotSkipEmptySections || m_head->numRows()))
1205 prevSection = m_head;
1206 return toRenderTableSection(prevSection);
1207 }
1208
sectionBelow(const RenderTableSection * section,SkipEmptySectionsValue skipEmptySections) const1209 RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, SkipEmptySectionsValue skipEmptySections) const
1210 {
1211 recalcSectionsIfNeeded();
1212
1213 if (section == m_foot)
1214 return 0;
1215
1216 RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling();
1217 while (nextSection) {
1218 if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (skipEmptySections == DoNotSkipEmptySections || toRenderTableSection(nextSection)->numRows()))
1219 break;
1220 nextSection = nextSection->nextSibling();
1221 }
1222 if (!nextSection && m_foot && (skipEmptySections == DoNotSkipEmptySections || m_foot->numRows()))
1223 nextSection = m_foot;
1224 return toRenderTableSection(nextSection);
1225 }
1226
bottomSection() const1227 RenderTableSection* RenderTable::bottomSection() const
1228 {
1229 recalcSectionsIfNeeded();
1230
1231 if (m_foot)
1232 return m_foot;
1233
1234 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1235 if (child->isTableSection())
1236 return toRenderTableSection(child);
1237 }
1238
1239 return 0;
1240 }
1241
cellAbove(const RenderTableCell * cell) const1242 RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const
1243 {
1244 recalcSectionsIfNeeded();
1245
1246 // Find the section and row to look in
1247 unsigned r = cell->rowIndex();
1248 RenderTableSection* section = 0;
1249 unsigned rAbove = 0;
1250 if (r > 0) {
1251 // cell is not in the first row, so use the above row in its own section
1252 section = cell->section();
1253 rAbove = r - 1;
1254 } else {
1255 section = sectionAbove(cell->section(), SkipEmptySections);
1256 if (section) {
1257 ASSERT(section->numRows());
1258 rAbove = section->numRows() - 1;
1259 }
1260 }
1261
1262 // Look up the cell in the section's grid, which requires effective col index
1263 if (section) {
1264 unsigned effCol = colToEffCol(cell->col());
1265 RenderTableSection::CellStruct& aboveCell = section->cellAt(rAbove, effCol);
1266 return aboveCell.primaryCell();
1267 } else
1268 return 0;
1269 }
1270
cellBelow(const RenderTableCell * cell) const1271 RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const
1272 {
1273 recalcSectionsIfNeeded();
1274
1275 // Find the section and row to look in
1276 unsigned r = cell->rowIndex() + cell->rowSpan() - 1;
1277 RenderTableSection* section = 0;
1278 unsigned rBelow = 0;
1279 if (r < cell->section()->numRows() - 1) {
1280 // The cell is not in the last row, so use the next row in the section.
1281 section = cell->section();
1282 rBelow = r + 1;
1283 } else {
1284 section = sectionBelow(cell->section(), SkipEmptySections);
1285 if (section)
1286 rBelow = 0;
1287 }
1288
1289 // Look up the cell in the section's grid, which requires effective col index
1290 if (section) {
1291 unsigned effCol = colToEffCol(cell->col());
1292 RenderTableSection::CellStruct& belowCell = section->cellAt(rBelow, effCol);
1293 return belowCell.primaryCell();
1294 } else
1295 return 0;
1296 }
1297
cellBefore(const RenderTableCell * cell) const1298 RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const
1299 {
1300 recalcSectionsIfNeeded();
1301
1302 RenderTableSection* section = cell->section();
1303 unsigned effCol = colToEffCol(cell->col());
1304 if (!effCol)
1305 return 0;
1306
1307 // If we hit a colspan back up to a real cell.
1308 RenderTableSection::CellStruct& prevCell = section->cellAt(cell->rowIndex(), effCol - 1);
1309 return prevCell.primaryCell();
1310 }
1311
cellAfter(const RenderTableCell * cell) const1312 RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const
1313 {
1314 recalcSectionsIfNeeded();
1315
1316 unsigned effCol = colToEffCol(cell->col() + cell->colSpan());
1317 if (effCol >= numEffCols())
1318 return 0;
1319 return cell->section()->primaryCellAt(cell->rowIndex(), effCol);
1320 }
1321
firstLineBlock() const1322 RenderBlock* RenderTable::firstLineBlock() const
1323 {
1324 return 0;
1325 }
1326
updateFirstLetter()1327 void RenderTable::updateFirstLetter()
1328 {
1329 }
1330
baselinePosition(FontBaseline baselineType,bool firstLine,LineDirectionMode direction,LinePositionMode linePositionMode) const1331 int RenderTable::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
1332 {
1333 ASSERT(linePositionMode == PositionOnContainingLine);
1334 LayoutUnit baseline = firstLineBoxBaseline();
1335 if (baseline != -1)
1336 return baseline;
1337
1338 return RenderBox::baselinePosition(baselineType, firstLine, direction, linePositionMode);
1339 }
1340
inlineBlockBaseline(LineDirectionMode) const1341 int RenderTable::inlineBlockBaseline(LineDirectionMode) const
1342 {
1343 // Tables are skipped when computing an inline-block's baseline.
1344 return -1;
1345 }
1346
firstLineBoxBaseline() const1347 int RenderTable::firstLineBoxBaseline() const
1348 {
1349 // The baseline of a 'table' is the same as the 'inline-table' baseline per CSS 3 Flexbox (CSS 2.1
1350 // doesn't define the baseline of a 'table' only an 'inline-table').
1351 // This is also needed to properly determine the baseline of a cell if it has a table child.
1352
1353 if (isWritingModeRoot())
1354 return -1;
1355
1356 recalcSectionsIfNeeded();
1357
1358 const RenderTableSection* topNonEmptySection = this->topNonEmptySection();
1359 if (!topNonEmptySection)
1360 return -1;
1361
1362 int baseline = topNonEmptySection->firstLineBoxBaseline();
1363 if (baseline > 0)
1364 return topNonEmptySection->logicalTop() + baseline;
1365
1366 // FIXME: A table row always has a baseline per CSS 2.1. Will this return the right value?
1367 return -1;
1368 }
1369
overflowClipRect(const LayoutPoint & location,RenderRegion * region,OverlayScrollbarSizeRelevancy relevancy)1370 LayoutRect RenderTable::overflowClipRect(const LayoutPoint& location, RenderRegion* region, OverlayScrollbarSizeRelevancy relevancy)
1371 {
1372 LayoutRect rect = RenderBlock::overflowClipRect(location, region, relevancy);
1373
1374 // If we have a caption, expand the clip to include the caption.
1375 // FIXME: Technically this is wrong, but it's virtually impossible to fix this
1376 // for real until captions have been re-written.
1377 // FIXME: This code assumes (like all our other caption code) that only top/bottom are
1378 // supported. When we actually support left/right and stop mapping them to top/bottom,
1379 // we might have to hack this code first (depending on what order we do these bug fixes in).
1380 if (!m_captions.isEmpty()) {
1381 if (style()->isHorizontalWritingMode()) {
1382 rect.setHeight(height());
1383 rect.setY(location.y());
1384 } else {
1385 rect.setWidth(width());
1386 rect.setX(location.x());
1387 }
1388 }
1389
1390 return rect;
1391 }
1392
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,const HitTestLocation & locationInContainer,const LayoutPoint & accumulatedOffset,HitTestAction action)1393 bool RenderTable::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
1394 {
1395 LayoutPoint adjustedLocation = accumulatedOffset + location();
1396
1397 // Check kids first.
1398 if (!hasOverflowClip() || locationInContainer.intersects(overflowClipRect(adjustedLocation, locationInContainer.region()))) {
1399 for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
1400 if (child->isBox() && !toRenderBox(child)->hasSelfPaintingLayer() && (child->isTableSection() || child->isTableCaption())) {
1401 LayoutPoint childPoint = flipForWritingModeForChild(toRenderBox(child), adjustedLocation);
1402 if (child->nodeAtPoint(request, result, locationInContainer, childPoint, action)) {
1403 updateHitTestResult(result, toLayoutPoint(locationInContainer.point() - childPoint));
1404 return true;
1405 }
1406 }
1407 }
1408 }
1409
1410 // Check our bounds next.
1411 LayoutRect boundsRect(adjustedLocation, size());
1412 if (visibleToHitTestRequest(request) && (action == HitTestBlockBackground || action == HitTestChildBlockBackground) && locationInContainer.intersects(boundsRect)) {
1413 updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(adjustedLocation)));
1414 if (!result.addNodeToRectBasedTestResult(node(), request, locationInContainer, boundsRect))
1415 return true;
1416 }
1417
1418 return false;
1419 }
1420
createAnonymousWithParentRenderer(const RenderObject * parent)1421 RenderTable* RenderTable::createAnonymousWithParentRenderer(const RenderObject* parent)
1422 {
1423 RefPtr<RenderStyle> newStyle = RenderStyle::createAnonymousStyleWithDisplay(parent->style(), TABLE);
1424 RenderTable* newTable = new RenderTable(0);
1425 newTable->setDocumentForAnonymous(&parent->document());
1426 newTable->setStyle(newStyle.release());
1427 return newTable;
1428 }
1429
tableStartBorderAdjoiningCell(const RenderTableCell * cell) const1430 const BorderValue& RenderTable::tableStartBorderAdjoiningCell(const RenderTableCell* cell) const
1431 {
1432 ASSERT(cell->isFirstOrLastCellInRow());
1433 if (hasSameDirectionAs(cell->row()))
1434 return style()->borderStart();
1435
1436 return style()->borderEnd();
1437 }
1438
tableEndBorderAdjoiningCell(const RenderTableCell * cell) const1439 const BorderValue& RenderTable::tableEndBorderAdjoiningCell(const RenderTableCell* cell) const
1440 {
1441 ASSERT(cell->isFirstOrLastCellInRow());
1442 if (hasSameDirectionAs(cell->row()))
1443 return style()->borderEnd();
1444
1445 return style()->borderStart();
1446 }
1447
1448 }
1449