Bug 504622 part 1. Rewrite fieldset border drawing to just clip to the area outside the legend instead of doing it in pieces with different clip rects. r=mattwoodrow,dbaron

This change will allow the border drawing code to deal with the following
changes, which will make us no longer force the fieldset to be wider than the
legend.  Without this patch, allowing the fieldset to be narrower than the
legend causes the vertical inline-start-side and inline-end-side borders of the
fieldset to paint under the legend, because the current code only modifies the
painting of the block-start-side border (the one the legend is positioned on).

This does change behavior in one situation, which the new tests test.  For
relatively positioned legends, we used to use the original vertical location but
the positioned horizontal location of the legend to decide which parts of the
border to not paint.  In the new setup, we use the original location for both.
I did check that this new behavior matches Chrome and Safari.  Edge seems to
have our old behavior.
This commit is contained in:
Boris Zbarsky 2016-11-29 15:52:30 -05:00
Родитель cd7a370a02
Коммит 5b089631f6
13 изменённых файлов: 164 добавлений и 49 удалений

Просмотреть файл

@ -110,6 +110,7 @@ public:
virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
const nsDisplayItemGeometry* aGeometry,
nsRegion *aInvalidRegion) override;
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) override;
NS_DISPLAY_DECL_NAME("FieldSetBorderBackground", TYPE_FIELDSET_BORDER_BACKGROUND)
};
@ -155,6 +156,19 @@ nsDisplayFieldSetBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilde
nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
}
nsRect
nsDisplayFieldSetBorderBackground::GetBounds(nsDisplayListBuilder* aBuilder,
bool* aSnap)
{
// Just go ahead and claim our frame's overflow rect as the bounds, because we
// may have border-image-outset or other features that cause borders to extend
// outside the border rect. We could try to duplicate all the complexity
// nsDisplayBorder has here, but keeping things in sync would be a pain, and
// this code is not typically performance-sensitive.
*aSnap = false;
return Frame()->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
}
void
nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
@ -217,14 +231,12 @@ nsFieldSetFrame::PaintBorder(
nsPoint aPt,
const nsRect& aDirtyRect)
{
// if the border is smaller than the legend. Move the border down
// to be centered on the legend.
// If the border is smaller than the legend, move the border down
// to be centered on the legend. We call VisualBorderRectRelativeToSelf() to
// compute the border positioning.
// FIXME: This means border-radius clamping is incorrect; we should
// override nsIFrame::GetBorderRadii.
WritingMode wm = GetWritingMode();
nsRect rect = VisualBorderRectRelativeToSelf();
nscoord off = wm.IsVertical() ? rect.x : rect.y;
rect += aPt;
nsRect rect = VisualBorderRectRelativeToSelf() + aPt;
nsPresContext* presContext = PresContext();
PaintBorderFlags borderFlags = aBuilder->ShouldSyncDecodeImages()
@ -237,55 +249,39 @@ nsFieldSetFrame::PaintBorder(
this, rect);
if (nsIFrame* legend = GetLegend()) {
Side legendSide = wm.PhysicalSide(eLogicalSideBStart);
nscoord legendBorderWidth =
StyleBorder()->GetComputedBorderWidth(legendSide);
// We want to avoid drawing our border under the legend, so clip out the
// legend while drawing our border. We don't want to use mLegendRect here,
// because we do want to draw our border under the legend's inline-start and
// -end margins. And we use GetNormalRect(), not GetRect(), because we do
// not want relative positioning applied to the legend to change how our
// border looks.
nsRect legendRect = legend->GetNormalRect() + aPt;
// Use the rect of the legend frame, not mLegendRect, so we draw our
// border under the legend's inline-start and -end margins.
LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size());
// Compute clipRect using logical coordinates, so that the legend space
// will be clipped out of the appropriate physical side depending on mode.
LogicalRect clipRect = LogicalRect(wm, rect, rect.Size());
DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
gfxContext* gfx = aRenderingContext.ThebesContext();
// We set up a clip path which has our rect clockwise and the legend rect
// counterclockwise, with FILL_WINDING as the fill rule. That will allow us
// to paint within our rect but outside the legend rect. For "our rect" we
// use our visual overflow rect (relative to ourselves, so it's not affected
// by transforms), because we can have borders sticking outside our border
// box (e.g. due to border-image-outset).
RefPtr<PathBuilder> pathBuilder =
drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
AppendRectToPath(pathBuilder,
NSRectToSnappedRect(GetVisualOverflowRectRelativeToSelf() + aPt,
appUnitsPerDevPixel,
*drawTarget),
true);
AppendRectToPath(pathBuilder,
NSRectToSnappedRect(legendRect, appUnitsPerDevPixel,
*drawTarget),
false);
RefPtr<Path> clipPath = pathBuilder->Finish();
// draw inline-start portion of the block-start side of the border
clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfxContext* gfx = aRenderingContext.ThebesContext();
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);
gfx->Restore();
// draw inline-end portion of the block-start side of the border
clipRect = LogicalRect(wm, rect, rect.Size());
clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm);
clipRect.IStart(wm) = legendRect.IEnd(wm);
clipRect.BSize(wm) = legendBorderWidth;
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);
gfx->Restore();
// draw remainder of the border (omitting the block-start side)
clipRect = LogicalRect(wm, rect, rect.Size());
clipRect.BStart(wm) += legendBorderWidth;
clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth);
gfx->Save();
gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()),
appUnitsPerDevPixel, *drawTarget));
gfx->Clip(clipPath);
result &=
nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
aDirtyRect, rect, mStyleContext, borderFlags);

