Issue #1355 - Better way to create display items for column backgrounds

Part 1: Remove current table item, as it's never set.

Part 2: Get rid of generic table painting code, and handle each class separately.

Part 4: Hoist outline skipping into col(group) frame code.

Part 5: Skip box-shadow for table column and column groups.

Part 6: Store column and column group backgrounds separately, and then append them before the rest of the table contents.

Part 7: Pass rects in display list coordinates to AppendBackgroundItemsToTop.

Part 8: Create column and column group background display items as part of the cell's BuildDisplayList.

Part 9: Used cached values instead of calling nsDisplayListBuilder::ToReferenceFrame when possible, since it can be expensive when the requested frame isn't the builder's current frame.

Part 10: Make sure we build display items for table parts where only the normal position is visible, since we may need to create background items for ancestors at that position.

Part 11: Create an AutoBuildingDisplayList when we create background items for table columns and column groups, so that we initialize the invalidation state correctly.
This commit is contained in:
win7-7 2020-05-08 01:21:41 +03:00 committed by Roy Tam
commit 9c783146b2
29 changed files with 488 additions and 340 deletions

View file

@ -721,7 +721,6 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
: mReferenceFrame(aReferenceFrame),
mIgnoreScrollFrame(nullptr),
mLayerEventRegions(nullptr),
mCurrentTableItem(nullptr),
mCurrentFrame(aReferenceFrame),
mCurrentReferenceFrame(aReferenceFrame),
mCurrentAGR(&mRootAGR),
@ -731,6 +730,7 @@ nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
mGlassDisplayItem(nullptr),
mScrollInfoItemsForHoisting(nullptr),
mMode(aMode),
mTableBackgroundSet(nullptr),
mCurrentScrollParentId(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarTarget(FrameMetrics::NULL_SCROLL_ID),
mCurrentScrollbarFlags(0),
@ -926,7 +926,6 @@ nsDisplayListBuilder::~nsDisplayListBuilder() {
"All frames should have been unmarked");
NS_ASSERTION(mPresShellStates.Length() == 0,
"All presshells should have been exited");
NS_ASSERTION(!mCurrentTableItem, "No table item should be active");
nsCSSRendering::EndFrameTreesLocked();
@ -2579,8 +2578,7 @@ static nsStyleContext* GetBackgroundStyleContext(nsIFrame* aFrame)
/* static */ void
SetBackgroundClipRegion(DisplayListClipState::AutoSaveRestore& aClipState,
nsIFrame* aFrame, const nsPoint& aToReferenceFrame,
const nsStyleImageLayers::Layer& aLayer,
nsIFrame* aFrame, const nsStyleImageLayers::Layer& aLayer,
const nsRect& aBackgroundRect,
bool aWillPaintBorder)
{
@ -2640,14 +2638,16 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
bool aAllowWillPaintBorderOptimization,
nsStyleContext* aStyleContext,
const nsRect& aBackgroundOriginRect,
nsIFrame* aSecondaryReferenceFrame)
nsIFrame* aSecondaryReferenceFrame,
Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
aAutoBuildingDisplayList)
{
nsStyleContext* bgSC = aStyleContext;
const nsStyleBackground* bg = nullptr;
nsRect bgRect = aBackgroundRect + aBuilder->ToReferenceFrame(aFrame);
nsRect bgRect = aBackgroundRect;
nsRect bgOriginRect = bgRect;
if (!aBackgroundOriginRect.IsEmpty()) {
bgOriginRect = aBackgroundOriginRect + aBuilder->ToReferenceFrame(aFrame);
bgOriginRect = aBackgroundOriginRect;
}
nsPresContext* presContext = aFrame->PresContext();
bool isThemed = aFrame->IsThemed();
@ -2683,8 +2683,6 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
!isThemed && !hasInsetShadow &&
borderStyle->HasBorder();
nsPoint toRef = aBuilder->ToReferenceFrame(aFrame);
// An auxiliary list is necessary in case we have background blending; if that
// is the case, background items need to be wrapped by a blend container to
// isolate blending to the background
@ -2693,6 +2691,9 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
// to create an item for hit testing.
if ((drawBackgroundColor && color != NS_RGBA(0,0,0,0)) ||
aBuilder->IsForEventDelivery()) {
if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
aAutoBuildingDisplayList->emplace(aBuilder, aFrame);
}
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (bg && !aBuilder->IsForEventDelivery()) {
// Disable the will-paint-border optimization for background
@ -2704,7 +2705,7 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
// artifacts along the rounded corners.
bool useWillPaintBorderOptimization = willPaintBorder &&
nsLayoutUtils::HasNonZeroCorner(borderStyle->mBorderRadius);
SetBackgroundClipRegion(clipState, aFrame, toRef,
SetBackgroundClipRegion(clipState, aFrame,
bg->BottomLayer(), bgRect,
useWillPaintBorderOptimization);
}
@ -2757,6 +2758,10 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
continue;
}
if (aAutoBuildingDisplayList && !*aAutoBuildingDisplayList) {
aAutoBuildingDisplayList->emplace(aBuilder, aFrame);
}
if (bg->mImage.mLayers[i].mBlendMode != NS_STYLE_BLEND_NORMAL) {
needBlendContainer = true;
}
@ -2764,8 +2769,8 @@ nsDisplayBackgroundImage::AppendBackgroundItemsToTop(nsDisplayListBuilder* aBuil
DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (!aBuilder->IsForEventDelivery()) {
const nsStyleImageLayers::Layer& layer = bg->mImage.mLayers[i];
SetBackgroundClipRegion(clipState, aFrame, toRef,
layer, bgRect, willPaintBorder);
SetBackgroundClipRegion(clipState, aFrame, layer, bgRect,
willPaintBorder);
}
nsDisplayList thisItemList;

View file

@ -48,6 +48,7 @@ class nsISelection;
class nsIScrollableFrame;
class nsDisplayLayerEventRegions;
class nsDisplayScrollInfoLayer;
class nsDisplayTableBackgroundSet;
class nsCaret;
namespace mozilla {
@ -588,6 +589,16 @@ public:
mSyncDecodeImages = aSyncDecodeImages;
}
nsDisplayTableBackgroundSet* SetTableBackgroundSet(
nsDisplayTableBackgroundSet* aTableSet) {
nsDisplayTableBackgroundSet* old = mTableBackgroundSet;
mTableBackgroundSet = aTableSet;
return old;
}
nsDisplayTableBackgroundSet* GetTableBackgroundSet() const {
return mTableBackgroundSet;
}
/**
* Helper method to generate background painting flags based on the
* information available in the display list builder. Currently only
@ -711,6 +722,10 @@ public:
class AutoBuildingDisplayList {
public:
AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild)
: AutoBuildingDisplayList(
aBuilder, aForChild, aBuilder->GetDirtyRect(), aForChild->IsTransformed()){}
AutoBuildingDisplayList(nsDisplayListBuilder* aBuilder, nsIFrame* aForChild,
const nsRect& aDirtyRect, bool aIsRoot)
: mBuilder(aBuilder),
@ -992,10 +1007,6 @@ public:
return mPreserves3DCtx.mAccumulatedRectLevels;
}
// Helpers for tables
nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
struct OutOfFlowDisplayData {
OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
const DisplayItemScrollClip* aContainingBlockScrollClip,
@ -1213,7 +1224,6 @@ private:
AutoTArray<PresShellState,8> mPresShellStates;
AutoTArray<nsIFrame*,400> mFramesMarkedForDisplay;
AutoTArray<ThemeGeometry,2> mThemeGeometries;
nsDisplayTableItem* mCurrentTableItem;
DisplayListClipState mClipState;
// mCurrentFrame is the frame that we're currently calling (or about to call)
// BuildDisplayList on.
@ -1256,6 +1266,7 @@ private:
nsTArray<DisplayItemScrollClip*> mScrollClipsToDestroy;
nsTArray<DisplayItemClip*> mDisplayItemClipsToDestroy;
nsDisplayListBuilderMode mMode;
nsDisplayTableBackgroundSet* mTableBackgroundSet;
ViewID mCurrentScrollParentId;
ViewID mCurrentScrollbarTarget;
uint32_t mCurrentScrollbarFlags;
@ -2751,7 +2762,9 @@ public:
bool aAllowWillPaintBorderOptimization = true,
nsStyleContext* aStyleContext = nullptr,
const nsRect& aBackgroundOriginRect = nsRect(),
nsIFrame* aSecondaryReferenceFrame = nullptr);
nsIFrame* aSecondaryReferenceFrame = nullptr,
mozilla::Maybe<nsDisplayListBuilder::AutoBuildingDisplayList>*
aAutoBuildingDisplayList = nullptr);
virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager,

View file

@ -263,8 +263,8 @@ nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
nsDisplayButtonBoxShadowOuter(aBuilder, this));
}
nsRect buttonRect;
GetButtonRect(mFrame->GetRectRelativeToSelf(), buttonRect);
nsRect buttonRect =
mFrame->GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(mFrame);
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, mFrame, buttonRect, aBackground);

View file

@ -165,9 +165,11 @@ nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
nsDisplayBoxShadowOuter(aBuilder, this));
}
const nsRect rect =
VisualBorderRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, this, VisualBorderRectRelativeToSelf(),
aLists.BorderBackground(),
aBuilder, this, rect, aLists.BorderBackground(),
/* aAllowWillPaintBorderOptimization = */ false);
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)

