void TableRowPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { DCHECK(m_layoutTableRow.hasSelfPaintingLayer()); // TODO(crbug.com/577282): This painting order is inconsistent with other // outlines. if (shouldPaintSelfOutline(paintInfo.phase)) paintOutline(paintInfo, paintOffset); if (paintInfo.phase == PaintPhaseSelfOutlineOnly) return; PaintInfo paintInfoForCells = paintInfo.forDescendants(); if (shouldPaintSelfBlockBackground(paintInfo.phase)) { paintBoxShadow(paintInfo, paintOffset, Normal); if (m_layoutTableRow.styleRef().hasBackground()) { // Paint row background of behind the cells. for (LayoutTableCell* cell = m_layoutTableRow.firstCell(); cell; cell = cell->nextCell()) TableCellPainter(*cell).paintContainerBackgroundBehindCell( paintInfoForCells, paintOffset, m_layoutTableRow, DisplayItem::kTableCellBackgroundFromRow); } paintBoxShadow(paintInfo, paintOffset, Inset); } if (paintInfo.phase == PaintPhaseSelfBlockBackgroundOnly) return; for (LayoutTableCell* cell = m_layoutTableRow.firstCell(); cell; cell = cell->nextCell()) { if (!cell->hasSelfPaintingLayer()) cell->paint(paintInfoForCells, paintOffset); } }
void LayoutTableRow::layout() { ASSERT(needsLayout()); LayoutAnalyzer::Scope analyzer(*this); // Table rows do not add translation. LayoutState state(*this, LayoutSize()); for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) { SubtreeLayoutScope layouter(*cell); if (!cell->needsLayout()) cell->markForPaginationRelayoutIfNeeded(layouter); if (cell->needsLayout()) cell->layout(); } m_overflow.clear(); addVisualEffectOverflow(); // We do not call addOverflowFromCell here. The cell are laid out to be // measured above and will be sized correctly in a follow-up phase. // We only ever need to issue paint invalidations if our cells didn't, which means that they didn't need // layout, so we know that our bounds didn't change. This code is just making up for // the fact that we did not invalidate paints in setStyle() because we had a layout hint. if (selfNeedsLayout()) { for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) { // FIXME: Is this needed when issuing paint invalidations after layout? cell->setShouldDoFullPaintInvalidation(); } } // LayoutTableSection::layoutRows will set our logical height and width later, so it calls updateLayerTransform(). clearNeedsLayout(); }
AccessibilityRole AXTableCell::scanToDecideHeaderRole() { if (!isTableHeaderCell()) return CellRole; // Check scope attribute first. if (isRowHeaderCell()) return RowHeaderRole; if (isColumnHeaderCell()) return ColumnHeaderRole; // Check the previous cell and the next cell on the same row. LayoutTableCell* layoutCell = toLayoutTableCell(m_layoutObject); AccessibilityRole headerRole = CellRole; // if header is preceded by header cells on the same row, then it is a // column header. If it is preceded by other cells then it's a row header. if ((headerRole = decideRoleFromSibling(layoutCell->previousCell())) != CellRole) return headerRole; // if header is followed by header cells on the same row, then it is a // column header. If it is followed by other cells then it's a row header. if ((headerRole = decideRoleFromSibling(layoutCell->nextCell())) != CellRole) return headerRole; // If there are no other cells on that row, then it is a column header. return ColumnHeaderRole; }
void TextAutosizer::inflateAutoTable(LayoutTable* table) { ASSERT(table); ASSERT(!table->style()->isFixedTableLayout()); ASSERT(table->containingBlock()); Cluster* cluster = currentCluster(); if (cluster->m_root != table) return; // Pre-inflate cells that have enough text so that their inflated preferred widths will be used // for column sizing. for (LayoutObject* section = table->firstChild(); section; section = section->nextSibling()) { if (!section->isTableSection()) continue; for (LayoutTableRow* row = toLayoutTableSection(section)->firstRow(); row; row = row->nextRow()) { for (LayoutTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) { if (!cell->needsLayout()) continue; beginLayout(cell); inflate(cell, DescendToInnerBlocks); endLayout(cell); } } } }
void LayoutTableCol::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle) { LayoutBox::styleDidChange(diff, oldStyle); // If border was changed, notify table. if (parent()) { LayoutTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) { table->invalidateCollapsedBorders(); } else if (oldStyle && oldStyle->logicalWidth() != style()->logicalWidth()) { // FIXME : setPreferredLogicalWidthsDirty is done for all cells as of now. // Need to find a better way so that only the cells which are changed by // the col width should have preferred logical widths recomputed. for (LayoutObject* child = table->children()->firstChild(); child; child = child->nextSibling()) { if (!child->isTableSection()) continue; LayoutTableSection* section = toLayoutTableSection(child); for (LayoutTableRow* row = section->firstRow(); row; row = row->nextRow()) { for (LayoutTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) cell->setPreferredLogicalWidthsDirty(); } } } } }
void TableLayoutAlgorithmFixed::willChangeTableLayout() { // When switching table layout algorithm, we need to dirty the preferred // logical widths as we cleared the bits without computing them. // (see calcWidthArray above.) This optimization is preferred to always // computing the logical widths we never intended to use. m_table->recalcSectionsIfNeeded(); for (LayoutTableSection* section = m_table->topNonEmptySection(); section; section = m_table->sectionBelow(section)) { for (unsigned i = 0; i < section->numRows(); i++) { LayoutTableRow* row = section->rowLayoutObjectAt(i); if (!row) continue; for (LayoutTableCell* cell = row->firstCell(); cell; cell = cell->nextCell()) cell->setPreferredLogicalWidthsDirty(); } } }
void LayoutTableRow::computeOverflow() { clearAllOverflows(); addVisualEffectOverflow(); for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell()) addOverflowFromCell(cell); }
void LayoutTableRow::addChild(LayoutObject* child, LayoutObject* beforeChild) { if (!child->isTableCell()) { LayoutObject* last = beforeChild; if (!last) last = lastCell(); if (last && last->isAnonymous() && last->isTableCell() && !last->isBeforeOrAfterContent()) { LayoutTableCell* lastCell = toLayoutTableCell(last); if (beforeChild == lastCell) beforeChild = lastCell->firstChild(); lastCell->addChild(child, beforeChild); return; } if (beforeChild && !beforeChild->isAnonymous() && beforeChild->parent() == this) { LayoutObject* cell = beforeChild->previousSibling(); if (cell && cell->isTableCell() && cell->isAnonymous()) { cell->addChild(child); return; } } // If beforeChild is inside an anonymous cell, insert into the cell. if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous() && !last->parent()->isBeforeOrAfterContent()) { last->parent()->addChild(child, beforeChild); return; } LayoutTableCell* cell = LayoutTableCell::createAnonymousWithParent(this); addChild(cell, beforeChild); cell->addChild(child); return; } if (beforeChild && beforeChild->parent() != this) beforeChild = splitAnonymousBoxesAroundChild(beforeChild); LayoutTableCell* cell = toLayoutTableCell(child); ASSERT(!beforeChild || beforeChild->isTableCell()); LayoutTableBoxComponent::addChild(cell, beforeChild); // Generated content can result in us having a null section so make sure to // null check our parent. if (parent()) { section()->addCell(cell, this); // When borders collapse, adding a cell can affect the the width of // neighboring cells. LayoutTable* enclosingTable = table(); if (enclosingTable && enclosingTable->collapseBorders()) { if (LayoutTableCell* previousCell = cell->previousCell()) previousCell->setNeedsLayoutAndPrefWidthsRecalc( LayoutInvalidationReason::TableChanged); if (LayoutTableCell* nextCell = cell->nextCell()) nextCell->setNeedsLayoutAndPrefWidthsRecalc( LayoutInvalidationReason::TableChanged); } } if (beforeChild || nextRow()) section()->setNeedsCellRecalc(); }
int TableLayoutAlgorithmFixed::calcWidthArray() { // FIXME: We might want to wait until we have all of the first row before computing for the first time. int usedWidth = 0; // iterate over all <col> elements unsigned nEffCols = m_table->numEffectiveColumns(); m_width.resize(nEffCols); m_width.fill(Length(Auto)); unsigned currentEffectiveColumn = 0; for (LayoutTableCol* col = m_table->firstColumn(); col; col = col->nextColumn()) { // LayoutTableCols don't have the concept of preferred logical width, but we need to clear their dirty bits // so that if we call setPreferredWidthsDirty(true) on a col or one of its descendants, we'll mark it's // ancestors as dirty. col->clearPreferredLogicalWidthsDirtyBits(); // Width specified by column-groups that have column child does not affect column width in fixed layout tables if (col->isTableColumnGroupWithColumnChildren()) continue; Length colStyleLogicalWidth = col->style()->logicalWidth(); int effectiveColWidth = 0; if (colStyleLogicalWidth.isFixed() && colStyleLogicalWidth.value() > 0) effectiveColWidth = colStyleLogicalWidth.value(); unsigned span = col->span(); while (span) { unsigned spanInCurrentEffectiveColumn; if (currentEffectiveColumn >= nEffCols) { m_table->appendEffectiveColumn(span); nEffCols++; m_width.append(Length()); spanInCurrentEffectiveColumn = span; } else { if (span < m_table->spanOfEffectiveColumn(currentEffectiveColumn)) { m_table->splitEffectiveColumn(currentEffectiveColumn, span); nEffCols++; m_width.append(Length()); } spanInCurrentEffectiveColumn = m_table->spanOfEffectiveColumn(currentEffectiveColumn); } // TODO(alancutter): Make this work correctly for calc lengths. if ((colStyleLogicalWidth.isFixed() || colStyleLogicalWidth.hasPercent()) && colStyleLogicalWidth.isPositive()) { m_width[currentEffectiveColumn] = colStyleLogicalWidth; m_width[currentEffectiveColumn] *= spanInCurrentEffectiveColumn; usedWidth += effectiveColWidth * spanInCurrentEffectiveColumn; } span -= spanInCurrentEffectiveColumn; currentEffectiveColumn++; } } // Iterate over the first row in case some are unspecified. LayoutTableSection* section = m_table->topNonEmptySection(); if (!section) return usedWidth; unsigned currentColumn = 0; LayoutTableRow* firstRow = section->firstRow(); for (LayoutTableCell* cell = firstRow->firstCell(); cell; cell = cell->nextCell()) { Length logicalWidth = cell->styleOrColLogicalWidth(); // FIXME: calc() on tables should be handled consistently with other lengths. See bug: https://crbug.com/382725 if (logicalWidth.isCalculated()) logicalWidth = Length(); // Make it Auto unsigned span = cell->colSpan(); int fixedBorderBoxLogicalWidth = 0; // FIXME: Support other length types. If the width is non-auto, it should probably just use // LayoutBox::computeLogicalWidthUsing to compute the width. if (logicalWidth.isFixed() && logicalWidth.isPositive()) { fixedBorderBoxLogicalWidth = cell->adjustBorderBoxLogicalWidthForBoxSizing(logicalWidth.value()); logicalWidth.setValue(fixedBorderBoxLogicalWidth); } unsigned usedSpan = 0; while (usedSpan < span && currentColumn < nEffCols) { float eSpan = m_table->spanOfEffectiveColumn(currentColumn); // Only set if no col element has already set it. if (m_width[currentColumn].isAuto() && logicalWidth.type() != Auto) { m_width[currentColumn] = logicalWidth; m_width[currentColumn] *= eSpan / span; usedWidth += fixedBorderBoxLogicalWidth * eSpan / span; } usedSpan += eSpan; ++currentColumn; } // TableLayoutAlgorithmFixed doesn't use min/maxPreferredLogicalWidths, but we need to clear the // dirty bit on the cell so that we'll correctly mark its ancestors dirty // in case we later call setPreferredLogicalWidthsDirty() on it later. if (cell->preferredLogicalWidthsDirty()) cell->clearPreferredLogicalWidthsDirty(); } return usedWidth; }