void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange) { if (!m_renderer || !m_renderer->isTableCell()) return; RenderTableCell* renderCell = toRenderTableCell(m_renderer); rowRange.first = renderCell->rowIndex(); rowRange.second = renderCell->rowSpan(); // since our table might have multiple sections, we have to offset our row appropriately RenderTableSection* section = renderCell->section(); RenderTable* table = renderCell->table(); if (!table || !section) return; RenderTableSection* tableSection = table->topSection(); unsigned rowOffset = 0; while (tableSection) { if (tableSection == section) break; rowOffset += tableSection->numRows(); tableSection = table->sectionBelow(tableSection, SkipEmptySections); } rowRange.first += rowOffset; }
void AccessibilityTableCell::rowIndexRange(pair<int, int>& rowRange) { if (!m_renderer || !m_renderer->isTableCell()) return; RenderTableCell* renderCell = toRenderTableCell(m_renderer); rowRange.first = renderCell->rowIndex(); rowRange.second = renderCell->rowSpan(); // since our table might have multiple sections, we have to offset our row appropriately RenderTableSection* section = renderCell->section(); RenderTable* table = renderCell->table(); if (!table || !section) return; // FIXME: This will skip a table with just a tfoot. Should fix by using RenderTable::topSection. RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); unsigned rowOffset = 0; while (tableSection) { if (tableSection == section) break; rowOffset += tableSection->numRows(); tableSection = table->sectionBelow(tableSection, SkipEmptySections); } rowRange.first += rowOffset; }
void AccessibilityTableCell::rowIndexRange(std::pair<unsigned, unsigned>& rowRange) { if (!m_renderer || !m_renderer->isTableCell()) return; RenderTableCell* renderCell = toRenderTableCell(m_renderer); rowRange.first = renderCell->rowIndex(); rowRange.second = renderCell->rowSpan(); // since our table might have multiple sections, we have to offset our row appropriately RenderTableSection* section = renderCell->section(); RenderTable* table = renderCell->table(); if (!table || !section) return; RenderTableSection* footerSection = table->footer(); unsigned rowOffset = 0; for (RenderTableSection* tableSection = table->topSection(); tableSection; tableSection = table->sectionBelow(tableSection, SkipEmptySections)) { // Don't add row offsets for bottom sections that are placed in before the body section. if (tableSection == footerSection) continue; if (tableSection == section) break; rowOffset += tableSection->numRows(); } rowRange.first += rowOffset; }
void RenderTableCol::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style().border()) table->invalidateCollapsedBorders(); else if (oldStyle->width() != style().width()) { for (auto& section : childrenOfType<RenderTableSection>(*table)) { unsigned nEffCols = table->numEffCols(); for (unsigned j = 0; j < nEffCols; j++) { unsigned rowCount = section.numRows(); for (unsigned i = 0; i < rowCount; i++) { RenderTableCell* cell = section.primaryCellAt(i, j); if (!cell) continue; cell->setPreferredLogicalWidthsDirty(true); } } } } } }
void AccessibilityTableCell::rowIndexRange(pair<int, int>& rowRange) { if (!m_renderer) return; RenderTableCell* renderCell = static_cast<RenderTableCell*>(m_renderer); rowRange.first = renderCell->row(); rowRange.second = renderCell->rowSpan(); // since our table might have multiple sections, we have to offset our row appropriately RenderTableSection* section = renderCell->section(); RenderTable* table = renderCell->table(); if (!table || !section) return; RenderTableSection* tableSection = table->header(); if (!tableSection) tableSection = table->firstBody(); unsigned rowOffset = 0; while (tableSection) { if (tableSection == section) break; rowOffset += tableSection->numRows(); tableSection = table->sectionBelow(tableSection, true); } rowRange.first += rowOffset; }
void TableCellPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (!paintInfo.shouldPaintWithinRoot(&m_renderTableCell)) return; RenderTable* tableElt = m_renderTableCell.table(); if (!tableElt->collapseBorders() && m_renderTableCell.style()->emptyCells() == HIDE && !m_renderTableCell.firstChild()) return; LayoutRect paintRect = paintBounds(paintOffset, DoNotAddOffsetFromParent); RenderDrawingRecorder recorder(paintInfo.context, m_renderTableCell, paintInfo.phase, pixelSnappedIntRect(paintRect)); if (recorder.canUseCachedDrawing()) return; BoxPainter::paintBoxShadow(paintInfo, paintRect, m_renderTableCell.style(), Normal); // Paint our cell background. paintBackgroundsBehindCell(paintInfo, paintOffset, &m_renderTableCell); BoxPainter::paintBoxShadow(paintInfo, paintRect, m_renderTableCell.style(), Inset); if (!m_renderTableCell.style()->hasBorder() || tableElt->collapseBorders()) return; BoxPainter::paintBorder(m_renderTableCell, paintInfo, paintRect, m_renderTableCell.style()); }
void TableCellPainter::paintBackgroundsBehindCell(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, RenderObject* backgroundObject) { if (!paintInfo.shouldPaintWithinRoot(&m_renderTableCell)) return; if (!backgroundObject) return; if (m_renderTableCell.style()->visibility() != VISIBLE) return; RenderTable* tableElt = m_renderTableCell.table(); if (!tableElt->collapseBorders() && m_renderTableCell.style()->emptyCells() == HIDE && !m_renderTableCell.firstChild()) return; Color c = backgroundObject->resolveColor(CSSPropertyBackgroundColor); const FillLayer& bgLayer = backgroundObject->style()->backgroundLayers(); LayoutRect paintRect = paintBounds(paintOffset, backgroundObject != &m_renderTableCell ? 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_renderTableCell || backgroundObject == m_renderTableCell.parent()) && tableElt->collapseBorders(); GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip); if (shouldClip) { LayoutRect clipRect(paintRect.location(), m_renderTableCell.size()); clipRect.expand(m_renderTableCell.borderInsets()); paintInfo.context->clip(clipRect); } BoxPainter(m_renderTableCell).paintFillLayers(paintInfo, c, bgLayer, paintRect, BackgroundBleedNone, CompositeSourceOver, backgroundObject); } }
void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { #if PLATFORM(WKC) CRASH_IF_STACK_OVERFLOW(WKC_STACK_MARGIN_DEFAULT); #endif RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; int w = width(); int h = height(); if (style()->boxShadow()) paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Normal); // Paint our cell background. paintBackgroundsBehindCell(paintInfo, tx, ty, this); if (style()->boxShadow()) paintBoxShadow(paintInfo.context, tx, ty, w, h, style(), Inset); if (!style()->hasBorder() || tableElt->collapseBorders()) return; paintBorder(paintInfo.context, tx, ty, w, h, style()); }
void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { ASSERT(style()->display() == TABLE_ROW); RenderBox::styleDidChange(diff, oldStyle); propagateStyleToAnonymousChildren(); if (section() && oldStyle && style()->logicalHeight() != oldStyle->logicalHeight()) section()->rowLogicalHeightChanged(rowIndex()); // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) table->invalidateCollapsedBorders(); if (table && oldStyle && diff == StyleDifferenceLayout && 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 (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { if (!childBox->isTableCell()) continue; childBox->setChildNeedsLayout(true, MarkOnlyThis); } } } }
IntRect RenderTableRow::getAbsoluteRepaintRect() { // For now, just repaint the whole table. // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we // might have propagated a background color into. RenderTable* parentTable = table(); if (parentTable) return parentTable->getAbsoluteRepaintRect(); else return IntRect(); }
void RenderTableCol::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) table->invalidateCollapsedBorders(); } }
void RenderTableCell::paintMask(PaintInfo& paintInfo, IntSize paintOffset) { if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; paintMaskImages(paintInfo, IntRect(toPoint(paintOffset), size())); }
void TableCellPainter::paintMask(const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { if (m_renderTableCell.style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; RenderTable* tableElt = m_renderTableCell.table(); if (!tableElt->collapseBorders() && m_renderTableCell.style()->emptyCells() == HIDE && !m_renderTableCell.firstChild()) return; BoxPainter(m_renderTableCell).paintMaskImages(paintInfo, paintBounds(paintOffset, DoNotAddOffsetFromParent)); }
LayoutRect RenderTableCol::clippedOverflowRectForRepaint(const RenderLayerModelObject* repaintContainer) const { // For now, just repaint the whole table. // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we // might have propagated a background color or borders into. // FIXME: check for repaintContainer each time here? RenderTable* parentTable = table(); if (!parentTable) return LayoutRect(); return parentTable->clippedOverflowRectForRepaint(repaintContainer); }
static bool shouldEmitTabBeforeNode(Node* node) { RenderObject* r = node->renderer(); // Table cells are delimited by tabs. if (!r || !isTableCell(node)) return false; // Want a tab before every cell other than the first one RenderTableCell* rc = static_cast<RenderTableCell*>(r); RenderTable* t = rc->table(); return t && (t->cellBefore(rc) || t->cellAbove(rc)); }
void RenderTableCell::readyWRATHWidgetBackgroundsBehindCell(PaintedWidgetsOfWRATHHandle& handle, PaintInfoOfWRATH& paintInfo, int tx, int ty, RenderObject* backgroundObject) { RenderTableCell_ReadyWRATHWidgetBackgroundsBehindCell *d(RenderTableCell_ReadyWRATHWidgetBackgroundsBehindCell::object(this, handle)); ContextOfWRATH::AutoPushNode autoPushRoot(paintInfo.wrath_context, d->m_root_node); d->m_fill_layers.visible(false); if (!paintInfo.shouldPaintWithinRoot(this)) return; if (!backgroundObject) return; if (style()->visibility() != VISIBLE) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; if (backgroundObject != this) { tx += x(); ty += y(); } int w = width(); int h = height(); Color c = backgroundObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers(); if (bgLayer->hasImage() || c.isValid()) { // 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 == this || backgroundObject == parent()) && tableElt->collapseBorders(); ContextOfWRATH::AutoPushNode autoPushClip(paintInfo.wrath_context, d->m_clip_node); d->m_clip_node.widget()->node()->clipping_active(false); if (shouldClip) { IntRect clipRect(tx + borderLeft(), ty + borderTop(), w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); ContextOfWRATH::set_clipping(d->m_clip_node, clipRect); d->m_clip_node.widget()->node()->clipping_active(true); } readyWRATHWidgetFillLayers(d->m_fill_layers, paintInfo, c, bgLayer, tx, ty, w, h, BackgroundBleedNone, CompositeSourceOver, backgroundObject); d->m_fill_layers.visible(true); } }
void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) { bool needsTable = false; if (newChild->isListItem()) updateListMarkerNumbers(beforeChild ? beforeChild : m_lastChild); else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) needsTable = !isTable(); else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) needsTable = !isTable(); else if (newChild->isTableSection()) needsTable = !isTable(); else if (newChild->isTableRow()) needsTable = !isTableSection(); else if (newChild->isTableCell()) { needsTable = !isTableRow(); // I'm not 100% sure this is the best way to fix this, but without this // change we recurse infinitely when trying to render the CSS2 test page: // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. // See Radar 2925291. if (needsTable && isTableCell() && !m_firstChild && !newChild->isTableCell()) needsTable = false; } if (needsTable) { RenderTable* table; RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : m_lastChild; if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) table = static_cast<RenderTable*>(afterChild); else { table = new (renderArena()) RenderTable(document() /* is anonymous */); RefPtr<RenderStyle> newStyle = RenderStyle::create(); newStyle->inheritFrom(style()); newStyle->setDisplay(TABLE); table->setStyle(newStyle.release()); addChild(table, beforeChild); } table->addChild(newChild); } else { // just add it... insertChildNode(newChild, beforeChild); } if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { RefPtr<StringImpl> textToTransform = static_cast<RenderText*>(newChild)->originalText(); if (textToTransform) static_cast<RenderText*>(newChild)->setText(textToTransform.release(), true); } }
void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { RenderBox::styleDidChange(diff, oldStyle); propagateStyleToAnonymousChildren(); if (parent()) updateBeforeAndAfterContent(); // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) table->invalidateCollapsedBorders(); } }
void RenderTableCell::paintMask(PaintInfo& paintInfo, int tx, int ty) { #if PLATFORM(WKC) CRASH_IF_STACK_OVERFLOW(WKC_STACK_MARGIN_DEFAULT); #endif if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; int w = width(); int h = height(); paintMaskImages(paintInfo, tx, ty, w, h); }
void AccessibilityTable::addChildren() { if (!isAccessibilityTable()) { AccessibilityRenderObject::addChildren(); return; } ASSERT(!m_haveChildren); m_haveChildren = true; if (!m_renderer || !m_renderer->isTable()) return; RenderTable* table = toRenderTable(m_renderer); // Go through all the available sections to pull out the rows and add them as children. table->recalcSectionsIfNeeded(); unsigned maxColumnCount = 0; RenderTableSection* footer = table->footer(); for (RenderTableSection* tableSection = table->topSection(); tableSection; tableSection = table->sectionBelow(tableSection, SkipEmptySections)) { if (tableSection == footer) continue; addChildrenFromSection(tableSection, maxColumnCount); } // Process the footer last, in case it was ordered earlier in the DOM. if (footer) addChildrenFromSection(footer, maxColumnCount); AXObjectCache* axCache = m_renderer->document().axObjectCache(); // make the columns based on the number of columns in the first body unsigned length = maxColumnCount; for (unsigned i = 0; i < length; ++i) { AccessibilityTableColumn* column = toAccessibilityTableColumn(axCache->getOrCreate(ColumnRole)); column->setColumnIndex((int)i); column->setParent(this); m_columns.append(column); if (!column->accessibilityIsIgnored()) m_children.append(column); } AccessibilityObject* headerContainerObject = headerContainer(); if (headerContainerObject && !headerContainerObject->accessibilityIsIgnored()) m_children.append(headerContainerObject); }
void RenderTableRow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) { ASSERT(style()->display() == TABLE_ROW); RenderBox::styleDidChange(diff, oldStyle); propagateStyleToAnonymousChildren(); if (section() && oldStyle && style()->logicalHeight() != oldStyle->logicalHeight()) section()->rowLogicalHeightChanged(rowIndex()); // If border was changed, notify table. if (parent()) { RenderTable* table = this->table(); if (table && !table->selfNeedsLayout() && !table->normalChildNeedsLayout() && oldStyle && oldStyle->border() != style()->border()) table->invalidateCollapsedBorders(); } }
void Control::parsePanoramaControl(ZVision *engine, Common::SeekableReadStream &stream) { RenderTable *renderTable = engine->getRenderManager()->getRenderTable(); renderTable->setRenderState(RenderTable::PANORAMA); // Loop until we find the closing brace Common::String line = stream.readLine(); trimCommentsAndWhiteSpace(&line); while (!stream.eos() && !line.contains('}')) { if (line.matchString("angle*", true)) { float fov; sscanf(line.c_str(), "angle(%f)", &fov); renderTable->setPanoramaFoV(fov); } else if (line.matchString("linscale*", true)) { float scale; sscanf(line.c_str(), "linscale(%f)", &scale); renderTable->setPanoramaScale(scale); } else if (line.matchString("reversepana*", true)) { uint reverse; sscanf(line.c_str(), "reversepana(%u)", &reverse); if (reverse == 1) { renderTable->setPanoramaReverse(true); } } else if (line.matchString("zeropoint*", true)) { uint point; sscanf(line.c_str(), "zeropoint(%u)", &point); renderTable->setPanoramaZeroPoint(point); } line = stream.readLine(); trimCommentsAndWhiteSpace(&line); } renderTable->generateRenderTable(); }
AccessibilityObject* AccessibilityTableColumn::headerObject() { if (!m_parent) return 0; RenderObject* renderer = m_parent->renderer(); if (!renderer) return 0; if (!m_parent->isAccessibilityTable()) return 0; AccessibilityTable* parentTable = toAccessibilityTable(m_parent); if (parentTable->isAriaTable()) { AccessibilityChildrenVector rowChildren = children(); unsigned childrenCount = rowChildren.size(); for (unsigned i = 0; i < childrenCount; ++i) { AccessibilityObject* cell = rowChildren[i].get(); if (cell->ariaRoleAttribute() == ColumnHeaderRole) return cell; } return 0; } if (!renderer->isTable()) return 0; RenderTable* table = toRenderTable(renderer); AccessibilityObject* headerObject = 0; // try the <thead> section first. this doesn't require th tags headerObject = headerObjectForSection(table->header(), false); if (headerObject) return headerObject; // now try for <th> tags in the first body headerObject = headerObjectForSection(table->firstBody(), true); return headerObject; }
static bool shouldEmitNewlinesBeforeAndAfterNode(Node* node) { // Block flow (versus inline flow) is represented by having // a newline both before and after the element. RenderObject* r = node->renderer(); if (!r) { return (node->hasTagName(blockquoteTag) || node->hasTagName(ddTag) || node->hasTagName(divTag) || node->hasTagName(dlTag) || node->hasTagName(dtTag) || node->hasTagName(h1Tag) || node->hasTagName(h2Tag) || node->hasTagName(h3Tag) || node->hasTagName(h4Tag) || node->hasTagName(h5Tag) || node->hasTagName(h6Tag) || node->hasTagName(hrTag) || node->hasTagName(liTag) || node->hasTagName(listingTag) || node->hasTagName(olTag) || node->hasTagName(pTag) || node->hasTagName(preTag) || node->hasTagName(trTag) || node->hasTagName(ulTag)); } // Need to make an exception for table cells, because they are blocks, but we // want them tab-delimited rather than having newlines before and after. if (isTableCell(node)) return false; // Need to make an exception for table row elements, because they are neither // "inline" or "RenderBlock", but we want newlines for them. if (r->isTableRow()) { RenderTable* t = static_cast<RenderTableRow*>(r)->table(); if (t && !t->isInline()) return true; } return !r->isInline() && r->isRenderBlock() && !r->isFloatingOrPositioned() && !r->isBody(); }
void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) { #if PLATFORM(WKC) CRASH_IF_STACK_OVERFLOW(WKC_STACK_MARGIN_DEFAULT); #endif if (!backgroundObject) return; if (style()->visibility() != VISIBLE) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; if (backgroundObject != this) { tx += x(); ty += y(); } int w = width(); int h = height(); Color c = backgroundObject->style()->backgroundColor(); const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers(); if (bgLayer->hasImage() || c.isValid()) { // 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 == this || backgroundObject == parent()) && tableElt->collapseBorders(); if (shouldClip) { IntRect clipRect(tx + borderLeft(), ty + borderTop(), w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); paintInfo.context->save(); paintInfo.context->clip(clipRect); } paintFillLayers(paintInfo, c, bgLayer, tx, ty, w, h); if (shouldClip) paintInfo.context->restore(); } }
HTMLTableElement* AccessibilityTable::tableElement() const { if (!m_renderer->isTable()) return nullptr; RenderTable* table = toRenderTable(m_renderer); if (table->element() && isHTMLTableElement(table->element())) return toHTMLTableElement(table->element()); // If the table has a display:table-row-group, then the RenderTable does not have a pointer to it's HTMLTableElement. // We can instead find it by asking the firstSection for its parent. RenderTableSection* firstBody = table->firstBody(); if (!firstBody || !firstBody->element()) return nullptr; Element* actualTable = firstBody->element()->parentElement(); if (!actualTable || !isHTMLTableElement(actualTable)) return nullptr; return toHTMLTableElement(actualTable); }
void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) { if (!paintInfo.shouldPaintWithinRoot(this)) return; if (!backgroundObject) return; if (style()->visibility() != VISIBLE) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; if (backgroundObject != this) { tx += x(); ty += y(); } int w = width(); int h = height(); Color c = backgroundObject->style()->visitedDependentColor(CSSPropertyBackgroundColor); const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers(); if (bgLayer->hasImage() || c.isValid()) { // 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 == this || backgroundObject == parent()) && tableElt->collapseBorders(); GraphicsContextStateSaver stateSaver(*paintInfo.context, shouldClip); if (shouldClip) { IntRect clipRect(tx + borderLeft(), ty + borderTop(), w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); paintInfo.context->clip(clipRect); } paintFillLayers(paintInfo, c, bgLayer, IntRect(tx, ty, w, h), BackgroundBleedNone, CompositeSourceOver, backgroundObject); } }
void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) { if (!paintInfo.shouldPaintWithinRoot(this)) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; IntRect paintRect = IntRect(IntPoint(tx, ty), size()); paintBoxShadow(paintInfo, paintRect, style(), Normal); // Paint our cell background. paintBackgroundsBehindCell(paintInfo, tx, ty, this); paintBoxShadow(paintInfo, paintRect, style(), Inset); if (!style()->hasBorder() || tableElt->collapseBorders()) return; paintBorder(paintInfo, paintRect, style()); }
void RenderTableCell::readyWRATHWidgetBoxDecorations(PaintedWidgetsOfWRATHHandle& handle, PaintInfoOfWRATH& paintInfo, int tx, int ty) { RenderTableCell_ReadyWRATHWidgetBoxDecorations *d(RenderTableCell_ReadyWRATHWidgetBoxDecorations::object(this, handle)); ContextOfWRATH::AutoPushNode autoPushRoot(paintInfo.wrath_context, d->m_root_node); d->m_box_shadow.visible(false); d->m_backgrounds.visible(false); d->m_latter_box_shadow.visible(false); d->m_border.visible(false); if (!paintInfo.shouldPaintWithinRoot(this)) return; RenderTable* tableElt = table(); if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) return; int w = width(); int h = height(); readyWRATHWidgetBoxShadow(d->m_box_shadow, paintInfo.wrath_context, tx, ty, w, h, style(), Normal); d->m_box_shadow.visible(true); // Paint our cell background. readyWRATHWidgetBackgroundsBehindCell(d->m_backgrounds, paintInfo, tx, ty, this); d->m_backgrounds.visible(true); readyWRATHWidgetBoxShadow(d->m_latter_box_shadow, paintInfo.wrath_context, tx, ty, w, h, style(), Inset); d->m_latter_box_shadow.visible(true); if (!style()->hasBorder() || tableElt->collapseBorders()) return; readyWRATHWidgetBorder(d->m_border, paintInfo.wrath_context, tx, ty, w, h, style()); d->m_border.visible(true); }
bool AccessibilityTable::isDataTable() const { if (!m_renderer) return false; // Do not consider it a data table is 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. RenderTable* table = toRenderTable(m_renderer); if (!table->element() || !isHTMLTableElement(table->element())) return false; // if there is a caption element, summary, THEAD, or TFOOT section, it's most certainly a data table HTMLTableElement* tableElement = toHTMLTableElement(table->element()); 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. for (const auto& child : childrenOfType<Element>(*tableElement)) { if (child.hasTagName(colTag) || child.hasTagName(colgroupTag)) 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(); RenderTableSection* firstBody = table->firstBody(); if (!firstBody) return false; int numCols = firstBody->numColumns(); 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 RenderStyle& tableStyle = table->style(); 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) { RenderTableCell* cell = firstBody->primaryCellAt(row, col); if (!cell) continue; Element* cellElement = cell->element(); if (!cellElement) continue; if (cell->width() < 1 || cell->height() < 1) continue; validCellCount++; bool isTHCell = cellElement->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 (cellElement->hasTagName(tdTag) || cellElement->hasTagName(thTag)) { HTMLTableCellElement* tableCellElement = toHTMLTableCellElement(cellElement); if (!tableCellElement->headers().isEmpty() || !tableCellElement->abbr().isEmpty() || !tableCellElement->axis().isEmpty() || !tableCellElement->scope().isEmpty()) return true; } const RenderStyle& renderStyle = cell->style(); // If the empty-cells style is set, we'll call it a data table. if (renderStyle.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 = renderStyle.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) { RenderObject* renderRow = cell->parent(); if (!renderRow || !renderRow->isBoxModelObject() || !toRenderBoxModelObject(renderRow)->isTableRow()) continue; const RenderStyle& rowRenderStyle = renderRow->style(); Color rowColor = rowRenderStyle.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; }