View file

@ -1793,6 +1793,13 @@ void
nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
// Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
// "All css properties of table-column and table-column-group boxes are
// ignored, except when explicitly specified by this specification."
// CSS outlines fall into this category, so we skip them on these boxes.
MOZ_ASSERT(GetType() != nsGkAtoms::tableColGroupFrame && GetType() != nsGkAtoms::tableColFrame);
if (StyleOutline()->mOutlineStyle == NS_STYLE_BORDER_STYLE_NONE) {
return;
}
@ -1839,7 +1846,9 @@ nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
if (aBuilder->IsForEventDelivery() || aForceBackground ||
!StyleBackground()->IsTransparent() || StyleDisplay()->mAppearance) {
return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, this, GetRectRelativeToSelf(), aLists.BorderBackground());
aBuilder, this,
GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
aLists.BorderBackground());
}
return false;
}
@ -1872,7 +1881,9 @@ nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
// If there's a themed background, we should not create a border item.
// It won't be rendered.
if (!bgIsThemed && StyleBorder()->HasBorder()) {
// Don't paint borders for tables here, since they paint them in a different
// order.
if (!bgIsThemed && StyleBorder()->HasBorder() && GetType() != nsGkAtoms::tableFrame) {
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayBorder(aBuilder, this));
}
@ -2626,8 +2637,33 @@ static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
return true;
}
if (aChild->IsFrameOfType(nsIFrame::eTablePart)) {
// Relative positioning and transforms can cause table parts to move, but we
// will still paint the backgrounds for their ancestor parts under them at
// their 'normal' position. That means that we must consider the overflow
// rects at both positions.
// We convert the overflow rect into the nsTableFrame's coordinate
// space, applying the normal position offset at each step. Then we
// compare that against the builder's cached dirty rect in table
// coordinate space.
const nsIFrame* f = aChild;
nsRect normalPositionOverflowRelativeToTable = overflow;
while (f->IsFrameOfType(nsIFrame::eTablePart)) {
normalPositionOverflowRelativeToTable += f->GetNormalPosition();
f = f->GetParent();
}
nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
if (tableBGs &&
tableBGs->GetDirtyRect().Intersects(normalPositionOverflowRelativeToTable)) {
return true;
}
}
return false;
}
}
void
nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
@ -2770,7 +2806,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
// If you change this, also change IsPseudoStackingContextFromStyle()
pseudoStackingContext = true;
}
NS_ASSERTION(!isStackingContext || pseudoStackingContext,
"Stacking contexts must also be pseudo-stacking-contexts");

