LayoutTableRow* LayoutTableRow::createAnonymousWithParent(const LayoutObject* parent) { LayoutTableRow* newRow = LayoutTableRow::createAnonymous(&parent->document()); RefPtr<ComputedStyle> newStyle = ComputedStyle::createAnonymousStyleWithDisplay(parent->styleRef(), TABLE_ROW); newRow->setStyle(newStyle.release()); return newRow; }
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 TableSectionPainter::paintCell(LayoutTableCell* cell, const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutPoint cellPoint = m_layoutTableSection.flipForWritingModeForChild(cell, paintOffset); PaintPhase paintPhase = paintInfo.phase; LayoutTableRow* row = toLayoutTableRow(cell->parent()); if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && BlockPainter(*cell).intersectsPaintRect(paintInfo, paintOffset)) { // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of // the column group, column, row group, row, and then the cell. LayoutTableCol* column = m_layoutTableSection.table()->colElement(cell->col()); LayoutTableCol* columnGroup = column ? column->enclosingColumnGroup() : 0; bool columnHasBackground = column && column->hasBackground(); bool columnGroupHasBackground = columnGroup && columnGroup->hasBackground(); bool sectionHasBackground = m_layoutTableSection.hasBackground(); bool rowHasBackground = row->hasBackground(); if (columnHasBackground || columnGroupHasBackground || sectionHasBackground || rowHasBackground) { TableCellPainter tableCellPainter(*cell); if (!LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, *cell, DisplayItem::TableCellBackgroundFromContainers, paintOffset)) { LayoutObjectDrawingRecorder recorder(*paintInfo.context, *cell, DisplayItem::TableCellBackgroundFromContainers, tableCellPainter.paintBounds(cellPoint, TableCellPainter::AddOffsetFromParent), paintOffset); // Column groups and columns first. // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in // the stack, since we have already opened a transparency layer (potentially) for the table row group. // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the // cell. if (columnGroupHasBackground) tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup); if (columnHasBackground) tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, column); // Paint the row group next. if (sectionHasBackground) tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, &m_layoutTableSection); // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for // painting the row background for the cell. if (rowHasBackground && !row->hasSelfPaintingLayer()) tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, row); } } } if ((!cell->hasSelfPaintingLayer() && !row->hasSelfPaintingLayer())) cell->paint(paintInfo, cellPoint); }
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(); } } }
LayoutTableRow* LayoutTableRow::createAnonymous(Document* document) { LayoutTableRow* layoutObject = new LayoutTableRow(nullptr); layoutObject->setDocumentForAnonymous(document); return layoutObject; }
void TableSectionPainter::paintObject(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { LayoutRect localPaintInvalidationRect = LayoutRect(paintInfo.rect); localPaintInvalidationRect.moveBy(-paintOffset); LayoutRect tableAlignedRect = m_layoutTableSection.logicalRectForWritingModeAndDirection(localPaintInvalidationRect); CellSpan dirtiedRows = m_layoutTableSection.dirtiedRows(tableAlignedRect); CellSpan dirtiedColumns = m_layoutTableSection.dirtiedColumns(tableAlignedRect); HashSet<LayoutTableCell*> overflowingCells = m_layoutTableSection.overflowingCells(); if (dirtiedColumns.start() < dirtiedColumns.end()) { if (!m_layoutTableSection.hasMultipleCellLevels() && !overflowingCells.size()) { if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { // Collapsed borders are painted from the bottom right to the top left so that precedence // due to cell position is respected. for (unsigned r = dirtiedRows.end(); r > dirtiedRows.start(); r--) { unsigned row = r - 1; for (unsigned c = dirtiedColumns.end(); c > dirtiedColumns.start(); c--) { unsigned col = c - 1; LayoutTableSection::CellStruct& current = m_layoutTableSection.cellAt(row, col); LayoutTableCell* cell = current.primaryCell(); if (!cell || (row > dirtiedRows.start() && m_layoutTableSection.primaryCellAt(row - 1, col) == cell) || (col > dirtiedColumns.start() && m_layoutTableSection.primaryCellAt(row, col - 1) == cell)) continue; LayoutPoint cellPoint = m_layoutTableSection.flipForWritingModeForChild(cell, paintOffset); TableCellPainter(*cell).paintCollapsedBorders(paintInfo, cellPoint); } } } else { // Draw the dirty cells in the order that they appear. for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { LayoutTableRow* row = m_layoutTableSection.rowLayoutObjectAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { LayoutTableSection::CellStruct& current = m_layoutTableSection.cellAt(r, c); LayoutTableCell* cell = current.primaryCell(); if (!cell || (r > dirtiedRows.start() && m_layoutTableSection.primaryCellAt(r - 1, c) == cell) || (c > dirtiedColumns.start() && m_layoutTableSection.primaryCellAt(r, c - 1) == cell)) continue; paintCell(cell, paintInfo, paintOffset); } } } } else { // The overflowing cells should be scarce to avoid adding a lot of cells to the HashSet. #if ENABLE(ASSERT) unsigned totalRows = m_layoutTableSection.numRows(); unsigned totalCols = m_layoutTableSection.table()->columns().size(); ASSERT(overflowingCells.size() < totalRows * totalCols * gMaxAllowedOverflowingCellRatioForFastPaintPath); #endif // To make sure we properly paint invalidate the section, we paint invalidated all the overflowing cells that we collected. Vector<LayoutTableCell*> cells; copyToVector(overflowingCells, cells); HashSet<LayoutTableCell*> spanningCells; for (unsigned r = dirtiedRows.start(); r < dirtiedRows.end(); r++) { LayoutTableRow* row = m_layoutTableSection.rowLayoutObjectAt(r); if (row && !row->hasSelfPaintingLayer()) TableRowPainter(*row).paintOutlineForRowIfNeeded(paintInfo, paintOffset); for (unsigned c = dirtiedColumns.start(); c < dirtiedColumns.end(); c++) { LayoutTableSection::CellStruct& current = m_layoutTableSection.cellAt(r, c); if (!current.hasCells()) continue; for (unsigned i = 0; i < current.cells.size(); ++i) { if (overflowingCells.contains(current.cells[i])) continue; if (current.cells[i]->rowSpan() > 1 || current.cells[i]->colSpan() > 1) { if (!spanningCells.add(current.cells[i]).isNewEntry) continue; } cells.append(current.cells[i]); } } } // Sort the dirty cells by paint order. if (!overflowingCells.size()) std::stable_sort(cells.begin(), cells.end(), compareCellPositions); else std::sort(cells.begin(), cells.end(), compareCellPositionsWithOverflowingCells); if (paintInfo.phase == PaintPhaseCollapsedTableBorders) { for (unsigned i = cells.size(); i > 0; --i) { LayoutPoint cellPoint = m_layoutTableSection.flipForWritingModeForChild(cells[i - 1], paintOffset); TableCellPainter(*cells[i - 1]).paintCollapsedBorders(paintInfo, cellPoint); } } else { for (unsigned i = 0; i < cells.size(); ++i) paintCell(cells[i], paintInfo, paintOffset); } } } }
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; }