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();
                }
            }
        }
    }
}
Beispiel #3
0
void AXTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange)
{
    if (!m_layoutObject || !m_layoutObject->isTableCell())
        return;

    LayoutTableCell* layoutCell = toLayoutTableCell(m_layoutObject);
    rowRange.first = layoutCell->rowIndex();
    rowRange.second = layoutCell->rowSpan();

    // since our table might have multiple sections, we have to offset our row appropriately
    LayoutTableSection* section = layoutCell->section();
    LayoutTable* table = layoutCell->table();
    if (!table || !section)
        return;

    LayoutTableSection* tableSection = table->topSection();
    unsigned rowOffset = 0;
    while (tableSection) {
        if (tableSection == section)
            break;
        rowOffset += tableSection->numRows();
        tableSection = table->sectionBelow(tableSection, SkipEmptySections);
    }

    rowRange.first += rowOffset;
}
Beispiel #4
0
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 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();
}
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());
    LayoutBox::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);

    if (beforeChild || nextRow())
        section()->setNeedsCellRecalc();
}
HTMLTableCellElement* HTMLTableCellElement::cellAbove() const
{
    LayoutObject* cellLayoutObject = layoutObject();
    if (!cellLayoutObject)
        return nullptr;
    if (!cellLayoutObject->isTableCell())
        return nullptr;

    LayoutTableCell* tableCellLayoutObject = toLayoutTableCell(cellLayoutObject);
    LayoutTableCell* cellAboveLayoutObject = tableCellLayoutObject->table()->cellAbove(tableCellLayoutObject);
    if (!cellAboveLayoutObject)
        return nullptr;

    return toHTMLTableCellElement(cellAboveLayoutObject->node());
}
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();
        }
    }
}
Beispiel #9
0
void AXTableColumn::headerObjectsForColumn(AXObjectVector& headers) {
  if (!m_parent)
    return;

  LayoutObject* layoutObject = m_parent->getLayoutObject();
  if (!layoutObject)
    return;

  if (!m_parent->isAXTable())
    return;

  if (toAXTable(m_parent)->isAriaTable()) {
    for (const auto& cell : children()) {
      if (cell->roleValue() == ColumnHeaderRole)
        headers.append(cell);
    }
    return;
  }

  if (!layoutObject->isTable())
    return;

  LayoutTable* table = toLayoutTable(layoutObject);
  LayoutTableSection* tableSection = table->topSection();
  for (; tableSection;
       tableSection = table->sectionBelow(tableSection, SkipEmptySections)) {
    unsigned numCols = tableSection->numEffectiveColumns();
    if (m_columnIndex >= numCols)
      continue;
    unsigned numRows = tableSection->numRows();
    for (unsigned r = 0; r < numRows; r++) {
      LayoutTableCell* layoutCell =
          tableSection->primaryCellAt(r, m_columnIndex);
      if (!layoutCell)
        continue;

      AXObject* cell = axObjectCache().getOrCreate(layoutCell->node());
      if (!cell || !cell->isTableCell() || headers.contains(cell))
        continue;

      if (toAXTableCell(cell)->scanToDecideHeaderRole() == ColumnHeaderRole)
        headers.append(cell);
    }
  }
}
Beispiel #10
0
// Hit Testing
bool LayoutTableRow::nodeAtPoint(HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction action)
{
    // Table rows cannot ever be hit tested.  Effectively they do not exist.
    // Just forward to our children always.
    for (LayoutTableCell* cell = lastCell(); cell; cell = cell->previousCell()) {
        // FIXME: We have to skip over inline flows, since they can show up inside table rows
        // at the moment (a demoted inline <form> for example). If we ever implement a
        // table-specific hit-test method (which we should do for performance reasons anyway),
        // then we can remove this check.
        if (!cell->hasSelfPaintingLayer()) {
            LayoutPoint cellPoint = flipForWritingModeForChild(cell, accumulatedOffset);
            if (cell->nodeAtPoint(result, locationInContainer, cellPoint, action)) {
                updateHitTestResult(result, locationInContainer.point() - toLayoutSize(cellPoint));
                return true;
            }
        }
    }

    return false;
}
Beispiel #11
0
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 TableSectionPainter::paintCell(const LayoutTableCell& cell, const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutPoint cellPoint = m_layoutTableSection.flipForWritingModeForChild(&cell, paintOffset);
    PaintPhase paintPhase = paintInfo.phase;
    const LayoutTableRow* row = toLayoutTableRow(cell.parent());

    if ((paintPhase == PaintPhaseSelfBlockBackground || paintPhase == PaintPhaseBlockBackground)
        && 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.

        LayoutTable::ColAndColGroup colAndColGroup = m_layoutTableSection.table()->colElement(cell.col());
        LayoutTableCol* column = colAndColGroup.col;
        LayoutTableCol* columnGroup = colAndColGroup.colgroup;
        TableCellPainter tableCellPainter(cell);

        // 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 (columnGroup && columnGroup->hasBackground())
            tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, columnGroup, DisplayItem::TableCellBackgroundFromColumnGroup);
        if (column && column->hasBackground())
            tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, column, DisplayItem::TableCellBackgroundFromColumn);

        // Paint the row group next.
        if (m_layoutTableSection.hasBackground())
            tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, &m_layoutTableSection, DisplayItem::TableCellBackgroundFromSection);

        // 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 (row->hasBackground() && !row->hasSelfPaintingLayer())
            tableCellPainter.paintBackgroundsBehindCell(paintInfo, cellPoint, row, DisplayItem::TableCellBackgroundFromRow);
    }
    if ((!cell.hasSelfPaintingLayer() && !row->hasSelfPaintingLayer()))
        cell.paint(paintInfo, cellPoint);
}
Beispiel #13
0
void AXTableCell::columnIndexRange(std::pair<unsigned, unsigned>& columnRange)
{
    if (!m_layoutObject || !m_layoutObject->isTableCell())
        return;

    LayoutTableCell* cell = toLayoutTableCell(m_layoutObject);
    columnRange.first = cell->table()->colToEffCol(cell->col());
    columnRange.second = cell->table()->colToEffCol(cell->col() + cell->colSpan()) - columnRange.first;
}
Beispiel #14
0
bool AXTable::isDataTable() const
{
    if (!m_layoutObject || !node())
        return false;

    // Do not consider it a data table if it has an ARIA role.
    if (hasARIARole())
        return false;

    // When a section of the document is contentEditable, all tables should be
    // treated as data tables, otherwise users may not be able to work with rich
    // text editors that allow creating and editing tables.
    if (node() && node()->hasEditableStyle())
        return true;

    // This employs a heuristic to determine if this table should appear.
    // Only "data" tables should be exposed as tables.
    // Unfortunately, there is no good way to determine the difference
    // between a "layout" table and a "data" table.

    LayoutTable* table = toLayoutTable(m_layoutObject);
    Node* tableNode = table->node();
    if (!isHTMLTableElement(tableNode))
        return false;

    // Do not consider it a data table if any of its descendants have an ARIA role.
    HTMLTableElement* tableElement = toHTMLTableElement(tableNode);
    if (elementHasAriaRole(tableElement->tHead()))
        return false;
    if (elementHasAriaRole(tableElement->tFoot()))
        return false;

    RefPtrWillBeRawPtr<HTMLCollection> bodies = tableElement->tBodies();
    for (unsigned bodyIndex = 0; bodyIndex < bodies->length(); ++bodyIndex) {
        Element* bodyElement = bodies->item(bodyIndex);
        if (elementHasAriaRole(bodyElement))
            return false;
    }

    RefPtrWillBeRawPtr<HTMLTableRowsCollection> rows = tableElement->rows();
    unsigned rowCount = rows->length();
    for (unsigned rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
        HTMLTableRowElement* rowElement = rows->item(rowIndex);
        if (elementHasAriaRole(rowElement))
            return false;
        RefPtrWillBeRawPtr<HTMLCollection> cells = rowElement->cells();
        for (unsigned cellIndex = 0; cellIndex < cells->length(); ++cellIndex) {
            if (elementHasAriaRole(cells->item(cellIndex)))
                return false;
        }
    }

    // If there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table
    if (!tableElement->summary().isEmpty() || tableElement->tHead() || tableElement->tFoot() || tableElement->caption())
        return true;

    // if someone used "rules" attribute than the table should appear
    if (!tableElement->rules().isEmpty())
        return true;

    // if there's a colgroup or col element, it's probably a data table.
    if (Traversal<HTMLTableColElement>::firstChild(*tableElement))
        return true;

    // go through the cell's and check for tell-tale signs of "data" table status
    // cells have borders, or use attributes like headers, abbr, scope or axis
    table->recalcSectionsIfNeeded();
    LayoutTableSection* firstBody = table->firstBody();
    if (!firstBody)
        return false;

    int numCols = firstBody->numEffectiveColumns();
    int numRows = firstBody->numRows();

    // If there's only one cell, it's not a good AXTable candidate.
    if (numRows == 1 && numCols == 1)
        return false;

    // If there are at least 20 rows, we'll call it a data table.
    if (numRows >= 20)
        return true;

    // Store the background color of the table to check against cell's background colors.
    const ComputedStyle* tableStyle = table->style();
    if (!tableStyle)
        return false;
    Color tableBGColor = tableStyle->visitedDependentColor(CSSPropertyBackgroundColor);

    // check enough of the cells to find if the table matches our criteria
    // Criteria:
    //   1) must have at least one valid cell (and)
    //   2) at least half of cells have borders (or)
    //   3) at least half of cells have different bg colors than the table, and there is cell spacing
    unsigned validCellCount = 0;
    unsigned borderedCellCount = 0;
    unsigned backgroundDifferenceCellCount = 0;
    unsigned cellsWithTopBorder = 0;
    unsigned cellsWithBottomBorder = 0;
    unsigned cellsWithLeftBorder = 0;
    unsigned cellsWithRightBorder = 0;

    Color alternatingRowColors[5];
    int alternatingRowColorCount = 0;

    int headersInFirstColumnCount = 0;
    for (int row = 0; row < numRows; ++row) {

        int headersInFirstRowCount = 0;
        for (int col = 0; col < numCols; ++col) {
            LayoutTableCell* cell = firstBody->primaryCellAt(row, col);
            if (!cell)
                continue;
            Node* cellNode = cell->node();
            if (!cellNode)
                continue;

            if (cell->size().width() < 1 || cell->size().height() < 1)
                continue;

            validCellCount++;

            bool isTHCell = cellNode->hasTagName(thTag);
            // If the first row is comprised of all <th> tags, assume it is a data table.
            if (!row && isTHCell)
                headersInFirstRowCount++;

            // If the first column is comprised of all <th> tags, assume it is a data table.
            if (!col && isTHCell)
                headersInFirstColumnCount++;

            // in this case, the developer explicitly assigned a "data" table attribute
            if (isHTMLTableCellElement(*cellNode)) {
                HTMLTableCellElement& cellElement = toHTMLTableCellElement(*cellNode);
                if (!cellElement.headers().isEmpty() || !cellElement.abbr().isEmpty()
                    || !cellElement.axis().isEmpty() || !cellElement.scope().isEmpty())
                    return true;
            }

            const ComputedStyle* computedStyle = cell->style();
            if (!computedStyle)
                continue;

            // If the empty-cells style is set, we'll call it a data table.
            if (computedStyle->emptyCells() == HIDE)
                return true;

            // If a cell has matching bordered sides, call it a (fully) bordered cell.
            if ((cell->borderTop() > 0 && cell->borderBottom() > 0)
                || (cell->borderLeft() > 0 && cell->borderRight() > 0))
                borderedCellCount++;

            // Also keep track of each individual border, so we can catch tables where most
            // cells have a bottom border, for example.
            if (cell->borderTop() > 0)
                cellsWithTopBorder++;
            if (cell->borderBottom() > 0)
                cellsWithBottomBorder++;
            if (cell->borderLeft() > 0)
                cellsWithLeftBorder++;
            if (cell->borderRight() > 0)
                cellsWithRightBorder++;

            // If the cell has a different color from the table and there is cell spacing,
            // then it is probably a data table cell (spacing and colors take the place of borders).
            Color cellColor = computedStyle->visitedDependentColor(CSSPropertyBackgroundColor);
            if (table->hBorderSpacing() > 0 && table->vBorderSpacing() > 0
                && tableBGColor != cellColor && cellColor.alpha() != 1)
                backgroundDifferenceCellCount++;

            // If we've found 10 "good" cells, we don't need to keep searching.
            if (borderedCellCount >= 10 || backgroundDifferenceCellCount >= 10)
                return true;

            // For the first 5 rows, cache the background color so we can check if this table has zebra-striped rows.
            if (row < 5 && row == alternatingRowColorCount) {
                LayoutObject* layoutRow = cell->parent();
                if (!layoutRow || !layoutRow->isBoxModelObject() || !toLayoutBoxModelObject(layoutRow)->isTableRow())
                    continue;
                const ComputedStyle* rowComputedStyle = layoutRow->style();
                if (!rowComputedStyle)
                    continue;
                Color rowColor = rowComputedStyle->visitedDependentColor(CSSPropertyBackgroundColor);
                alternatingRowColors[alternatingRowColorCount] = rowColor;
                alternatingRowColorCount++;
            }
        }

        if (!row && headersInFirstRowCount == numCols && numCols > 1)
            return true;
    }

    if (headersInFirstColumnCount == numRows && numRows > 1)
        return true;

    // if there is less than two valid cells, it's not a data table
    if (validCellCount <= 1)
        return false;

    // half of the cells had borders, it's a data table
    unsigned neededCellCount = validCellCount / 2;
    if (borderedCellCount >= neededCellCount
        || cellsWithTopBorder >= neededCellCount
        || cellsWithBottomBorder >= neededCellCount
        || cellsWithLeftBorder >= neededCellCount
        || cellsWithRightBorder >= neededCellCount)
        return true;

    // half had different background colors, it's a data table
    if (backgroundDifferenceCellCount >= neededCellCount)
        return true;

    // Check if there is an alternating row background color indicating a zebra striped style pattern.
    if (alternatingRowColorCount > 2) {
        Color firstColor = alternatingRowColors[0];
        for (int k = 1; k < alternatingRowColorCount; k++) {
            // If an odd row was the same color as the first row, its not alternating.
            if (k % 2 == 1 && alternatingRowColors[k] == firstColor)
                return false;
            // If an even row is not the same as the first row, its not alternating.
            if (!(k % 2) && alternatingRowColors[k] != firstColor)
                return false;
        }
        return true;
    }

    return false;
}
Beispiel #15
0
void LayoutTableRow::computeOverflow() {
  clearAllOverflows();
  addVisualEffectOverflow();
  for (LayoutTableCell* cell = firstCell(); cell; cell = cell->nextCell())
    addOverflowFromCell(cell);
}
Beispiel #16
0
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;
}