View file

@ -1992,7 +1992,8 @@ nsMathMLChar::Display(nsDisplayListBuilder* aBuilder,
if (styleContext != parentContext &&
NS_GET_A(backg->mBackgroundColor) > 0) {
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, aForFrame, mRect, aLists.BorderBackground(),
aBuilder, aForFrame, mRect + aBuilder->ToReferenceFrame(aForFrame),
aLists.BorderBackground(),
/* aAllowWillPaintBorderOptimization */ true, styleContext);
}
//else

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body>
<table>
<col style="background:green">
<tr>
<td style="width:20px; height:20px"></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<body>
<table>
<col style="background:green">
<tr>
<td style="position:relative; left:-100px; width:20px; height:20px"></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body style="overflow:hidden">
<table style="position:relative; top:-50px">
<col style="background:green; width: 40px;">
<tr style="position:relative; left:-100px; height: 40px">
<td rowspan=2></td>
</tr>
<tr style="height: 40px"></tr>
</table>
</body>
</html>

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html>
<body style="overflow:hidden">
<table style="position:relative; top:-50px">
<tbody style="background:green">
<tr style="position:relative; left:-100px; height: 40px">
<td rowspan=2 style="width: 40px;"></td>
</tr>
<tr style="height: 40px"></tr>
</tbody>
</table>
</body>
</html>

View file

@ -7,3 +7,6 @@
== table-separate-3.html table-separate-3-ref.html
== table-separate-4.html table-separate-4-ref.html
== 1115999-1.html 1115999-1-ref.html
== 1409114-1.html 1409114-1-ref.html
!= 1409114-2.html about:blank
!= 1409114-3.html about:blank

View file