Двоичные данные
layout/reftests/forms/fieldset/blue-1x1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 69 B

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<style>
div {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
}
</style>
<div></div>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<style>
fieldset {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
}
</style>
<fieldset></fieldset>

Просмотреть файл

@ -0,0 +1,13 @@
<!DOCTYPE html>
<style>
fieldset {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
}
</style>
<fieldset><legend></legend></fieldset>

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<style>
div {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
transform: scale(0.5);
}
</style>
<div></div>

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<style>
fieldset {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
transform: scale(0.5);
}
</style>
<fieldset></fieldset>

Просмотреть файл

@ -0,0 +1,14 @@
<!DOCTYPE html>
<style>
fieldset {
padding: 0;
margin: 10px;
height: 100px;
width: 100px;
border: 10px solid;
border-image-source: url(blue-1x1.png);
border-image-outset: 10px;
transform: scale(0.5);
}
</style>
<fieldset><legend></legend></fieldset>

Просмотреть файл

@ -8,9 +8,15 @@ fuzzy-if(skiaContent,2,13) == dynamic-legend-scroll-1.html dynamic-legend-scroll
fuzzy-if(winWidget&&!layersGPUAccelerated,121,276) == positioned-container-1.html positioned-container-1-ref.html
== relpos-legend-1.html relpos-legend-1-ref.html
== relpos-legend-2.html relpos-legend-2-ref.html
== relpos-legend-3.html relpos-legend-3-ref.html
== relpos-legend-4.html relpos-legend-4-ref.html
== sticky-legend-1.html sticky-legend-1-ref.html
fuzzy-if(skiaContent,1,40768) == abs-pos-child-sizing.html abs-pos-child-sizing-ref.html
== overflow-hidden.html overflow-hidden-ref.html
== legend-rtl.html legend-rtl-ref.html
== fieldset-grid-001.html fieldset-grid-001-ref.html
== fieldset-flexbox-001.html fieldset-flexbox-001-ref.html
== fieldset-border-image-1a.html fieldset-border-image-1-ref.html
== fieldset-border-image-1b.html fieldset-border-image-1-ref.html
== fieldset-border-image-2a.html fieldset-border-image-2-ref.html
== fieldset-border-image-2b.html fieldset-border-image-2-ref.html

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<fieldset>
<legend><div style="position:relative; left:20px">Legend</div></legend>
</fieldset>
</body>
</html>

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<fieldset>
<legend style="position:relative; left:20px">Legend</legend>
</fieldset>
</body>
</html>

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<fieldset>
<legend><span style="position:relative; left:20px">Legend</span></legend>
</fieldset>
</body>
</html>

Просмотреть файл

@ -0,0 +1,8 @@
<!DOCTYPE HTML>
<html>
<body>
<fieldset>
<legend style="display:inline; position:relative; left:20px">Legend</legend>
</fieldset>
</body>
</html>