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