@ -10,8 +10,6 @@
html, body { margin: 0; padding: 0; border: 0; font-size: 16px; }
body { padding: 15px; }
.o { opacity: 0.5; }
/*
table {
margin: 0px 3px 2px 4px;
@ -55,8 +53,8 @@
</head>
<body>
<div class="color o"></div>
<div class="imagetl o"></div>
<div class="color"></div>
<div class="imagetl"></div>
<div class="imagebr"></div>
</body>

View file

@ -10,8 +10,6 @@
html, body { margin: 0; padding: 0; border: 0; font-size: 16px; }
body { padding: 15px; }
.o { opacity: 0.5; }
/*
table {
margin: 0px 3px 2px 4px;
@ -55,8 +53,8 @@
</head>
<body>
<div class="color o"></div>
<div class="imagetl o"></div>
<div class="color"></div>
<div class="imagetl"></div>
<div class="imagebr"></div>
</body>

View file

@ -10,8 +10,6 @@
html, body { margin: 0; padding: 0; border: 0; font-size: 16px; }
body { padding: 15px; }
.o { opacity: 0.5; }
/*
table {
margin: 0px 3px 2px 4px;
@ -65,8 +63,8 @@
<body>
<div class="color"></div>
<div class="imagetl o"></div>
<div class="imagebr o"></div>
<div class="imagetl"></div>
<div class="imagebr"></div>
<div class="hstripe" style="top: 46px"></div>
<div class="hstripe" style="top: 70px"></div>

View file

@ -10,8 +10,6 @@
html, body { margin: 0; padding: 0; border: 0; font-size: 16px; }
body { padding: 15px; }
.o { opacity: 0.5; }
/*
table {
margin: 0px 3px 2px 4px;
@ -65,8 +63,8 @@
<body>
<div class="color"></div>
<div class="imagetl o"></div>
<div class="imagebr o"></div>
<div class="imagetl"></div>
<div class="imagebr"></div>
<div class="hstripe" style="top: 46px"></div>
<div class="hstripe" style="top: 70px"></div>

View file

@ -60,3 +60,6 @@ fuzzy-if(d2d||skiaContent,1,95000) == border-separate-opacity-table.html border-
== empty-cells-default-2.html empty-cells-default-2-ref.html
fuzzy-if(OSX,1,113) fuzzy-if(winWidget,1,12) fuzzy-if(Android,1,39) fuzzy-if(winWidget&&!layersGPUAccelerated,82,116) fuzzy-if(skiaContent,84,5500) == table-row-opacity-dynamic-1.html table-row-opacity-dynamic-1-ref.html
== table-row-opacity-dynamic-2.html table-row-opacity-dynamic-2-ref.html
== table-col-overlapping.html table-col-overlapping-ref.html
== table-col-span-1.html table-col-span-1-ref.html

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<style>
td {
width: 20px;
height: 20px;
background-color: green;
}
table {
border-collapse:separate;
border-spacing: 0px;
}
</style>
</head>
<body>
<table>
<tr>
<td></td>
<td style="background-color: blue"></td>
<tr>
<td></td>
<td></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,28 @@
<!DOCTYPE html>
<html>
<head>
<style>
td {
width: 20px;
height: 20px;
}
table {
border-collapse:separate;
border-spacing: 0px;
}
</style>
</head>
<body>
<table>
<col style="background: green"></col>
<col style="background: blue"></col>
<tr>
<td></td>
<td rowspan=2></td>
<tr>
<td colspan=2></td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<body>
<table border>
<col style="background:purple"></col>
<col style="background:purple"></col>
<col style="background:blue"></col>
<tr>
<td>x</td>
<td>y</td>
<td>z</td>
</tr>
</table>
</body>
</html>

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<body>
<table border>
<col span=2 style="background:purple"></col>
<col style="background:blue"></col>
<tr>
<td>x</td>
<td>y</td>
<td>z</td>
</tr>
</table>
</body>
</html>

View file

@ -461,14 +461,14 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, this));
}
nsRect bgRect = GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
// display background if we need to.
if (aBuilder->IsForEventDelivery() ||
!StyleBackground()->IsTransparent() ||
StyleDisplay()->mAppearance) {
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder,
this,
GetRectRelativeToSelf(),
aLists.BorderBackground());
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, this, bgRect, aLists.BorderBackground());
}
// display inset box-shadows if we need to.
@ -487,16 +487,49 @@ nsTableCellFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
"TableCellSelection",
nsDisplayItem::TYPE_TABLE_CELL_SELECTION));
}
// This can be null if display list building initiated in the middle
// of the table, which can happen with background-clip:text and
// -moz-element.
nsDisplayTableBackgroundSet* backgrounds =
aBuilder->GetTableBackgroundSet();
if (backgrounds) {
// Compute bgRect relative to reference frame, but using the
// normal (without position:relative offsets) positions for the
// cell, row and row group.
bgRect = GetRectRelativeToSelf() + GetNormalPosition();
nsTableRowFrame* row = GetTableRowFrame();
bgRect += row->GetNormalPosition();
nsTableRowGroupFrame* rowGroup = row->GetTableRowGroupFrame();
bgRect += rowGroup->GetNormalPosition();
bgRect += backgrounds->TableToReferenceFrame();
// Create backgrounds items as needed for the column and column
// group that this cell occupies.
nsTableColFrame* col = backgrounds->GetColForIndex(ColIndex());
nsTableColGroupFrame* colGroup = col->GetTableColGroupFrame();
Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForColGroup;
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, colGroup, bgRect, backgrounds->ColGroupBackgrounds(), false,
nullptr, colGroup->GetRect() + backgrounds->TableToReferenceFrame(),
this, &buildingForColGroup);
Maybe<nsDisplayListBuilder::AutoBuildingDisplayList> buildingForCol;
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, col, bgRect, backgrounds->ColBackgrounds(), false, nullptr,
col->GetRect() + colGroup->GetPosition() +
backgrounds->TableToReferenceFrame(),
this, &buildingForCol);
}
}
// the 'empty-cells' property has no effect on 'outline'
DisplayOutline(aBuilder, aLists);
// Push a null 'current table item' so that descendant tables can't
// accidentally mess with our table
nsAutoPushCurrentTableItem pushTableItem;
pushTableItem.Push(aBuilder, nullptr);
nsIFrame* kid = mFrames.FirstChild();
NS_ASSERTION(kid && !kid->GetNextSibling(), "Table cells should have just one child");
// The child's background will go in our BorderBackground() list.

View file

@ -112,7 +112,13 @@ void
nsTableColFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
nsTableFrame::DisplayGenericTablePart(aBuilder, this, aLists);
// Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
// "All css properties of table-column and table-column-group boxes are
// ignored, except when explicitly specified by this specification."
// CSS outlines and box-shadows fall into this category, so we skip them
// on these boxes.
MOZ_ASSERT_UNREACHABLE("Cols don't paint themselves");
}
int32_t nsTableColFrame::GetSpan()

View file

@ -387,7 +387,13 @@ void
nsTableColGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
nsTableFrame::DisplayGenericTablePart(aBuilder, this, aLists);
// Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
// "All css properties of table-column and table-column-group boxes are
// ignored, except when explicitly specified by this specification."
// CSS outlines and box-shadows fall into this category, so we skip them
// on these boxes.
MOZ_ASSERT_UNREACHABLE("Colgroups don't paint themselves");
}
nsTableColFrame * nsTableColGroupFrame::GetFirstColumn()

View file

@ -1166,113 +1166,7 @@ nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder,
static_cast<nsTableFrame*>(mFrame)->PaintBCBorders(*drawTarget, mVisibleRect - pt);
}
/* static */ void
nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
const nsDisplayListSet& aLists)
{
// This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
// does, except that we allow the children's background and borders to go
// in our BorderBackground list. This doesn't really affect background
// painting --- the children won't actually draw their own backgrounds
// because the nsTableFrame already drew them, unless a child has its own
// stacking context, in which case the child won't use its passed-in
// BorderBackground list anyway. It does affect cell borders though; this
// lets us get cell borders into the nsTableFrame's BorderBackground list.
for (nsIFrame* kid : aFrame->GetChildList(kColGroupList)) {
aFrame->BuildDisplayListForChild(aBuilder, kid, aLists);
}
for (nsIFrame* kid : aFrame->PrincipalChildList()) {
aFrame->BuildDisplayListForChild(aBuilder, kid, aLists);
}
}
static void
PaintRowBackground(nsTableRowFrame* aRow,
nsIFrame* aFrame,
nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
const nsPoint& aOffset = nsPoint())
{
// Compute background rect by iterating over all cell frames.
for (nsTableCellFrame* cell = aRow->GetFirstCell(); cell; cell = cell->GetNextCell()) {
if (!cell->ShouldPaintBackground(aBuilder)) {
continue;
}
auto cellRect = cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + aOffset;
if (!aBuilder->GetDirtyRect().Intersects(cellRect)) {
continue;
}
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect,
aLists.BorderBackground(),
true, nullptr,
aFrame->GetRectRelativeToSelf(),
cell);
}
}
static void
PaintRowGroupBackground(nsTableRowGroupFrame* aRowGroup,
nsIFrame* aFrame,
nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
if (!aBuilder->GetDirtyRect().Intersects(nsRect(row->GetNormalPosition(),
row->GetSize()))) {
continue;
}
PaintRowBackground(row, aFrame, aBuilder, aLists, row->GetNormalPosition());
}
}
static void
PaintRowGroupBackgroundByColIdx(nsTableRowGroupFrame* aRowGroup,
nsIFrame* aFrame,
nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
const nsTArray<uint32_t>& aColIdx,
const nsPoint& aOffset)
{
MOZ_DIAGNOSTIC_ASSERT(!aColIdx.IsEmpty(),
"Must be painting backgrounds for something");
for (nsTableRowFrame* row = aRowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
auto rowPos = row->GetNormalPosition() + aOffset;
if (!aBuilder->GetDirtyRect().Intersects(nsRect(rowPos, row->GetSize()))) {
continue;
}
for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
uint32_t curColIdx = cell->ColIndex();
if (!aColIdx.Contains(curColIdx)) {
if (curColIdx > aColIdx.LastElement()) {
// We can just stop looking at this row.
break;
}
continue;
}
if (!cell->ShouldPaintBackground(aBuilder)) {
continue;
}
auto cellPos = cell->GetNormalPosition() + rowPos;
auto cellRect = nsRect(cellPos, cell->GetSize());
if (!aBuilder->GetDirtyRect().Intersects(cellRect)) {
continue;
}
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, cellRect,
aLists.BorderBackground(),
true, nullptr,
aFrame->GetRectRelativeToSelf(),
cell);
}
}
}
static inline bool FrameHasBorder(nsIFrame* f)
{
static inline bool FrameHasBorder(nsIFrame* f) {
if (!f->StyleVisibility()->IsVisible()) {
return false;
}
@ -1284,8 +1178,7 @@ static inline bool FrameHasBorder(nsIFrame* f)
return false;
}
void nsTableFrame::CalcHasBCBorders()
{
void nsTableFrame::CalcHasBCBorders() {
if (!IsBorderCollapse()) {
SetHasBCBorders(false);
return;
@ -1303,8 +1196,9 @@ void nsTableFrame::CalcHasBCBorders()
return;
}
nsTableColGroupFrame *colGroup = static_cast<nsTableColGroupFrame*>(f);
for (nsTableColFrame* col = colGroup->GetFirstColumn(); col; col = col->GetNextCol()) {
nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(f);
for (nsTableColFrame* col = colGroup->GetFirstColumn(); col;
col = col->GetNextCol()) {
if (FrameHasBorder(col)) {
SetHasBCBorders(true);
return;
@ -1321,13 +1215,15 @@ void nsTableFrame::CalcHasBCBorders()
return;
}
for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row; row = row->GetNextRow()) {
for (nsTableRowFrame* row = rowGroup->GetFirstRow(); row;
row = row->GetNextRow()) {
if (FrameHasBorder(row)) {
SetHasBCBorders(true);
return;
}
for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
for (nsTableCellFrame* cell = row->GetFirstCell(); cell;
cell = cell->GetNextCell()) {
if (FrameHasBorder(cell)) {
SetHasBCBorders(true);
return;
@ -1339,129 +1235,6 @@ void nsTableFrame::CalcHasBCBorders()
SetHasBCBorders(false);
}
/* static */ void
nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
nsFrame* aFrame,
const nsDisplayListSet& aLists,
DisplayGenericTablePartTraversal aTraversal)
{
bool isVisible = aFrame->IsVisibleForPainting(aBuilder);
bool isTable = (aFrame->GetType() == nsGkAtoms::tableFrame);
if (isVisible || !isTable) {
nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
// currentItem may be null, when none of the table parts have a
// background or border
if (currentItem) {
currentItem->UpdateForFrameBackground(aFrame);
}
}
if (isVisible) {
// XXX: should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the outset box-shadows for the table frames
if (aFrame->StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame));
}
}
// Background visibility for rows, rowgroups, columns, colgroups depends on
// the visibility of the _cell_, not of the row/col(group).
// See spec at https://drafts.csswg.org/css-tables-3/#drawing-cell-backgrounds
if (aFrame->GetType() == nsGkAtoms::tableRowGroupFrame) {
nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame);
PaintRowGroupBackground(rowGroup, aFrame, aBuilder, aLists);
} else if (aFrame->GetType() == nsGkAtoms::tableRowFrame) {
nsTableRowFrame* row = static_cast<nsTableRowFrame*>(aFrame);
PaintRowBackground(row, aFrame, aBuilder, aLists);
} else if (aFrame->GetType() == nsGkAtoms::tableColGroupFrame) {
// Compute background rect by iterating all cell frame.
nsTableColGroupFrame* colGroup = static_cast<nsTableColGroupFrame*>(aFrame);
// Collecting column index.
AutoTArray<uint32_t, 1> colIdx;
for (nsTableColFrame* col = colGroup->GetFirstColumn(); col; col = col->GetNextCol()) {
MOZ_ASSERT(colIdx.IsEmpty() ||
static_cast<uint32_t>(col->GetColIndex()) > colIdx.LastElement());
colIdx.AppendElement(col->GetColIndex());
}
if (!colIdx.IsEmpty()) {
// We have some actual cells that live inside this rowgroup.
nsTableFrame* table = colGroup->GetTableFrame();
RowGroupArray rowGroups;
table->OrderRowGroups(rowGroups);
for (nsTableRowGroupFrame* rowGroup : rowGroups) {
auto offset = rowGroup->GetNormalPosition() - colGroup->GetNormalPosition();
if (!aBuilder->GetDirtyRect().Intersects(nsRect(offset, rowGroup->GetSize()))) {
continue;
}
PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, colIdx, offset);
}
}
} else if (aFrame->GetType() == nsGkAtoms::tableColFrame) {
// Compute background rect by iterating all cell frame.
nsTableColFrame* col = static_cast<nsTableColFrame*>(aFrame);
AutoTArray<uint32_t, 1> colIdx;
colIdx.AppendElement(col->GetColIndex());
nsTableFrame* table = col->GetTableFrame();
RowGroupArray rowGroups;
table->OrderRowGroups(rowGroups);
for (nsTableRowGroupFrame* rowGroup : rowGroups) {
auto offset = rowGroup->GetNormalPosition() -
col->GetNormalPosition() -
col->GetTableColGroupFrame()->GetNormalPosition();
if (!aBuilder->GetDirtyRect().Intersects(nsRect(offset, rowGroup->GetSize()))) {
continue;
}
PaintRowGroupBackgroundByColIdx(rowGroup, aFrame, aBuilder, aLists, colIdx, offset);
}
} else if (isVisible) {
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame,
aFrame->GetRectRelativeToSelf(),
aLists.BorderBackground());
}
if (isVisible) {
// XXX: should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the inset box-shadows for the table frames
if (aFrame->StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame));
}
}
aTraversal(aBuilder, aFrame, aLists);
if (isVisible) {
if (isTable) {
nsTableFrame* table = static_cast<nsTableFrame*>(aFrame);
// In the collapsed border model, overlay all collapsed borders.
if (table->IsBorderCollapse()) {
if (table->HasBCBorders()) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayTableBorderCollapse(aBuilder, table));
}
} else {
const nsStyleBorder* borderStyle = aFrame->StyleBorder();
if (borderStyle->HasBorder()) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBorder(aBuilder, table));
}
}
}
}
aFrame->DisplayOutline(aBuilder, aLists);
}
// table paint code is concerned primarily with borders and bg color
// SEC: TODO: adjust the rect for captions
void
@ -1470,7 +1243,50 @@ nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
{
DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
DisplayGenericTablePart(aBuilder, this, aLists);
DisplayBorderBackgroundOutline(aBuilder, aLists);
nsDisplayTableBackgroundSet tableBGs(aBuilder, this);
nsDisplayListCollection lists(aBuilder);
// This is similar to what
// nsContainerFrame::BuildDisplayListForNonBlockChildren does, except that we
// allow the children's background and borders to go in our BorderBackground
// list. This doesn't really affect background painting --- the children won't
// actually draw their own backgrounds because the nsTableFrame already drew
// them, unless a child has its own stacking context, in which case the child
// won't use its passed-in BorderBackground list anyway. It does affect cell
// borders though; this lets us get cell borders into the nsTableFrame's
// BorderBackground list.
for (nsIFrame* colGroup : FirstContinuation()->GetChildList(kColGroupList)) {
for (nsIFrame* col : colGroup->PrincipalChildList()) {
tableBGs.AddColumn((nsTableColFrame*)col);
}
}
for (nsIFrame* kid : PrincipalChildList()) {
BuildDisplayListForChild(aBuilder, kid, lists);
}
tableBGs.MoveTo(aLists);
lists.MoveTo(aLists);
if (IsVisibleForPainting(aBuilder)) {
// In the collapsed border model, overlay all collapsed borders.
if (IsBorderCollapse()) {
if (HasBCBorders()) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayTableBorderCollapse
(aBuilder, this));
}
} else {
const nsStyleBorder* borderStyle = StyleBorder();
if (borderStyle->HasBorder()) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBorder
(aBuilder, this));
}
}
}
}
nsMargin

