nsresult TableBackgroundPainter::PaintTableFrame(nsTableFrame* aTableFrame, nsTableRowGroupFrame* aFirstRowGroup, nsTableRowGroupFrame* aLastRowGroup, const nsMargin& aDeflate) { NS_PRECONDITION(aTableFrame, "null frame"); TableBackgroundData tableData; tableData.SetFull(aTableFrame); tableData.mRect.MoveTo(0,0); //using table's coords tableData.mRect.Deflate(aDeflate); if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) { if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) { //only handle non-degenerate tables; we need a more robust BC model //to make degenerate tables' borders reasonable to deal with nsMargin border, tempBorder; nsTableColFrame* colFrame = aTableFrame->GetColFrame(mNumCols - 1); if (colFrame) { colFrame->GetContinuousBCBorderWidth(tempBorder); } border.right = tempBorder.right; aLastRowGroup->GetContinuousBCBorderWidth(tempBorder); border.bottom = tempBorder.bottom; nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow(); if (rowFrame) { rowFrame->GetContinuousBCBorderWidth(tempBorder); border.top = tempBorder.top; } border.left = aTableFrame->GetContinuousLeftBCBorderWidth(); nsresult rv = tableData.SetBCBorder(border, this); if (NS_FAILED(rv)) { tableData.Destroy(mPresContext); return rv; } } } if (tableData.IsVisible()) { nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext, tableData.mFrame, mDirtyRect, tableData.mRect + mRenderPt, tableData.mFrame->StyleContext(), *tableData.mBorder, mBGPaintFlags); } tableData.Destroy(mPresContext); return NS_OK; }
DrawResult TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame, const TableBackgroundData& aRowGroupBGData, TableBackgroundData aRowBGData, bool aPassThrough) { MOZ_ASSERT(aFrame, "null frame"); /* Load row data */ WritingMode wm = aFrame->GetWritingMode(); if (aPassThrough) { aRowBGData.MakeInvisible(); } else { if (mIsBorderCollapse && aRowBGData.ShouldSetBCBorder()) { LogicalMargin border(wm); nsTableRowFrame* nextRow = aFrame->GetNextRow(); if (nextRow) { //outer bStart after us is inner bEnd for us border.BEnd(wm) = nextRow->GetOuterBStartContBCBorderWidth(); } else { //acquire rg's bEnd border nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame->GetParent()); rowGroup->GetContinuousBCBorderWidth(wm, border); } //get the rest of the borders; will overwrite all but bEnd aFrame->GetContinuousBCBorderWidth(wm, border); aRowBGData.SetBCBorder(border.GetPhysicalMargin(wm)); } aPassThrough = !aRowBGData.IsVisible(); } /* Translate */ if (eOrigin_TableRow == mOrigin) { /* If we originate from the row, then make the row the origin. */ aRowBGData.mRect.MoveTo(0, 0); } //else: Use row group's coord system -> no translation necessary DrawResult result = DrawResult::SUCCESS; for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) { nsRect cellBGRect, rowBGRect, rowGroupBGRect, colBGRect; ComputeCellBackgrounds(cell, aRowGroupBGData, aRowBGData, cellBGRect, rowBGRect, rowGroupBGRect, colBGRect); // Find the union of all the cell background layers. nsRect combinedRect(cellBGRect); combinedRect.UnionRect(combinedRect, rowBGRect); combinedRect.UnionRect(combinedRect, rowGroupBGRect); combinedRect.UnionRect(combinedRect, colBGRect); if (combinedRect.Intersects(mDirtyRect)) { bool passCell = aPassThrough || cell->IsPseudoStackingContextFromStyle(); DrawResult cellResult = PaintCell(cell, aRowGroupBGData, aRowBGData, cellBGRect, rowBGRect, rowGroupBGRect, colBGRect, passCell); UpdateDrawResult(&result, cellResult); } } return result; }
DrawResult TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame, TableBackgroundData aRowGroupBGData, bool aPassThrough) { MOZ_ASSERT(aFrame, "null frame"); nsTableRowFrame* firstRow = aFrame->GetFirstRow(); WritingMode wm = aFrame->GetWritingMode(); /* Load row group data */ if (aPassThrough) { aRowGroupBGData.MakeInvisible(); } else { if (mIsBorderCollapse && aRowGroupBGData.ShouldSetBCBorder()) { LogicalMargin border(wm); if (firstRow) { //pick up first row's bstart border (= rg bstart border) firstRow->GetContinuousBCBorderWidth(wm, border); /* (row group doesn't store its bstart border) */ } //overwrite sides+bottom borders with rg's own aFrame->GetContinuousBCBorderWidth(wm, border); aRowGroupBGData.SetBCBorder(border.GetPhysicalMargin(wm)); } aPassThrough = !aRowGroupBGData.IsVisible(); } /* translate everything into row group coord system*/ if (eOrigin_TableRowGroup != mOrigin) { TranslateContext(aRowGroupBGData.mRect.x, aRowGroupBGData.mRect.y); } nsRect rgRect = aRowGroupBGData.mRect; aRowGroupBGData.mRect.MoveTo(0, 0); /* Find the right row to start with */ // Note that mDirtyRect - mRenderPt is guaranteed to be in the row // group's coordinate system here, so passing its .y to // GetFirstRowContaining is ok. nscoord overflowAbove; nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &overflowAbove); // Sadly, it seems like there may be non-row frames in there... or something? // There are certainly null-checks in GetFirstRow() and GetNextRow(). :( while (cursor && cursor->GetType() != nsGkAtoms::tableRowFrame) { cursor = cursor->GetNextSibling(); } // It's OK if cursor is null here. nsTableRowFrame* row = static_cast<nsTableRowFrame*>(cursor); if (!row) { // No useful cursor; just start at the top. Don't bother to set up a // cursor; if we've gotten this far then we've already built the display // list for the rowgroup, so not having a cursor means that there's some // good reason we don't have a cursor and we shouldn't create one here. row = firstRow; } DrawResult result = DrawResult::SUCCESS; /* Finally paint */ for (; row; row = row->GetNextRow()) { TableBackgroundData rowBackgroundData(row); // Be sure to consider our positions both pre- and post-relative // positioning, since we potentially need to paint at both places. nscoord rowY = std::min(rowBackgroundData.mRect.y, row->GetNormalPosition().y); // Intersect wouldn't handle rowspans. if (cursor && (mDirtyRect.YMost() - mRenderPt.y) <= (rowY - overflowAbove)) { // All done; cells originating in later rows can't intersect mDirtyRect. break; } DrawResult rowResult = PaintRow(row, aRowGroupBGData, rowBackgroundData, aPassThrough || row->IsPseudoStackingContextFromStyle()); UpdateDrawResult(&result, rowResult); } /* translate back into table coord system */ if (eOrigin_TableRowGroup != mOrigin) { TranslateContext(-rgRect.x, -rgRect.y); } return result; }
nsresult TableBackgroundPainter::PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate, bool aPaintTableBackground) { NS_PRECONDITION(aTableFrame, "null table frame"); nsTableFrame::RowGroupArray rowGroups; aTableFrame->OrderRowGroups(rowGroups); if (rowGroups.Length() < 1) { //degenerate case if (aPaintTableBackground) { PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0)); } /* No cells; nothing else to paint */ return NS_OK; } if (aPaintTableBackground) { PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1], aDeflate); } /*Set up column background/border data*/ if (mNumCols > 0) { nsFrameList& colGroupList = aTableFrame->GetColGroups(); NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup"); mCols = new ColData[mNumCols]; if (!mCols) return NS_ERROR_OUT_OF_MEMORY; TableBackgroundData* cgData = nullptr; nsMargin border; /* BC left borders aren't stored on cols, but the previous column's right border is the next one's left border.*/ //Start with table's left border. nscoord lastLeftBorder = aTableFrame->GetContinuousLeftBCBorderWidth(); for (nsTableColGroupFrame* cgFrame = static_cast<nsTableColGroupFrame*>(colGroupList.FirstChild()); cgFrame; cgFrame = static_cast<nsTableColGroupFrame*>(cgFrame->GetNextSibling())) { if (cgFrame->GetColCount() < 1) { //No columns, no cells, so no need for data continue; } /*Create data struct for column group*/ cgData = new TableBackgroundData; if (!cgData) return NS_ERROR_OUT_OF_MEMORY; cgData->SetFull(cgFrame); if (mIsBorderCollapse && cgData->ShouldSetBCBorder()) { border.left = lastLeftBorder; cgFrame->GetContinuousBCBorderWidth(border); nsresult rv = cgData->SetBCBorder(border, this); if (NS_FAILED(rv)) { cgData->Destroy(mPresContext); delete cgData; return rv; } } // Boolean that indicates whether mCols took ownership of cgData bool cgDataOwnershipTaken = false; /*Loop over columns in this colgroup*/ for (nsTableColFrame* col = cgFrame->GetFirstColumn(); col; col = static_cast<nsTableColFrame*>(col->GetNextSibling())) { /*Create data struct for column*/ uint32_t colIndex = col->GetColIndex(); NS_ASSERTION(colIndex < mNumCols, "prevent array boundary violation"); if (mNumCols <= colIndex) break; mCols[colIndex].mCol.SetFull(col); //Bring column mRect into table's coord system mCols[colIndex].mCol.mRect.MoveBy(cgData->mRect.x, cgData->mRect.y); //link to parent colgroup's data mCols[colIndex].mColGroup = cgData; cgDataOwnershipTaken = true; if (mIsBorderCollapse) { border.left = lastLeftBorder; lastLeftBorder = col->GetContinuousBCBorderWidth(border); if (mCols[colIndex].mCol.ShouldSetBCBorder()) { nsresult rv = mCols[colIndex].mCol.SetBCBorder(border, this); if (NS_FAILED(rv)) return rv; } } } if (!cgDataOwnershipTaken) { cgData->Destroy(mPresContext); delete cgData; } } } for (uint32_t i = 0; i < rowGroups.Length(); i++) { nsTableRowGroupFrame* rg = rowGroups[i]; mRowGroup.SetFrame(rg); // Need to compute the right rect via GetOffsetTo, since the row // group may not be a child of the table. mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame)); if (mRowGroup.mRect.Intersects(mDirtyRect - mRenderPt)) { nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle()); if (NS_FAILED(rv)) return rv; } } return NS_OK; }