1 /* 2 * Copyright (C) 1997 Martin Jones (mjones@kde.org) 3 * (C) 1997 Torben Weis (weis@kde.org) 4 * (C) 1998 Waldo Bastian (bastian@kde.org) 5 * (C) 1999 Lars Knoll (knoll@kde.org) 6 * (C) 1999 Antti Koivisto (koivisto@kde.org) 7 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25 #include "config.h" 26 #include "RenderTableCell.h" 27 28 #include "CollapsedBorderValue.h" 29 #include "FloatQuad.h" 30 #include "GraphicsContext.h" 31 #include "HTMLNames.h" 32 #include "HTMLTableCellElement.h" 33 #include "PaintInfo.h" 34 #include "RenderTableCol.h" 35 #include "RenderView.h" 36 #include "StyleProperties.h" 37 #include "TransformState.h" 38 #include <wtf/StackStats.h> 39 40 #if ENABLE(MATHML) 41 #include "MathMLElement.h" 42 #include "MathMLNames.h" 43 #endif 44 45 namespace WebCore { 46 47 using namespace HTMLNames; 48 49 struct SameSizeAsRenderTableCell : public RenderBlockFlow { 50 unsigned bitfields; 51 int paddings[2]; 52 }; 53 54 COMPILE_ASSERT(sizeof(RenderTableCell) == sizeof(SameSizeAsRenderTableCell), RenderTableCell_should_stay_small); 55 COMPILE_ASSERT(sizeof(CollapsedBorderValue) == 8, CollapsedBorderValue_should_stay_small); 56 57 RenderTableCell::RenderTableCell(Element& element, PassRef<RenderStyle> style) 58 : RenderBlockFlow(element, std::move(style)) 59 , m_column(unsetColumnIndex) 60 , m_cellWidthChanged(false) 61 , m_intrinsicPaddingBefore(0) 62 , m_intrinsicPaddingAfter(0) 63 { 64 // We only update the flags when notified of DOM changes in colSpanOrRowSpanChanged() 65 // so we need to set their initial values here in case something asks for colSpan()/rowSpan() before then. 66 updateColAndRowSpanFlags(); 67 } 68 69 RenderTableCell::RenderTableCell(Document& document, PassRef<RenderStyle> style) 70 : RenderBlockFlow(document, std::move(style)) 71 , m_column(unsetColumnIndex) 72 , m_cellWidthChanged(false) 73 , m_hasColSpan(false) 74 , m_hasRowSpan(false) 75 , m_intrinsicPaddingBefore(0) 76 , m_intrinsicPaddingAfter(0) 77 { 78 } 79 80 void RenderTableCell::willBeRemovedFromTree() 81 { 82 RenderBlockFlow::willBeRemovedFromTree(); 83 84 section()->setNeedsCellRecalc(); 85 section()->removeCachedCollapsedBorders(this); 86 } 87 88 unsigned RenderTableCell::parseColSpanFromDOM() const 89 { 90 ASSERT(element()); 91 if (element()->hasTagName(tdTag) || element()->hasTagName(thTag)) 92 return std::min<unsigned>(toHTMLTableCellElement(element())->colSpan(), maxColumnIndex); 93 #if ENABLE(MATHML) 94 if (element()->hasTagName(MathMLNames::mtdTag)) 95 return std::min<unsigned>(toMathMLElement(element())->colSpan(), maxColumnIndex); 96 #endif 97 return 1; 98 } 99 100 unsigned RenderTableCell::parseRowSpanFromDOM() const 101 { 102 ASSERT(element()); 103 if (element()->hasTagName(tdTag) || element()->hasTagName(thTag)) 104 return std::min<unsigned>(toHTMLTableCellElement(element())->rowSpan(), maxRowIndex); 105 #if ENABLE(MATHML) 106 if (element()->hasTagName(MathMLNames::mtdTag)) 107 return std::min<unsigned>(toMathMLElement(element())->rowSpan(), maxRowIndex); 108 #endif 109 return 1; 110 } 111 112 void RenderTableCell::updateColAndRowSpanFlags() 113 { 114 // The vast majority of table cells do not have a colspan or rowspan, 115 // so we keep a bool to know if we need to bother reading from the DOM. 116 m_hasColSpan = element() && parseColSpanFromDOM() != 1; 117 m_hasRowSpan = element() && parseRowSpanFromDOM() != 1; 118 } 119 120 void RenderTableCell::colSpanOrRowSpanChanged() 121 { 122 ASSERT(element()); 123 #if ENABLE(MATHML) 124 ASSERT(element()->hasTagName(tdTag) || element()->hasTagName(thTag) || element()->hasTagName(MathMLNames::mtdTag)); 125 #else 126 ASSERT(element()->hasTagName(tdTag) || element()->hasTagName(thTag)); 127 #endif 128 129 updateColAndRowSpanFlags(); 130 131 // FIXME: I suspect that we could return early here if !m_hasColSpan && !m_hasRowSpan. 132 133 setNeedsLayoutAndPrefWidthsRecalc(); 134 if (parent() && section()) 135 section()->setNeedsCellRecalc(); 136 } 137 138 Length RenderTableCell::logicalWidthFromColumns(RenderTableCol* firstColForThisCell, Length widthFromStyle) const 139 { 140 ASSERT(firstColForThisCell && firstColForThisCell == table()->colElement(col())); 141 RenderTableCol* tableCol = firstColForThisCell; 142 143 unsigned colSpanCount = colSpan(); 144 int colWidthSum = 0; 145 for (unsigned i = 1; i <= colSpanCount; i++) { 146 Length colWidth = tableCol->style().logicalWidth(); 147 148 // Percentage value should be returned only for colSpan == 1. 149 // Otherwise we return original width for the cell. 150 if (!colWidth.isFixed()) { 151 if (colSpanCount > 1) 152 return widthFromStyle; 153 return colWidth; 154 } 155 156 colWidthSum += colWidth.value(); 157 tableCol = tableCol->nextColumn(); 158 // If no next <col> tag found for the span we just return what we have for now. 159 if (!tableCol) 160 break; 161 } 162 163 // Column widths specified on <col> apply to the border box of the cell, see bug 8126. 164 // FIXME: Why is border/padding ignored in the negative width case? 165 if (colWidthSum > 0) 166 return Length(std::max(0, colWidthSum - borderAndPaddingLogicalWidth().ceil()), Fixed); 167 return Length(colWidthSum, Fixed); 168 } 169 170 void RenderTableCell::computePreferredLogicalWidths() 171 { 172 // The child cells rely on the grids up in the sections to do their computePreferredLogicalWidths work. Normally the sections are set up early, as table 173 // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections' 174 // grids. We must refresh those grids before the child cells try to use them. 175 table()->recalcSectionsIfNeeded(); 176 177 RenderBlockFlow::computePreferredLogicalWidths(); 178 if (element() && style().autoWrap()) { 179 // See if nowrap was set. 180 Length w = styleOrColLogicalWidth(); 181 String nowrap = element()->getAttribute(nowrapAttr); 182 if (!nowrap.isNull() && w.isFixed()) 183 // Nowrap is set, but we didn't actually use it because of the 184 // fixed width set on the cell. Even so, it is a WinIE/Moz trait 185 // to make the minwidth of the cell into the fixed width. They do this 186 // even in strict mode, so do not make this a quirk. Affected the top 187 // of hiptop.com. 188 m_minPreferredLogicalWidth = std::max<LayoutUnit>(w.value(), m_minPreferredLogicalWidth); 189 } 190 } 191 192 void RenderTableCell::computeIntrinsicPadding(int rowHeight) 193 { 194 int oldIntrinsicPaddingBefore = intrinsicPaddingBefore(); 195 int oldIntrinsicPaddingAfter = intrinsicPaddingAfter(); 196 int logicalHeightWithoutIntrinsicPadding = pixelSnappedLogicalHeight() - oldIntrinsicPaddingBefore - oldIntrinsicPaddingAfter; 197 198 int intrinsicPaddingBefore = 0; 199 switch (style().verticalAlign()) { 200 case SUB: 201 case SUPER: 202 case TEXT_TOP: 203 case TEXT_BOTTOM: 204 case LENGTH: 205 case BASELINE: { 206 LayoutUnit baseline = cellBaselinePosition(); 207 if (baseline > borderAndPaddingBefore()) 208 intrinsicPaddingBefore = section()->rowBaseline(rowIndex()) - (baseline - oldIntrinsicPaddingBefore); 209 break; 210 } 211 case TOP: 212 break; 213 case MIDDLE: 214 intrinsicPaddingBefore = (rowHeight - logicalHeightWithoutIntrinsicPadding) / 2; 215 break; 216 case BOTTOM: 217 intrinsicPaddingBefore = rowHeight - logicalHeightWithoutIntrinsicPadding; 218 break; 219 case BASELINE_MIDDLE: 220 break; 221 } 222 223 int intrinsicPaddingAfter = rowHeight - logicalHeightWithoutIntrinsicPadding - intrinsicPaddingBefore; 224 setIntrinsicPaddingBefore(intrinsicPaddingBefore); 225 setIntrinsicPaddingAfter(intrinsicPaddingAfter); 226 227 // FIXME: Changing an intrinsic padding shouldn't trigger a relayout as it only shifts the cell inside the row but 228 // doesn't change the logical height. 229 if (intrinsicPaddingBefore != oldIntrinsicPaddingBefore || intrinsicPaddingAfter != oldIntrinsicPaddingAfter) 230 setNeedsLayout(MarkOnlyThis); 231 } 232 233 void RenderTableCell::updateLogicalWidth() 234 { 235 } 236 237 void RenderTableCell::setCellLogicalWidth(int tableLayoutLogicalWidth) 238 { 239 if (tableLayoutLogicalWidth == logicalWidth()) 240 return; 241 242 setNeedsLayout(MarkOnlyThis); 243 row()->setChildNeedsLayout(MarkOnlyThis); 244 245 if (!table()->selfNeedsLayout() && checkForRepaintDuringLayout()) 246 repaint(); 247 248 setLogicalWidth(tableLayoutLogicalWidth); 249 setCellWidthChanged(true); 250 } 251 252 void RenderTableCell::layout() 253 { 254 StackStats::LayoutCheckPoint layoutCheckPoint; 255 updateFirstLetter(); 256 257 int oldCellBaseline = cellBaselinePosition(); 258 layoutBlock(cellWidthChanged()); 259 260 // If we have replaced content, the intrinsic height of our content may have changed since the last time we laid out. If that's the case the intrinsic padding we used 261 // for layout (the padding required to push the contents of the cell down to the row's baseline) is included in our new height and baseline and makes both 262 // of them wrong. So if our content's intrinsic height has changed push the new content up into the intrinsic padding and relayout so that the rest of 263 // table and row layout can use the correct baseline and height for this cell. 264 if (isBaselineAligned() && section()->rowBaseline(rowIndex()) && cellBaselinePosition() > section()->rowBaseline(rowIndex())) { 265 int newIntrinsicPaddingBefore = std::max<LayoutUnit>(0, intrinsicPaddingBefore() - std::max<LayoutUnit>(0, cellBaselinePosition() - oldCellBaseline)); 266 setIntrinsicPaddingBefore(newIntrinsicPaddingBefore); 267 setNeedsLayout(MarkOnlyThis); 268 layoutBlock(cellWidthChanged()); 269 } 270 271 setCellWidthChanged(false); 272 } 273 274 LayoutUnit RenderTableCell::paddingTop() const 275 { 276 int result = computedCSSPaddingTop(); 277 if (!isHorizontalWritingMode()) 278 return result; 279 return result + (style().writingMode() == TopToBottomWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); 280 } 281 282 LayoutUnit RenderTableCell::paddingBottom() const 283 { 284 int result = computedCSSPaddingBottom(); 285 if (!isHorizontalWritingMode()) 286 return result; 287 return result + (style().writingMode() == TopToBottomWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); 288 } 289 290 LayoutUnit RenderTableCell::paddingLeft() const 291 { 292 int result = computedCSSPaddingLeft(); 293 if (isHorizontalWritingMode()) 294 return result; 295 return result + (style().writingMode() == LeftToRightWritingMode ? intrinsicPaddingBefore() : intrinsicPaddingAfter()); 296 } 297 298 LayoutUnit RenderTableCell::paddingRight() const 299 { 300 int result = computedCSSPaddingRight(); 301 if (isHorizontalWritingMode()) 302 return result; 303 return result + (style().writingMode() == LeftToRightWritingMode ? intrinsicPaddingAfter() : intrinsicPaddingBefore()); 304 } 305 306 LayoutUnit RenderTableCell::paddingBefore() const 307 { 308 return static_cast<int>(computedCSSPaddingBefore()) + intrinsicPaddingBefore(); 309 } 310 311 LayoutUnit RenderTableCell::paddingAfter() const 312 { 313 return static_cast<int>(computedCSSPaddingAfter()) + intrinsicPaddingAfter(); 314 } 315 316 void RenderTableCell::setOverrideLogicalContentHeightFromRowHeight(LayoutUnit rowHeight) 317 { 318 clearIntrinsicPadding(); 319 setOverrideLogicalContentHeight(std::max<LayoutUnit>(0, rowHeight - borderAndPaddingLogicalHeight())); 320 } 321 322 LayoutSize RenderTableCell::offsetFromContainer(RenderObject* o, const LayoutPoint& point, bool* offsetDependsOnPoint) const 323 { 324 ASSERT(o == container()); 325 326 LayoutSize offset = RenderBlockFlow::offsetFromContainer(o, point, offsetDependsOnPoint); 327 if (parent()) 328 offset -= parentBox()->locationOffset(); 329 330 return offset; 331 } 332 333 LayoutRect RenderTableCell::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const 334 { 335 // If the table grid is dirty, we cannot get reliable information about adjoining cells, 336 // so we ignore outside borders. This should not be a problem because it means that 337 // the table is going to recalculate the grid, relayout and repaint its current rect, which 338 // includes any outside borders of this cell. 339 if (!table()->collapseBorders() || table()->needsSectionRecalc()) 340 return RenderBlockFlow::clippedOverflowRectForRepaint(repaintContainer); 341 342 bool rtl = !styleForCellFlow().isLeftToRightDirection(); 343 int outlineSize = style().outlineSize(); 344 int left = std::max(borderHalfLeft(true), outlineSize); 345 int right = std::max(borderHalfRight(true), outlineSize); 346 int top = std::max(borderHalfTop(true), outlineSize); 347 int bottom = std::max(borderHalfBottom(true), outlineSize); 348 if ((left && !rtl) || (right && rtl)) { 349 if (RenderTableCell* before = table()->cellBefore(this)) { 350 top = std::max(top, before->borderHalfTop(true)); 351 bottom = std::max(bottom, before->borderHalfBottom(true)); 352 } 353 } 354 if ((left && rtl) || (right && !rtl)) { 355 if (RenderTableCell* after = table()->cellAfter(this)) { 356 top = std::max(top, after->borderHalfTop(true)); 357 bottom = std::max(bottom, after->borderHalfBottom(true)); 358 } 359 } 360 if (top) { 361 if (RenderTableCell* above = table()->cellAbove(this)) { 362 left = std::max(left, above->borderHalfLeft(true)); 363 right = std::max(right, above->borderHalfRight(true)); 364 } 365 } 366 if (bottom) { 367 if (RenderTableCell* below = table()->cellBelow(this)) { 368 left = std::max(left, below->borderHalfLeft(true)); 369 right = std::max(right, below->borderHalfRight(true)); 370 } 371 } 372 LayoutPoint location(std::max<LayoutUnit>(left, -visualOverflowRect().x()), std::max<LayoutUnit>(top, -visualOverflowRect().y())); 373 LayoutRect r(-location.x(), -location.y(), location.x() + std::max(width() + right, visualOverflowRect().maxX()), location.y() + std::max(height() + bottom, visualOverflowRect().maxY())); 374 375 // FIXME: layoutDelta needs to be applied in parts before/after transforms and 376 // repaint containers. https://bugs.webkit.org/show_bug.cgi?id=23308 377 r.move(view().layoutDelta()); 378 379 computeRectForRepaint(repaintContainer, r); 380 return r; 381 } 382 383 void RenderTableCell::computeRectForRepaint(const RenderLayerModelObject* repaintContainer, LayoutRect& r, bool fixed) const 384 { 385 if (repaintContainer == this) 386 return; 387 r.setY(r.y()); 388 if ((!view().layoutStateEnabled() || repaintContainer) && parent()) 389 r.moveBy(-parentBox()->location()); // Rows are in the same coordinate space, so don't add their offset in. 390 RenderBlockFlow::computeRectForRepaint(repaintContainer, r, fixed); 391 } 392 393 LayoutUnit RenderTableCell::cellBaselinePosition() const 394 { 395 // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of 396 // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there 397 // is no such line box or table-row, the baseline is the bottom of content edge of the cell box. 398 LayoutUnit baseline = firstLineBaseline(); 399 if (baseline != -1) 400 return baseline; 401 return borderAndPaddingBefore() + contentLogicalHeight(); 402 } 403 404 void RenderTableCell::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) 405 { 406 ASSERT(style().display() == TABLE_CELL); 407 ASSERT(!row() || row()->rowIndexWasSet()); 408 409 RenderBlockFlow::styleDidChange(diff, oldStyle); 410 setHasBoxDecorations(true); 411 412 if (parent() && section() && oldStyle && style().height() != oldStyle->height()) 413 section()->rowLogicalHeightChanged(rowIndex()); 414 415 // Our intrinsic padding pushes us down to align with the baseline of other cells on the row. If our vertical-align 416 // has changed then so will the padding needed to align with other cells - clear it so we can recalculate it from scratch. 417 if (oldStyle && style().verticalAlign() != oldStyle->verticalAlign()) 418 clearIntrinsicPadding(); 419 420 // If border was changed, notify table. 421 if (parent()) { 422 RenderTable* table = this->table(); 423 if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout()&& oldStyle && oldStyle->border() != style().border()) 424 table->invalidateCollapsedBorders(); 425 } 426 } 427 428 // The following rules apply for resolving conflicts and figuring out which border 429 // to use. 430 // (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting 431 // borders. Any border with this value suppresses all borders at this location. 432 // (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all 433 // the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is 434 // the default value for the border style.) 435 // (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders 436 // are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred 437 // in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. 438 // (4) If border styles differ only in color, then a style set on a cell wins over one on a row, 439 // which wins over a row group, column, column group and, lastly, table. It is undefined which color 440 // is used when two elements of the same type disagree. 441 static int compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) 442 { 443 // Sanity check the values passed in. The null border have lowest priority. 444 if (!border2.exists()) { 445 if (!border1.exists()) 446 return 0; 447 return 1; 448 } 449 if (!border1.exists()) 450 return -1; 451 452 // Rule #1 above. 453 if (border2.style() == BHIDDEN) { 454 if (border1.style() == BHIDDEN) 455 return 0; 456 return -1; 457 } 458 if (border1.style() == BHIDDEN) 459 return 1; 460 461 // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. 462 if (border2.style() == BNONE) { 463 if (border1.style() == BNONE) 464 return 0; 465 return 1; 466 } 467 if (border1.style() == BNONE) 468 return -1; 469 470 // The first part of rule #3 above. Wider borders win. 471 if (border1.width() != border2.width()) 472 return border1.width() < border2.width() ? -1 : 1; 473 474 // The borders have equal width. Sort by border style. 475 if (border1.style() != border2.style()) 476 return border1.style() < border2.style() ? -1 : 1; 477 478 // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) 479 if (border1.precedence() == border2.precedence()) 480 return 0; 481 return border1.precedence() < border2.precedence() ? -1 : 1; 482 } 483 484 static CollapsedBorderValue chooseBorder(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) 485 { 486 const CollapsedBorderValue& border = compareBorders(border1, border2) < 0 ? border2 : border1; 487 return border.style() == BHIDDEN ? CollapsedBorderValue() : border; 488 } 489 490 bool RenderTableCell::hasStartBorderAdjoiningTable() const 491 { 492 bool isStartColumn = !col(); 493 bool isEndColumn = table()->colToEffCol(col() + colSpan() - 1) == table()->numEffCols() - 1; 494 bool hasSameDirectionAsTable = hasSameDirectionAs(section()); 495 496 // The table direction determines the row direction. In mixed directionality, we cannot guarantee that 497 // we have a common border with the table (think a ltr table with rtl start cell). 498 return (isStartColumn && hasSameDirectionAsTable) || (isEndColumn && !hasSameDirectionAsTable); 499 } 500 501 bool RenderTableCell::hasEndBorderAdjoiningTable() const 502 { 503 bool isStartColumn = !col(); 504 bool isEndColumn = table()->colToEffCol(col() + colSpan() - 1) == table()->numEffCols() - 1; 505 bool hasSameDirectionAsTable = hasSameDirectionAs(section()); 506 507 // The table direction determines the row direction. In mixed directionality, we cannot guarantee that 508 // we have a common border with the table (think a ltr table with ltr end cell). 509 return (isStartColumn && !hasSameDirectionAsTable) || (isEndColumn && hasSameDirectionAsTable); 510 } 511 512 CollapsedBorderValue RenderTableCell::collapsedStartBorder(IncludeBorderColorOrNot includeColor) const 513 { 514 CollapsedBorderValue result = computeCollapsedStartBorder(includeColor); 515 if (includeColor) 516 section()->setCachedCollapsedBorder(this, CBSStart, result); 517 return result; 518 } 519 520 CollapsedBorderValue RenderTableCell::computeCollapsedStartBorder(IncludeBorderColorOrNot includeColor) const 521 { 522 RenderTable* table = this->table(); 523 524 // For the start border, we need to check, in order of precedence: 525 // (1) Our start border. 526 int startColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 527 int endColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 528 CollapsedBorderValue result(style().borderStart(), includeColor ? style().visitedDependentColor(startColorProperty) : Color(), BCELL); 529 530 // (2) The end border of the preceding cell. 531 RenderTableCell* cellBefore = table->cellBefore(this); 532 if (cellBefore) { 533 CollapsedBorderValue cellBeforeAdjoiningBorder = CollapsedBorderValue(cellBefore->borderAdjoiningCellAfter(this), includeColor ? cellBefore->style().visitedDependentColor(endColorProperty) : Color(), BCELL); 534 // |result| should be the 2nd argument as |cellBefore| should win in case of equality per CSS 2.1 (Border conflict resolution, point 4). 535 result = chooseBorder(cellBeforeAdjoiningBorder, result); 536 if (!result.exists()) 537 return result; 538 } 539 540 bool startBorderAdjoinsTable = hasStartBorderAdjoiningTable(); 541 if (startBorderAdjoinsTable) { 542 // (3) Our row's start border. 543 result = chooseBorder(result, CollapsedBorderValue(row()->borderAdjoiningStartCell(this), includeColor ? parent()->style().visitedDependentColor(startColorProperty) : Color(), BROW)); 544 if (!result.exists()) 545 return result; 546 547 // (4) Our row group's start border. 548 result = chooseBorder(result, CollapsedBorderValue(section()->borderAdjoiningStartCell(this), includeColor ? section()->style().visitedDependentColor(startColorProperty) : Color(), BROWGROUP)); 549 if (!result.exists()) 550 return result; 551 } 552 553 // (5) Our column and column group's start borders. 554 bool startColEdge; 555 bool endColEdge; 556 if (RenderTableCol* colElt = table->colElement(col(), &startColEdge, &endColEdge)) { 557 if (colElt->isTableColumnGroup() && startColEdge) { 558 // The |colElt| is a column group and is also the first colgroup (in case of spanned colgroups). 559 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellStartBorder(this), includeColor ? colElt->style().visitedDependentColor(startColorProperty) : Color(), BCOLGROUP)); 560 if (!result.exists()) 561 return result; 562 } else if (!colElt->isTableColumnGroup()) { 563 // We first consider the |colElt| and irrespective of whether it is a spanned col or not, we apply 564 // its start border. This is as per HTML5 which states that: "For the purposes of the CSS table model, 565 // the col element is expected to be treated as if it was present as many times as its span attribute specifies". 566 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellStartBorder(this), includeColor ? colElt->style().visitedDependentColor(startColorProperty) : Color(), BCOL)); 567 if (!result.exists()) 568 return result; 569 // Next, apply the start border of the enclosing colgroup but only if it is adjacent to the cell's edge. 570 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentBefore()) { 571 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellStartBorder(this), includeColor ? enclosingColumnGroup->style().visitedDependentColor(startColorProperty) : Color(), BCOLGROUP)); 572 if (!result.exists()) 573 return result; 574 } 575 } 576 } 577 578 // (6) The end border of the preceding column. 579 if (cellBefore) { 580 if (RenderTableCol* colElt = table->colElement(col() - 1, &startColEdge, &endColEdge)) { 581 if (colElt->isTableColumnGroup() && endColEdge) { 582 // The element is a colgroup and is also the last colgroup (in case of spanned colgroups). 583 result = chooseBorder(CollapsedBorderValue(colElt->borderAdjoiningCellAfter(this), includeColor ? colElt->style().visitedDependentColor(endColorProperty) : Color(), BCOLGROUP), result); 584 if (!result.exists()) 585 return result; 586 } else if (colElt->isTableColumn()) { 587 // Resolve the collapsing border against the col's border ignoring any 'span' as per HTML5. 588 result = chooseBorder(CollapsedBorderValue(colElt->borderAdjoiningCellAfter(this), includeColor ? colElt->style().visitedDependentColor(endColorProperty) : Color(), BCOL), result); 589 if (!result.exists()) 590 return result; 591 // Next, if the previous col has a parent colgroup then its end border should be applied 592 // but only if it is adjacent to the cell's edge. 593 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentAfter()) { 594 result = chooseBorder(CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellEndBorder(this), includeColor ? enclosingColumnGroup->style().visitedDependentColor(endColorProperty) : Color(), BCOLGROUP), result); 595 if (!result.exists()) 596 return result; 597 } 598 } 599 } 600 } 601 602 if (startBorderAdjoinsTable) { 603 // (7) The table's start border. 604 result = chooseBorder(result, CollapsedBorderValue(table->tableStartBorderAdjoiningCell(this), includeColor ? table->style().visitedDependentColor(startColorProperty) : Color(), BTABLE)); 605 if (!result.exists()) 606 return result; 607 } 608 609 return result; 610 } 611 612 CollapsedBorderValue RenderTableCell::collapsedEndBorder(IncludeBorderColorOrNot includeColor) const 613 { 614 CollapsedBorderValue result = computeCollapsedEndBorder(includeColor); 615 if (includeColor) 616 section()->setCachedCollapsedBorder(this, CBSEnd, result); 617 return result; 618 } 619 620 CollapsedBorderValue RenderTableCell::computeCollapsedEndBorder(IncludeBorderColorOrNot includeColor) const 621 { 622 RenderTable* table = this->table(); 623 // Note: We have to use the effective column information instead of whether we have a cell after as a table doesn't 624 // have to be regular (any row can have less cells than the total cell count). 625 bool isEndColumn = table->colToEffCol(col() + colSpan() - 1) == table->numEffCols() - 1; 626 627 // For end border, we need to check, in order of precedence: 628 // (1) Our end border. 629 int startColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderStartColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 630 int endColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderEndColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 631 CollapsedBorderValue result = CollapsedBorderValue(style().borderEnd(), includeColor ? style().visitedDependentColor(endColorProperty) : Color(), BCELL); 632 633 // (2) The start border of the following cell. 634 if (!isEndColumn) { 635 if (RenderTableCell* cellAfter = table->cellAfter(this)) { 636 CollapsedBorderValue cellAfterAdjoiningBorder = CollapsedBorderValue(cellAfter->borderAdjoiningCellBefore(this), includeColor ? cellAfter->style().visitedDependentColor(startColorProperty) : Color(), BCELL); 637 result = chooseBorder(result, cellAfterAdjoiningBorder); 638 if (!result.exists()) 639 return result; 640 } 641 } 642 643 bool endBorderAdjoinsTable = hasEndBorderAdjoiningTable(); 644 if (endBorderAdjoinsTable) { 645 // (3) Our row's end border. 646 result = chooseBorder(result, CollapsedBorderValue(row()->borderAdjoiningEndCell(this), includeColor ? parent()->style().visitedDependentColor(endColorProperty) : Color(), BROW)); 647 if (!result.exists()) 648 return result; 649 650 // (4) Our row group's end border. 651 result = chooseBorder(result, CollapsedBorderValue(section()->borderAdjoiningEndCell(this), includeColor ? section()->style().visitedDependentColor(endColorProperty) : Color(), BROWGROUP)); 652 if (!result.exists()) 653 return result; 654 } 655 656 // (5) Our column and column group's end borders. 657 bool startColEdge; 658 bool endColEdge; 659 if (RenderTableCol* colElt = table->colElement(col() + colSpan() - 1, &startColEdge, &endColEdge)) { 660 if (colElt->isTableColumnGroup() && endColEdge) { 661 // The element is a colgroup and is also the last colgroup (in case of spanned colgroups). 662 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellEndBorder(this), includeColor ? colElt->style().visitedDependentColor(endColorProperty) : Color(), BCOLGROUP)); 663 if (!result.exists()) 664 return result; 665 } else if (!colElt->isTableColumnGroup()) { 666 // First apply the end border of the column irrespective of whether it is spanned or not. This is as per 667 // HTML5 which states that: "For the purposes of the CSS table model, the col element is expected to be 668 // treated as if it was present as many times as its span attribute specifies". 669 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellEndBorder(this), includeColor ? colElt->style().visitedDependentColor(endColorProperty) : Color(), BCOL)); 670 if (!result.exists()) 671 return result; 672 // Next, if it has a parent colgroup then we apply its end border but only if it is adjacent to the cell. 673 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentAfter()) { 674 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellEndBorder(this), includeColor ? enclosingColumnGroup->style().visitedDependentColor(endColorProperty) : Color(), BCOLGROUP)); 675 if (!result.exists()) 676 return result; 677 } 678 } 679 } 680 681 // (6) The start border of the next column. 682 if (!isEndColumn) { 683 if (RenderTableCol* colElt = table->colElement(col() + colSpan(), &startColEdge, &endColEdge)) { 684 if (colElt->isTableColumnGroup() && startColEdge) { 685 // This case is a colgroup without any col, we only compute it if it is adjacent to the cell's edge. 686 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellBefore(this), includeColor ? colElt->style().visitedDependentColor(startColorProperty) : Color(), BCOLGROUP)); 687 if (!result.exists()) 688 return result; 689 } else if (colElt->isTableColumn()) { 690 // Resolve the collapsing border against the col's border ignoring any 'span' as per HTML5. 691 result = chooseBorder(result, CollapsedBorderValue(colElt->borderAdjoiningCellBefore(this), includeColor ? colElt->style().visitedDependentColor(startColorProperty) : Color(), BCOL)); 692 if (!result.exists()) 693 return result; 694 // If we have a parent colgroup, resolve the border only if it is adjacent to the cell. 695 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroupIfAdjacentBefore()) { 696 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->borderAdjoiningCellStartBorder(this), includeColor ? enclosingColumnGroup->style().visitedDependentColor(startColorProperty) : Color(), BCOLGROUP)); 697 if (!result.exists()) 698 return result; 699 } 700 } 701 } 702 } 703 704 if (endBorderAdjoinsTable) { 705 // (7) The table's end border. 706 result = chooseBorder(result, CollapsedBorderValue(table->tableEndBorderAdjoiningCell(this), includeColor ? table->style().visitedDependentColor(endColorProperty) : Color(), BTABLE)); 707 if (!result.exists()) 708 return result; 709 } 710 711 return result; 712 } 713 714 CollapsedBorderValue RenderTableCell::collapsedBeforeBorder(IncludeBorderColorOrNot includeColor) const 715 { 716 CollapsedBorderValue result = computeCollapsedBeforeBorder(includeColor); 717 if (includeColor) 718 section()->setCachedCollapsedBorder(this, CBSBefore, result); 719 return result; 720 } 721 722 CollapsedBorderValue RenderTableCell::computeCollapsedBeforeBorder(IncludeBorderColorOrNot includeColor) const 723 { 724 RenderTable* table = this->table(); 725 726 // For before border, we need to check, in order of precedence: 727 // (1) Our before border. 728 int beforeColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderBeforeColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 729 int afterColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderAfterColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 730 CollapsedBorderValue result = CollapsedBorderValue(style().borderBefore(), includeColor ? style().visitedDependentColor(beforeColorProperty) : Color(), BCELL); 731 732 RenderTableCell* prevCell = table->cellAbove(this); 733 if (prevCell) { 734 // (2) A before cell's after border. 735 result = chooseBorder(CollapsedBorderValue(prevCell->style().borderAfter(), includeColor ? prevCell->style().visitedDependentColor(afterColorProperty) : Color(), BCELL), result); 736 if (!result.exists()) 737 return result; 738 } 739 740 // (3) Our row's before border. 741 result = chooseBorder(result, CollapsedBorderValue(parent()->style().borderBefore(), includeColor ? parent()->style().visitedDependentColor(beforeColorProperty) : Color(), BROW)); 742 if (!result.exists()) 743 return result; 744 745 // (4) The previous row's after border. 746 if (prevCell) { 747 RenderObject* prevRow = 0; 748 if (prevCell->section() == section()) 749 prevRow = parent()->previousSibling(); 750 else 751 prevRow = prevCell->section()->lastRow(); 752 753 if (prevRow) { 754 result = chooseBorder(CollapsedBorderValue(prevRow->style().borderAfter(), includeColor ? prevRow->style().visitedDependentColor(afterColorProperty) : Color(), BROW), result); 755 if (!result.exists()) 756 return result; 757 } 758 } 759 760 // Now check row groups. 761 RenderTableSection* currSection = section(); 762 if (!rowIndex()) { 763 // (5) Our row group's before border. 764 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderBefore(), includeColor ? currSection->style().visitedDependentColor(beforeColorProperty) : Color(), BROWGROUP)); 765 if (!result.exists()) 766 return result; 767 768 // (6) Previous row group's after border. 769 currSection = table->sectionAbove(currSection, SkipEmptySections); 770 if (currSection) { 771 result = chooseBorder(CollapsedBorderValue(currSection->style().borderAfter(), includeColor ? currSection->style().visitedDependentColor(afterColorProperty) : Color(), BROWGROUP), result); 772 if (!result.exists()) 773 return result; 774 } 775 } 776 777 if (!currSection) { 778 // (8) Our column and column group's before borders. 779 RenderTableCol* colElt = table->colElement(col()); 780 if (colElt) { 781 result = chooseBorder(result, CollapsedBorderValue(colElt->style().borderBefore(), includeColor ? colElt->style().visitedDependentColor(beforeColorProperty) : Color(), BCOL)); 782 if (!result.exists()) 783 return result; 784 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroup()) { 785 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->style().borderBefore(), includeColor ? enclosingColumnGroup->style().visitedDependentColor(beforeColorProperty) : Color(), BCOLGROUP)); 786 if (!result.exists()) 787 return result; 788 } 789 } 790 791 // (9) The table's before border. 792 result = chooseBorder(result, CollapsedBorderValue(table->style().borderBefore(), includeColor ? table->style().visitedDependentColor(beforeColorProperty) : Color(), BTABLE)); 793 if (!result.exists()) 794 return result; 795 } 796 797 return result; 798 } 799 800 CollapsedBorderValue RenderTableCell::collapsedAfterBorder(IncludeBorderColorOrNot includeColor) const 801 { 802 CollapsedBorderValue result = computeCollapsedAfterBorder(includeColor); 803 if (includeColor) 804 section()->setCachedCollapsedBorder(this, CBSAfter, result); 805 return result; 806 } 807 808 CollapsedBorderValue RenderTableCell::computeCollapsedAfterBorder(IncludeBorderColorOrNot includeColor) const 809 { 810 RenderTable* table = this->table(); 811 812 // For after border, we need to check, in order of precedence: 813 // (1) Our after border. 814 int beforeColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderBeforeColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 815 int afterColorProperty = includeColor ? CSSProperty::resolveDirectionAwareProperty(CSSPropertyWebkitBorderAfterColor, styleForCellFlow().direction(), styleForCellFlow().writingMode()) : 0; 816 CollapsedBorderValue result = CollapsedBorderValue(style().borderAfter(), includeColor ? style().visitedDependentColor(afterColorProperty) : Color(), BCELL); 817 818 RenderTableCell* nextCell = table->cellBelow(this); 819 if (nextCell) { 820 // (2) An after cell's before border. 821 result = chooseBorder(result, CollapsedBorderValue(nextCell->style().borderBefore(), includeColor ? nextCell->style().visitedDependentColor(beforeColorProperty) : Color(), BCELL)); 822 if (!result.exists()) 823 return result; 824 } 825 826 // (3) Our row's after border. (FIXME: Deal with rowspan!) 827 result = chooseBorder(result, CollapsedBorderValue(parent()->style().borderAfter(), includeColor ? parent()->style().visitedDependentColor(afterColorProperty) : Color(), BROW)); 828 if (!result.exists()) 829 return result; 830 831 // (4) The next row's before border. 832 if (nextCell) { 833 result = chooseBorder(result, CollapsedBorderValue(nextCell->parent()->style().borderBefore(), includeColor ? nextCell->parent()->style().visitedDependentColor(beforeColorProperty) : Color(), BROW)); 834 if (!result.exists()) 835 return result; 836 } 837 838 // Now check row groups. 839 RenderTableSection* currSection = section(); 840 if (rowIndex() + rowSpan() >= currSection->numRows()) { 841 // (5) Our row group's after border. 842 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderAfter(), includeColor ? currSection->style().visitedDependentColor(afterColorProperty) : Color(), BROWGROUP)); 843 if (!result.exists()) 844 return result; 845 846 // (6) Following row group's before border. 847 currSection = table->sectionBelow(currSection, SkipEmptySections); 848 if (currSection) { 849 result = chooseBorder(result, CollapsedBorderValue(currSection->style().borderBefore(), includeColor ? currSection->style().visitedDependentColor(beforeColorProperty) : Color(), BROWGROUP)); 850 if (!result.exists()) 851 return result; 852 } 853 } 854 855 if (!currSection) { 856 // (8) Our column and column group's after borders. 857 RenderTableCol* colElt = table->colElement(col()); 858 if (colElt) { 859 result = chooseBorder(result, CollapsedBorderValue(colElt->style().borderAfter(), includeColor ? colElt->style().visitedDependentColor(afterColorProperty) : Color(), BCOL)); 860 if (!result.exists()) return result; 861 if (RenderTableCol* enclosingColumnGroup = colElt->enclosingColumnGroup()) { 862 result = chooseBorder(result, CollapsedBorderValue(enclosingColumnGroup->style().borderAfter(), includeColor ? enclosingColumnGroup->style().visitedDependentColor(afterColorProperty) : Color(), BCOLGROUP)); 863 if (!result.exists()) 864 return result; 865 } 866 } 867 868 // (9) The table's after border. 869 result = chooseBorder(result, CollapsedBorderValue(table->style().borderAfter(), includeColor ? table->style().visitedDependentColor(afterColorProperty) : Color(), BTABLE)); 870 if (!result.exists()) 871 return result; 872 } 873 874 return result; 875 } 876 877 inline CollapsedBorderValue RenderTableCell::cachedCollapsedLeftBorder(const RenderStyle* styleForCellFlow) const 878 { 879 if (styleForCellFlow->isHorizontalWritingMode()) 880 return styleForCellFlow->isLeftToRightDirection() ? section()->cachedCollapsedBorder(this, CBSStart) : section()->cachedCollapsedBorder(this, CBSEnd); 881 return styleForCellFlow->isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(this, CBSAfter) : section()->cachedCollapsedBorder(this, CBSBefore); 882 } 883 884 inline CollapsedBorderValue RenderTableCell::cachedCollapsedRightBorder(const RenderStyle* styleForCellFlow) const 885 { 886 if (styleForCellFlow->isHorizontalWritingMode()) 887 return styleForCellFlow->isLeftToRightDirection() ? section()->cachedCollapsedBorder(this, CBSEnd) : section()->cachedCollapsedBorder(this, CBSStart); 888 return styleForCellFlow->isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(this, CBSBefore) : section()->cachedCollapsedBorder(this, CBSAfter); 889 } 890 891 inline CollapsedBorderValue RenderTableCell::cachedCollapsedTopBorder(const RenderStyle* styleForCellFlow) const 892 { 893 if (styleForCellFlow->isHorizontalWritingMode()) 894 return styleForCellFlow->isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(this, CBSAfter) : section()->cachedCollapsedBorder(this, CBSBefore); 895 return styleForCellFlow->isLeftToRightDirection() ? section()->cachedCollapsedBorder(this, CBSStart) : section()->cachedCollapsedBorder(this, CBSEnd); 896 } 897 898 inline CollapsedBorderValue RenderTableCell::cachedCollapsedBottomBorder(const RenderStyle* styleForCellFlow) const 899 { 900 if (styleForCellFlow->isHorizontalWritingMode()) 901 return styleForCellFlow->isFlippedBlocksWritingMode() ? section()->cachedCollapsedBorder(this, CBSBefore) : section()->cachedCollapsedBorder(this, CBSAfter); 902 return styleForCellFlow->isLeftToRightDirection() ? section()->cachedCollapsedBorder(this, CBSEnd) : section()->cachedCollapsedBorder(this, CBSStart); 903 } 904 905 inline RenderTableCell* RenderTableCell::cellAtLeft(const RenderStyle* styleForCellFlow) const 906 { 907 if (styleForCellFlow->isHorizontalWritingMode()) 908 return styleForCellFlow->isLeftToRightDirection() ? table()->cellBefore(this) : table()->cellAfter(this); 909 return styleForCellFlow->isFlippedBlocksWritingMode() ? table()->cellBelow(this) : table()->cellAbove(this); 910 } 911 912 inline RenderTableCell* RenderTableCell::cellAtRight(const RenderStyle* styleForCellFlow) const 913 { 914 if (styleForCellFlow->isHorizontalWritingMode()) 915 return styleForCellFlow->isLeftToRightDirection() ? table()->cellAfter(this) : table()->cellBefore(this); 916 return styleForCellFlow->isFlippedBlocksWritingMode() ? table()->cellAbove(this) : table()->cellBelow(this); 917 } 918 919 inline RenderTableCell* RenderTableCell::cellAtTop(const RenderStyle* styleForCellFlow) const 920 { 921 if (styleForCellFlow->isHorizontalWritingMode()) 922 return styleForCellFlow->isFlippedBlocksWritingMode() ? table()->cellBelow(this) : table()->cellAbove(this); 923 return styleForCellFlow->isLeftToRightDirection() ? table()->cellBefore(this) : table()->cellAfter(this); 924 } 925 926 inline RenderTableCell* RenderTableCell::cellAtBottom(const RenderStyle* styleForCellFlow) const 927 { 928 if (styleForCellFlow->isHorizontalWritingMode()) 929 return styleForCellFlow->isFlippedBlocksWritingMode() ? table()->cellAbove(this) : table()->cellBelow(this); 930 return styleForCellFlow->isLeftToRightDirection() ? table()->cellAfter(this) : table()->cellBefore(this); 931 } 932 933 LayoutUnit RenderTableCell::borderLeft() const 934 { 935 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfLeft(false)) : RenderBlockFlow::borderLeft(); 936 } 937 938 LayoutUnit RenderTableCell::borderRight() const 939 { 940 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfRight(false)) : RenderBlockFlow::borderRight(); 941 } 942 943 LayoutUnit RenderTableCell::borderTop() const 944 { 945 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfTop(false)) : RenderBlockFlow::borderTop(); 946 } 947 948 LayoutUnit RenderTableCell::borderBottom() const 949 { 950 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfBottom(false)) : RenderBlockFlow::borderBottom(); 951 } 952 953 // FIXME: https://bugs.webkit.org/show_bug.cgi?id=46191, make the collapsed border drawing 954 // work with different block flow values instead of being hard-coded to top-to-bottom. 955 LayoutUnit RenderTableCell::borderStart() const 956 { 957 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfStart(false)) : RenderBlockFlow::borderStart(); 958 } 959 960 LayoutUnit RenderTableCell::borderEnd() const 961 { 962 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfEnd(false)) : RenderBlockFlow::borderEnd(); 963 } 964 965 LayoutUnit RenderTableCell::borderBefore() const 966 { 967 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfBefore(false)) : RenderBlockFlow::borderBefore(); 968 } 969 970 LayoutUnit RenderTableCell::borderAfter() const 971 { 972 return table()->collapseBorders() ? LayoutUnit::fromPixel(borderHalfAfter(false)) : RenderBlockFlow::borderAfter(); 973 } 974 975 int RenderTableCell::borderHalfLeft(bool outer) const 976 { 977 const RenderStyle& styleForCellFlow = this->styleForCellFlow(); 978 if (styleForCellFlow.isHorizontalWritingMode()) 979 return styleForCellFlow.isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer); 980 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer); 981 } 982 983 int RenderTableCell::borderHalfRight(bool outer) const 984 { 985 const RenderStyle& styleForCellFlow = this->styleForCellFlow(); 986 if (styleForCellFlow.isHorizontalWritingMode()) 987 return styleForCellFlow.isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer); 988 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer); 989 } 990 991 int RenderTableCell::borderHalfTop(bool outer) const 992 { 993 const RenderStyle& styleForCellFlow = this->styleForCellFlow(); 994 if (styleForCellFlow.isHorizontalWritingMode()) 995 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfAfter(outer) : borderHalfBefore(outer); 996 return styleForCellFlow.isLeftToRightDirection() ? borderHalfStart(outer) : borderHalfEnd(outer); 997 } 998 999 int RenderTableCell::borderHalfBottom(bool outer) const 1000 { 1001 const RenderStyle& styleForCellFlow = this->styleForCellFlow(); 1002 if (styleForCellFlow.isHorizontalWritingMode()) 1003 return styleForCellFlow.isFlippedBlocksWritingMode() ? borderHalfBefore(outer) : borderHalfAfter(outer); 1004 return styleForCellFlow.isLeftToRightDirection() ? borderHalfEnd(outer) : borderHalfStart(outer); 1005 } 1006 1007 int RenderTableCell::borderHalfStart(bool outer) const 1008 { 1009 CollapsedBorderValue border = collapsedStartBorder(DoNotIncludeBorderColor); 1010 if (border.exists()) 1011 return (border.width() + ((styleForCellFlow().isLeftToRightDirection() ^ outer) ? 1 : 0)) / 2; // Give the extra pixel to top and left. 1012 return 0; 1013 } 1014 1015 int RenderTableCell::borderHalfEnd(bool outer) const 1016 { 1017 CollapsedBorderValue border = collapsedEndBorder(DoNotIncludeBorderColor); 1018 if (border.exists()) 1019 return (border.width() + ((styleForCellFlow().isLeftToRightDirection() ^ outer) ? 0 : 1)) / 2; 1020 return 0; 1021 } 1022 1023 int RenderTableCell::borderHalfBefore(bool outer) const 1024 { 1025 CollapsedBorderValue border = collapsedBeforeBorder(DoNotIncludeBorderColor); 1026 if (border.exists()) 1027 return (border.width() + ((styleForCellFlow().isFlippedBlocksWritingMode() ^ outer) ? 0 : 1)) / 2; // Give the extra pixel to top and left. 1028 return 0; 1029 } 1030 1031 int RenderTableCell::borderHalfAfter(bool outer) const 1032 { 1033 CollapsedBorderValue border = collapsedAfterBorder(DoNotIncludeBorderColor); 1034 if (border.exists()) 1035 return (border.width() + ((styleForCellFlow().isFlippedBlocksWritingMode() ^ outer) ? 1 : 0)) / 2; 1036 return 0; 1037 } 1038 1039 void RenderTableCell::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1040 { 1041 ASSERT(paintInfo.phase != PaintPhaseCollapsedTableBorders); 1042 RenderBlockFlow::paint(paintInfo, paintOffset); 1043 } 1044 1045 static EBorderStyle collapsedBorderStyle(EBorderStyle style) 1046 { 1047 if (style == OUTSET) 1048 return GROOVE; 1049 if (style == INSET) 1050 return RIDGE; 1051 return style; 1052 } 1053 1054 struct CollapsedBorder { 1055 CollapsedBorderValue borderValue; 1056 BoxSide side; 1057 bool shouldPaint; 1058 int x1; 1059 int y1; 1060 int x2; 1061 int y2; 1062 EBorderStyle style; 1063 }; 1064 1065 class CollapsedBorders { 1066 public: 1067 CollapsedBorders() 1068 : m_count(0) 1069 { 1070 } 1071 1072 void addBorder(const CollapsedBorderValue& borderValue, BoxSide borderSide, bool shouldPaint, 1073 int x1, int y1, int x2, int y2, EBorderStyle borderStyle) 1074 { 1075 if (borderValue.exists() && shouldPaint) { 1076 m_borders[m_count].borderValue = borderValue; 1077 m_borders[m_count].side = borderSide; 1078 m_borders[m_count].shouldPaint = shouldPaint; 1079 m_borders[m_count].x1 = x1; 1080 m_borders[m_count].x2 = x2; 1081 m_borders[m_count].y1 = y1; 1082 m_borders[m_count].y2 = y2; 1083 m_borders[m_count].style = borderStyle; 1084 m_count++; 1085 } 1086 } 1087 1088 CollapsedBorder* nextBorder() 1089 { 1090 for (unsigned i = 0; i < m_count; i++) { 1091 if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) { 1092 m_borders[i].shouldPaint = false; 1093 return &m_borders[i]; 1094 } 1095 } 1096 1097 return 0; 1098 } 1099 1100 CollapsedBorder m_borders[4]; 1101 unsigned m_count; 1102 }; 1103 1104 static void addBorderStyle(RenderTable::CollapsedBorderValues& borderValues, 1105 CollapsedBorderValue borderValue) 1106 { 1107 if (!borderValue.exists()) 1108 return; 1109 size_t count = borderValues.size(); 1110 for (size_t i = 0; i < count; ++i) 1111 if (borderValues[i].isSameIgnoringColor(borderValue)) 1112 return; 1113 borderValues.append(borderValue); 1114 } 1115 1116 void RenderTableCell::collectBorderValues(RenderTable::CollapsedBorderValues& borderValues) const 1117 { 1118 addBorderStyle(borderValues, collapsedStartBorder()); 1119 addBorderStyle(borderValues, collapsedEndBorder()); 1120 addBorderStyle(borderValues, collapsedBeforeBorder()); 1121 addBorderStyle(borderValues, collapsedAfterBorder()); 1122 } 1123 1124 static int compareBorderValuesForQSort(const void* pa, const void* pb) 1125 { 1126 const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa); 1127 const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb); 1128 if (a->isSameIgnoringColor(*b)) 1129 return 0; 1130 return compareBorders(*a, *b); 1131 } 1132 1133 void RenderTableCell::sortBorderValues(RenderTable::CollapsedBorderValues& borderValues) 1134 { 1135 qsort(borderValues.data(), borderValues.size(), sizeof(CollapsedBorderValue), 1136 compareBorderValuesForQSort); 1137 } 1138 1139 void RenderTableCell::paintCollapsedBorders(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1140 { 1141 ASSERT(paintInfo.phase == PaintPhaseCollapsedTableBorders); 1142 1143 if (!paintInfo.shouldPaintWithinRoot(*this) || style().visibility() != VISIBLE) 1144 return; 1145 1146 LayoutRect localRepaintRect = paintInfo.rect; 1147 localRepaintRect.inflate(maximalOutlineSize(paintInfo.phase)); 1148 1149 LayoutRect paintRect = LayoutRect(paintOffset + location(), pixelSnappedSize()); 1150 if (paintRect.y() - table()->outerBorderTop() >= localRepaintRect.maxY()) 1151 return; 1152 1153 if (paintRect.maxY() + table()->outerBorderBottom() <= localRepaintRect.y()) 1154 return; 1155 1156 GraphicsContext* graphicsContext = paintInfo.context; 1157 if (!table()->currentBorderValue() || graphicsContext->paintingDisabled()) 1158 return; 1159 1160 const RenderStyle& styleForCellFlow = this->styleForCellFlow(); 1161 CollapsedBorderValue leftVal = cachedCollapsedLeftBorder(&styleForCellFlow); 1162 CollapsedBorderValue rightVal = cachedCollapsedRightBorder(&styleForCellFlow); 1163 CollapsedBorderValue topVal = cachedCollapsedTopBorder(&styleForCellFlow); 1164 CollapsedBorderValue bottomVal = cachedCollapsedBottomBorder(&styleForCellFlow); 1165 1166 // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. 1167 int topWidth = topVal.width(); 1168 int bottomWidth = bottomVal.width(); 1169 int leftWidth = leftVal.width(); 1170 int rightWidth = rightVal.width(); 1171 1172 IntRect borderRect = pixelSnappedIntRect(paintRect.x() - leftWidth / 2, 1173 paintRect.y() - topWidth / 2, 1174 paintRect.width() + leftWidth / 2 + (rightWidth + 1) / 2, 1175 paintRect.height() + topWidth / 2 + (bottomWidth + 1) / 2); 1176 1177 EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); 1178 EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); 1179 EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); 1180 EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); 1181 1182 bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); 1183 bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); 1184 bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); 1185 bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); 1186 1187 // We never paint diagonals at the joins. We simply let the border with the highest 1188 // precedence paint on top of borders with lower precedence. 1189 CollapsedBorders borders; 1190 borders.addBorder(topVal, BSTop, renderTop, borderRect.x(), borderRect.y(), borderRect.maxX(), borderRect.y() + topWidth, topStyle); 1191 borders.addBorder(bottomVal, BSBottom, renderBottom, borderRect.x(), borderRect.maxY() - bottomWidth, borderRect.maxX(), borderRect.maxY(), bottomStyle); 1192 borders.addBorder(leftVal, BSLeft, renderLeft, borderRect.x(), borderRect.y(), borderRect.x() + leftWidth, borderRect.maxY(), leftStyle); 1193 borders.addBorder(rightVal, BSRight, renderRight, borderRect.maxX() - rightWidth, borderRect.y(), borderRect.maxX(), borderRect.maxY(), rightStyle); 1194 1195 bool antialias = shouldAntialiasLines(graphicsContext); 1196 1197 for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { 1198 if (border->borderValue.isSameIgnoringColor(*table()->currentBorderValue())) 1199 drawLineForBoxSide(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, 1200 border->borderValue.color(), border->style, 0, 0, antialias); 1201 } 1202 } 1203 1204 void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderElement* backgroundObject) 1205 { 1206 if (!paintInfo.shouldPaintWithinRoot(*this)) 1207 return; 1208 1209 if (!backgroundObject) 1210 return; 1211 1212 if (style().visibility() != VISIBLE) 1213 return; 1214 1215 RenderTable* tableElt = table(); 1216 if (!tableElt->collapseBorders() && style().emptyCells() == HIDE && !firstChild()) 1217 return; 1218 1219 LayoutPoint adjustedPaintOffset = paintOffset; 1220 if (backgroundObject != this) 1221 adjustedPaintOffset.moveBy(location()); 1222 1223 Color c = backgroundObject->style().visitedDependentColor(CSSPropertyBackgroundColor); 1224 const FillLayer* bgLayer = backgroundObject->style().backgroundLayers(); 1225 1226 if (bgLayer->hasImage() || c.isValid()) { 1227 // We have to clip here because the background would paint 1228 // on top of the borders otherwise. This only matters for cells and rows. 1229 bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders(); 1230 GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip); 1231 if (shouldClip) { 1232 LayoutRect clipRect(adjustedPaintOffset.x() + borderLeft(), adjustedPaintOffset.y() + borderTop(), 1233 width() - borderLeft() - borderRight(), height() - borderTop() - borderBottom()); 1234 paintInfo.context->clip(clipRect); 1235 } 1236 paintFillLayers(paintInfo, c, bgLayer, LayoutRect(adjustedPaintOffset, pixelSnappedSize()), BackgroundBleedNone, CompositeSourceOver, backgroundObject); 1237 } 1238 } 1239 1240 void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1241 { 1242 if (!paintInfo.shouldPaintWithinRoot(*this)) 1243 return; 1244 1245 RenderTable* tableElt = table(); 1246 if (!tableElt->collapseBorders() && style().emptyCells() == HIDE && !firstChild()) 1247 return; 1248 1249 LayoutRect paintRect = LayoutRect(paintOffset, pixelSnappedSize()); 1250 paintBoxShadow(paintInfo, paintRect, &style(), Normal); 1251 1252 // Paint our cell background. 1253 paintBackgroundsBehindCell(paintInfo, paintOffset, this); 1254 1255 paintBoxShadow(paintInfo, paintRect, &style(), Inset); 1256 1257 if (!style().hasBorder() || tableElt->collapseBorders()) 1258 return; 1259 1260 paintBorder(paintInfo, paintRect, &style()); 1261 } 1262 1263 void RenderTableCell::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1264 { 1265 if (style().visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 1266 return; 1267 1268 RenderTable* tableElt = table(); 1269 if (!tableElt->collapseBorders() && style().emptyCells() == HIDE && !firstChild()) 1270 return; 1271 1272 paintMaskImages(paintInfo, LayoutRect(paintOffset, pixelSnappedSize())); 1273 } 1274 1275 bool RenderTableCell::boxShadowShouldBeAppliedToBackground(BackgroundBleedAvoidance, InlineFlowBox*) const 1276 { 1277 return false; 1278 } 1279 1280 void RenderTableCell::scrollbarsChanged(bool horizontalScrollbarChanged, bool verticalScrollbarChanged) 1281 { 1282 LayoutUnit scrollbarHeight = scrollbarLogicalHeight(); 1283 if (!scrollbarHeight) 1284 return; // Not sure if we should be doing something when a scrollbar goes away or not. 1285 1286 // We only care if the scrollbar that affects our intrinsic padding has been added. 1287 if ((isHorizontalWritingMode() && !horizontalScrollbarChanged) || 1288 (!isHorizontalWritingMode() && !verticalScrollbarChanged)) 1289 return; 1290 1291 // Shrink our intrinsic padding as much as possible to accommodate the scrollbar. 1292 if (style().verticalAlign() == MIDDLE) { 1293 LayoutUnit totalHeight = logicalHeight(); 1294 LayoutUnit heightWithoutIntrinsicPadding = totalHeight - intrinsicPaddingBefore() - intrinsicPaddingAfter(); 1295 totalHeight -= scrollbarHeight; 1296 LayoutUnit newBeforePadding = (totalHeight - heightWithoutIntrinsicPadding) / 2; 1297 LayoutUnit newAfterPadding = totalHeight - heightWithoutIntrinsicPadding - newBeforePadding; 1298 setIntrinsicPaddingBefore(newBeforePadding); 1299 setIntrinsicPaddingAfter(newAfterPadding); 1300 } else 1301 setIntrinsicPaddingAfter(intrinsicPaddingAfter() - scrollbarHeight); 1302 } 1303 1304 RenderTableCell* RenderTableCell::createAnonymousWithParentRenderer(const RenderObject* parent) 1305 { 1306 auto cell = new RenderTableCell(parent->document(), RenderStyle::createAnonymousStyleWithDisplay(&parent->style(), TABLE_CELL)); 1307 cell->initializeStyle(); 1308 return cell; 1309 } 1310 1311 } // namespace WebCore