View file

@ -72,36 +72,59 @@ private:
bool mDrawsBackground;
};
class nsAutoPushCurrentTableItem
{
public:
nsAutoPushCurrentTableItem() : mBuilder(nullptr) {}
class nsDisplayTableBackgroundSet {
public:
nsDisplayList* ColGroupBackgrounds() { return &mColGroupBackgrounds; }
void Push(nsDisplayListBuilder* aBuilder, nsDisplayTableItem* aPushItem)
{
mBuilder = aBuilder;
mOldCurrentItem = aBuilder->GetCurrentTableItem();
aBuilder->SetCurrentTableItem(aPushItem);
#ifdef DEBUG
mPushedItem = aPushItem;
#endif
}
~nsAutoPushCurrentTableItem() {
if (!mBuilder)
return;
#ifdef DEBUG
NS_ASSERTION(mBuilder->GetCurrentTableItem() == mPushedItem,
"Someone messed with the current table item behind our back!");
#endif
mBuilder->SetCurrentTableItem(mOldCurrentItem);
nsDisplayList* ColBackgrounds() { return &mColBackgrounds; }
nsDisplayTableBackgroundSet(nsDisplayListBuilder* aBuilder, nsIFrame* aTable)
: mBuilder(aBuilder) {
mPrevTableBackgroundSet = mBuilder->SetTableBackgroundSet(this);
mozilla::DebugOnly<const nsIFrame*> reference =
mBuilder->FindReferenceFrameFor(aTable, &mToReferenceFrame);
MOZ_ASSERT(nsLayoutUtils::IsAncestorFrameCrossDoc(reference, aTable));
mDirtyRect = mBuilder->GetDirtyRect();
}
private:
~nsDisplayTableBackgroundSet() {
mozilla::DebugOnly<nsDisplayTableBackgroundSet*> result =
mBuilder->SetTableBackgroundSet(mPrevTableBackgroundSet);
MOZ_ASSERT(result == this);
}
/**
* Move all display items in our lists to top of the corresponding lists in
* the destination.
*/
void MoveTo(const nsDisplayListSet& aDestination) {
aDestination.BorderBackground()->AppendToTop(ColGroupBackgrounds());
aDestination.BorderBackground()->AppendToTop(ColBackgrounds());
}
void AddColumn(nsTableColFrame* aFrame) { mColumns.AppendElement(aFrame); }
nsTableColFrame* GetColForIndex(int32_t aIndex) { return mColumns[aIndex]; }
const nsPoint& TableToReferenceFrame() { return mToReferenceFrame; }
const nsRect& GetDirtyRect() { return mDirtyRect; }
private:
// This class is only used on stack, so we don't have to worry about leaking
// it. Don't let us be heap-allocated!
void* operator new(size_t sz) CPP_THROW_NEW;
protected:
nsDisplayListBuilder* mBuilder;
nsDisplayTableItem* mOldCurrentItem;
#ifdef DEBUG
nsDisplayTableItem* mPushedItem;
#endif
nsDisplayTableBackgroundSet* mPrevTableBackgroundSet;
nsDisplayList mColGroupBackgrounds;
nsDisplayList mColBackgrounds;
nsTArray<nsTableColFrame*> mColumns;
nsPoint mToReferenceFrame;
nsRect mDirtyRect;
};
/* ============================================================================ */
@ -229,28 +252,6 @@ public:
nsIFrame* aSourceFrame,
bool* aDidPassThrough);
typedef void (* DisplayGenericTablePartTraversal)
(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
const nsDisplayListSet& aLists);
static void GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
const nsDisplayListSet& aLists);
/**
* Helper method to handle display common to table frames, rowgroup frames
* and row frames. It creates a background display item for handling events
* if necessary, an outline display item if necessary, and displays
* all the the frame's children.
* @param aDisplayItem the display item created for this part, or null
* if this part's border/background painting is delegated to an ancestor
* @param aTraversal a function that gets called to traverse the table
* part's child frames and add their display list items to a
* display list set.
*/
static void DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
nsFrame* aFrame,
const nsDisplayListSet& aLists,
DisplayGenericTablePartTraversal aTraversal = GenericTraversal);
// Return the closest sibling of aPriorChildFrame (including aPriroChildFrame)
// of type aChildType.
static nsIFrame* GetFrameAtOrBefore(nsIFrame* aParentFrame,

View file

@ -549,11 +549,66 @@ nsTableRowFrame::CalcBSize(const ReflowInput& aReflowInput)
return GetInitialBSize();
}
void nsTableRowFrame::PaintCellBackgroundsForFrame(
nsIFrame* aFrame, nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists, const nsPoint& aOffset) {
// Compute background rect by iterating all cell frame.
const nsPoint toReferenceFrame = aBuilder->ToReferenceFrame(aFrame);
for (nsTableCellFrame* cell = GetFirstCell(); cell;
cell = cell->GetNextCell()) {
if (!cell->ShouldPaintBackground(aBuilder)) {
continue;
}
auto cellRect =
cell->GetRectRelativeToSelf() + cell->GetNormalPosition() + aOffset;
if (!aBuilder->GetDirtyRect().Intersects(cellRect)) {
continue;
}
cellRect += toReferenceFrame;
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, aFrame, cellRect, aLists.BorderBackground(), true, nullptr,
aFrame->GetRectRelativeToSelf() + toReferenceFrame, cell);
}
}
void
nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
nsTableFrame::DisplayGenericTablePart(aBuilder, this, aLists);
if (IsVisibleForPainting(aBuilder)) {
// XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the outset box-shadows for the table frames
if (StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowOuter
(aBuilder, this));
}
}
PaintCellBackgroundsForFrame(this, aBuilder, aLists);
if (IsVisibleForPainting(aBuilder)) {
// XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the inset box-shadows for the table frames
if (StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowInner
(aBuilder, this));
}
}
DisplayOutline(aBuilder, aLists);
for (nsIFrame* kid : PrincipalChildList()) {
BuildDisplayListForChild(aBuilder, kid, aLists);
}
}
nsIFrame::LogicalSides

