void LayoutTableRow::styleDidChange(StyleDifference diff, const ComputedStyle* oldStyle)
{
    ASSERT(style()->display() == TABLE_ROW);

    LayoutBox::styleDidChange(diff, oldStyle);
    propagateStyleToAnonymousChildren();

    if (section() && oldStyle && style()->logicalHeight() != oldStyle->logicalHeight())
        section()->rowLogicalHeightChanged(this);

    // 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();

        if (table && oldStyle && diff.needsFullLayout() && needsLayout() && table->collapseBorders() && borderWidthChanged(oldStyle, style())) {
            // If the border width changes on a row, we need to make sure the cells in the row know to lay out again.
            // This only happens when borders are collapsed, since they end up affecting the border sides of the cell
            // itself.
            for (LayoutBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) {
                if (!childBox->isTableCell())
                    continue;
                childBox->setChildNeedsLayout();
            }
        }
    }
}
void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    LayoutTable* table = m_layoutTableCell.table();
    if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EmptyCellsHide && !m_layoutTableCell.firstChild())
        return;

    bool needsToPaintBorder = m_layoutTableCell.styleRef().hasBorderDecoration() && !table->collapseBorders();
    if (!m_layoutTableCell.hasBackground() && !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder)
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground))
        return;

    LayoutRect visualOverflowRect = m_layoutTableCell.visualOverflowRect();
    visualOverflowRect.moveBy(paintOffset);
    // TODO(chrishtr): the pixel-snapping here is likely incorrect.
    LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(visualOverflowRect));

    LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent);

    BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Normal);

    // Paint our cell background.
    paintBackgroundsBehindCell(paintInfo, paintOffset, &m_layoutTableCell, DisplayItem::BoxDecorationBackground);

    BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Inset);

    if (!needsToPaintBorder)
        return;

    BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, m_layoutTableCell.styleRef());
}
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;
}
void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell))
        return;

    LayoutTable* table = m_layoutTableCell.table();
    if (!table->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild())
        return;

    bool needsToPaintBorder = m_layoutTableCell.styleRef().hasBorder() && !table->collapseBorders();
    if (!m_layoutTableCell.hasBackground() && !m_layoutTableCell.styleRef().boxShadow() && !needsToPaintBorder)
        return;

    LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent);
    LayoutObjectDrawingRecorder recorder(*paintInfo.context, m_layoutTableCell, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(paintRect));
    if (recorder.canUseCachedDrawing())
        return;

    BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Normal);

    // Paint our cell background.
    paintBackgroundsBehindCell(paintInfo, paintOffset, &m_layoutTableCell);

    BoxPainter::paintBoxShadow(paintInfo, paintRect, m_layoutTableCell.styleRef(), Inset);

    if (!needsToPaintBorder)
        return;

    BoxPainter::paintBorder(m_layoutTableCell, paintInfo, paintRect, m_layoutTableCell.styleRef());
}
void TableCellPainter::paintBackgroundsBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutObject* backgroundObject)
{
    if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell))
        return;

    if (!backgroundObject)
        return;

    if (m_layoutTableCell.style()->visibility() != VISIBLE)
        return;

    LayoutTable* tableElt = m_layoutTableCell.table();
    if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild())
        return;

    Color c = backgroundObject->resolveColor(CSSPropertyBackgroundColor);
    const FillLayer& bgLayer = backgroundObject->style()->backgroundLayers();

    LayoutRect paintRect = paintBounds(paintOffset, backgroundObject != &m_layoutTableCell ? AddOffsetFromParent : DoNotAddOffsetFromParent);

    if (bgLayer.hasImage() || c.alpha()) {
        // We have to clip here because the background would paint
        // on top of the borders otherwise.  This only matters for cells and rows.
        bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == &m_layoutTableCell || backgroundObject == m_layoutTableCell.parent()) && tableElt->collapseBorders();
        GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip);
        if (shouldClip) {
            LayoutRect clipRect(paintRect.location(), m_layoutTableCell.size());
            clipRect.expand(m_layoutTableCell.borderInsets());
            paintInfo.context->clip(clipRect);
        }
        BoxPainter(m_layoutTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, SkXfermode::kSrcOver_Mode, backgroundObject);
    }
}
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 TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (m_layoutTableCell.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
        return;

    LayoutTable* tableElt = m_layoutTableCell.table();
    if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild())
        return;

    BoxPainter(m_layoutTableCell).paintMaskImages(paintInfo, paintBounds(paintOffset, DoNotAddOffsetFromParent));
}
LayoutRect LayoutTableCol::clippedOverflowRectForPaintInvalidation(const LayoutBoxModelObject* paintInvalidationContainer, const PaintInvalidationState* paintInvalidationState) const
{
    // For now, just paint invalidate the whole table.
    // FIXME: Find a better way to do this, e.g., need to paint invalidate all the cells that we
    // might have propagated a background color or borders into.
    // FIXME: check for paintInvalidationContainer each time here?

    LayoutTable* parentTable = table();
    if (!parentTable)
        return LayoutRect();
    return parentTable->clippedOverflowRectForPaintInvalidation(paintInvalidationContainer, paintInvalidationState);
}
void TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (m_layoutTableCell.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
        return;

    LayoutTable* tableElt = m_layoutTableCell.table();
    if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == EmptyCellsHide && !m_layoutTableCell.firstChild())
        return;

    if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_layoutTableCell, paintInfo.phase))
        return;

    LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent);
    LayoutObjectDrawingRecorder recorder(paintInfo.context, m_layoutTableCell, paintInfo.phase, paintRect);
    BoxPainter(m_layoutTableCell).paintMaskImages(paintInfo, paintRect);
}
LayoutRect LayoutTableCol::localOverflowRectForPaintInvalidation() const
{
    // Entire table gets invalidated, instead of invalidating
    // every cell in the column.
    // This is simpler, but suboptimal.

    LayoutTable* table = this->table();
    if (!table)
        return LayoutRect();

    // The correctness of this method depends on the fact that LayoutTableCol's
    // location is always zero.
    ASSERT(this->location() == LayoutPoint());

    return table->localOverflowRectForPaintInvalidation();
}
Beispiel #11
0
void LayoutTableRow::styleDidChange(StyleDifference diff,
                                    const ComputedStyle* oldStyle) {
  DCHECK_EQ(style()->display(), EDisplay::TableRow);

  LayoutTableBoxComponent::styleDidChange(diff, oldStyle);
  propagateStyleToAnonymousChildren();

  if (!oldStyle)
    return;

  if (section() && style()->logicalHeight() != oldStyle->logicalHeight())
    section()->rowLogicalHeightChanged(this);

  if (!parent())
    return;
  LayoutTable* table = this->table();
  if (!table)
    return;

  if (!table->selfNeedsLayout() && !table->normalChildNeedsLayout() &&
      oldStyle->border() != style()->border())
    table->invalidateCollapsedBorders();

  if (LayoutTableBoxComponent::doCellsHaveDirtyWidth(*this, *table, diff,
                                                     *oldStyle)) {
    // If the border width changes on a row, we need to make sure the cells in
    // the row know to lay out again.
    // This only happens when borders are collapsed, since they end up affecting
    // the border sides of the cell itself.
    for (LayoutBox* childBox = firstChildBox(); childBox;
         childBox = childBox->nextSiblingBox()) {
      if (!childBox->isTableCell())
        continue;
      // TODO(dgrogan) Add a layout test showing that setChildNeedsLayout is
      // needed instead of setNeedsLayout.
      childBox->setChildNeedsLayout();
      childBox->setPreferredLogicalWidthsDirty(MarkOnlyThis);
    }
    // Most table componenents can rely on LayoutObject::styleDidChange
    // to mark the container chain dirty. But LayoutTableSection seems
    // to never clear its dirty bit, which stops the propagation. So
    // anything under LayoutTableSection has to restart the propagation
    // at the table.
    // TODO(dgrogan): Make LayoutTableSection clear its dirty bit.
    table->setPreferredLogicalWidthsDirty();
  }
}
Beispiel #12
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 #13
0
void TableCellPainter::paintBackgroundsBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutObject* backgroundObject, DisplayItem::Type type)
{
    if (!paintInfo.shouldPaintWithinRoot(&m_layoutTableCell))
        return;

    if (!backgroundObject)
        return;

    if (m_layoutTableCell.style()->visibility() != VISIBLE)
        return;

    LayoutTable* tableElt = m_layoutTableCell.table();
    if (!tableElt->collapseBorders() && m_layoutTableCell.style()->emptyCells() == HIDE && !m_layoutTableCell.firstChild())
        return;

    LayoutRect paintRect = paintBounds(paintOffset, backgroundObject != &m_layoutTableCell ? AddOffsetFromParent : DoNotAddOffsetFromParent);

    // Record drawing only if the cell is painting background from containers.
    Optional<LayoutObjectDrawingRecorder> recorder;
    if (backgroundObject != &m_layoutTableCell) {
        LayoutPoint adjustedPaintOffset = paintRect.location();
        if (LayoutObjectDrawingRecorder::useCachedDrawingIfPossible(*paintInfo.context, m_layoutTableCell, type, adjustedPaintOffset))
            return;
        recorder.emplace(*paintInfo.context, m_layoutTableCell, type, paintRect, adjustedPaintOffset);
    } else {
        ASSERT(paintRect.location() == paintOffset);
    }

    Color c = backgroundObject->resolveColor(CSSPropertyBackgroundColor);
    const FillLayer& bgLayer = backgroundObject->style()->backgroundLayers();
    if (bgLayer.hasImage() || c.alpha()) {
        // We have to clip here because the background would paint
        // on top of the borders otherwise.  This only matters for cells and rows.
        bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == &m_layoutTableCell || backgroundObject == m_layoutTableCell.parent()) && tableElt->collapseBorders();
        GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip);
        if (shouldClip) {
            LayoutRect clipRect(paintRect.location(), m_layoutTableCell.size());
            clipRect.expand(m_layoutTableCell.borderInsets());
            paintInfo.context->clip(pixelSnappedIntRect(clipRect));
        }
        BoxPainter(m_layoutTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, SkXfermode::kSrcOver_Mode, backgroundObject);
    }
}
Beispiel #14
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();
}
Beispiel #15
0
void AXTable::addChildren()
{
    ASSERT(!isDetached());
    if (!isAXTable()) {
        AXLayoutObject::addChildren();
        return;
    }

    ASSERT(!m_haveChildren);

    m_haveChildren = true;
    if (!m_layoutObject || !m_layoutObject->isTable())
        return;

    LayoutTable* table = toLayoutTable(m_layoutObject);
    AXObjectCacheImpl& axCache = axObjectCache();

    Node* tableNode = table->node();
    if (!isHTMLTableElement(tableNode))
        return;

    // Add caption
    if (HTMLTableCaptionElement* caption  = toHTMLTableElement(tableNode)->caption()) {
        AXObject* captionObject = axCache.getOrCreate(caption);
        if (captionObject && !captionObject->accessibilityIsIgnored())
            m_children.append(captionObject);
    }

    // Go through all the available sections to pull out the rows and add them as children.
    table->recalcSectionsIfNeeded();
    LayoutTableSection* tableSection = table->topSection();
    if (!tableSection)
        return;

    LayoutTableSection* initialTableSection = tableSection;
    while (tableSection) {

        HeapHashSet<Member<AXObject>> appendedRows;
        unsigned numRows = tableSection->numRows();
        for (unsigned rowIndex = 0; rowIndex < numRows; ++rowIndex) {

            LayoutTableRow* layoutRow = tableSection->rowLayoutObjectAt(rowIndex);
            if (!layoutRow)
                continue;

            AXObject* rowObject = axCache.getOrCreate(layoutRow);
            if (!rowObject || !rowObject->isTableRow())
                continue;

            AXTableRow* row = toAXTableRow(rowObject);
            // We need to check every cell for a new row, because cell spans
            // can cause us to miss rows if we just check the first column.
            if (appendedRows.contains(row))
                continue;

            row->setRowIndex(static_cast<int>(m_rows.size()));
            m_rows.append(row);
            if (!row->accessibilityIsIgnored())
                m_children.append(row);
            appendedRows.add(row);
        }

        tableSection = table->sectionBelow(tableSection, SkipEmptySections);
    }

    // make the columns based on the number of columns in the first body
    unsigned length = initialTableSection->numEffectiveColumns();
    for (unsigned i = 0; i < length; ++i) {
        AXTableColumn* column = toAXTableColumn(axCache.getOrCreate(ColumnRole));
        column->setColumnIndex((int)i);
        column->setParent(this);
        m_columns.append(column);
        if (!column->accessibilityIsIgnored())
            m_children.append(column);
    }

    AXObject* headerContainerObject = headerContainer();
    if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored())
        m_children.append(headerContainerObject);
}
Beispiel #16
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;
}