View file

@ -81,6 +81,11 @@ public:
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists) override;
void PaintCellBackgroundsForFrame(nsIFrame* aFrame,
nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists,
const nsPoint& aOffset = nsPoint());
// Implemented in nsTableCellFrame.h, because it needs to know about the
// nsTableCellFrame class, but we can't include nsTableCellFrame.h here.
inline nsTableCellFrame* GetFirstCell() const;

View file

@ -207,8 +207,43 @@ void
nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsDisplayListSet& aLists)
{
nsTableFrame::DisplayGenericTablePart(aBuilder, this,
aLists, DisplayRows);
if (IsVisibleForPainting(aBuilder)) {
// XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the outset box-shadows for the table frames
if (StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowOuter
(aBuilder, this));
}
}
for (nsTableRowFrame* row = GetFirstRow(); row; row = row->GetNextRow()) {
if (!aBuilder->GetDirtyRect().Intersects(row->GetVisualOverflowRect() + row->GetNormalPosition())) {
continue;
}
row->PaintCellBackgroundsForFrame(this, aBuilder, aLists,
row->GetNormalPosition());
}
if (IsVisibleForPainting(aBuilder)) {
// XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
// just because we're visible? Or should it depend on the cell visibility
// when we're not the whole table?
// Paint the inset box-shadows for the table frames
if (StyleEffects()->mBoxShadow) {
aLists.BorderBackground()->AppendNewToTop(
new (aBuilder) nsDisplayBoxShadowInner
(aBuilder, this));
}
}
DisplayOutline(aBuilder, aLists);
DisplayRows(aBuilder, this, aLists);
}
nsIFrame::LogicalSides

View file

@ -149,7 +149,8 @@ nsGroupBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
// Paint our background and border
if (IsVisibleForPainting(aBuilder)) {
nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
aBuilder, this, GetBackgroundRectRelativeToSelf(),
aBuilder, this,
GetBackgroundRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
aLists.BorderBackground());
aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
nsDisplayXULGroupBorder(aBuilder, this));