Bug 1461313 - Handle invalid clip-path URIs with WebRender. r=mstange

In the case of an invalid clip-path, the browser is supposed to discard the
mask entirely. In the non-webrender codepath this would happen
implicitly because the computed MaskUsage would have no flags set, and
so no actions would be taken on the gfxContext which contained the
display items rasterized so far. In the WebRender codepath, though, we
invoke the code on a A8 drawtarget that's zero-filled, so if PaintMask
fails to rasterize anything into it, it gets treated as a "mask everything
out" mask. Instead, this patch makes it so that we detect the scenario
where the computed MaskUsage is a no-op, and ensure that we don't apply
the mask in that case.

An alternative approach considered was to initialize the A8 drawtarget to
white instead of black but in cases where there is an actual mask, the
rest of the code assumes it is zero-filled and so that doesn't work.

MozReview-Commit-ID: Hw7nCiUXVJl

--HG--
extra : rebase_source : 241d550fa0ed1b3bd088c73d9565b166acbcece8
This commit is contained in:
Kartikaya Gupta 2018-07-05 08:05:34 -04:00
parent 05d14001f5
commit 78dba184db
9 changed files with 44 additions and 11 deletions

View File

@ -1543,8 +1543,7 @@ PaintItemByDrawTarget(nsDisplayItem* aItem,
switch (aItem->GetType()) {
case DisplayItemType::TYPE_MASK:
context->SetMatrix(context->CurrentMatrix().PreScale(aScale.width, aScale.height).PreTranslate(-aOffset.x, -aOffset.y));
static_cast<nsDisplayMask*>(aItem)->PaintMask(aDisplayListBuilder, context);
isInvalidated = true;
static_cast<nsDisplayMask*>(aItem)->PaintMask(aDisplayListBuilder, context, &isInvalidated);
break;
case DisplayItemType::TYPE_SVG_WRAPPER:
{

View File

@ -0,0 +1,4 @@
<!doctype html>
<body>
<div style="background-color: green; width: 100px; height: 100px;"></div>
</body>

View File

@ -0,0 +1,9 @@
<!doctype html>
<style>
body {
clip-path: url(non-existent-resource);
}
</style>
<body>
<div style="background-color: green; width: 100px; height: 100px;"></div>
</body>

View File

@ -14,3 +14,4 @@ fuzzy(100,30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted
== 1444904.html 1444904-ref.html
== 1451168.html 1451168-ref.html
fuzzy(5-32,21908-26354) fuzzy-if(webrender,0-1,0-3) == 1463802.html 1463802-ref.html
== 1461313.html 1461313-ref.html

View File

@ -9200,7 +9200,8 @@ nsDisplayMask::BuildLayer(nsDisplayListBuilder* aBuilder,
bool
nsDisplayMask::PaintMask(nsDisplayListBuilder* aBuilder,
gfxContext* aMaskContext)
gfxContext* aMaskContext,
bool* aMaskPainted)
{
MOZ_ASSERT(aMaskContext->GetDrawTarget()->GetFormat() == SurfaceFormat::A8);
@ -9214,7 +9215,10 @@ nsDisplayMask::PaintMask(nsDisplayListBuilder* aBuilder,
nullptr,
mHandleOpacity, imgParmas);
ComputeMaskGeometry(params);
nsSVGIntegrationUtils::PaintMask(params);
bool painted = nsSVGIntegrationUtils::PaintMask(params);
if (aMaskPainted) {
*aMaskPainted = painted;
}
nsDisplayMaskGeometry::UpdateDrawResult(this, imgParmas.result);

View File

@ -6093,7 +6093,9 @@ public:
* Paint mask onto aMaskContext in mFrame's coordinate space and
* return whether the mask layer was painted successfully.
*/
bool PaintMask(nsDisplayListBuilder* aBuilder, gfxContext* aMaskContext);
bool PaintMask(nsDisplayListBuilder* aBuilder,
gfxContext* aMaskContext,
bool* aMaskPainted = nullptr);
const nsTArray<nsRect>& GetDestRects()
{

View File

@ -593,7 +593,7 @@ CreateAndPaintMaskSurface(const PaintFramesParams& aParams,
// For a SVG doc:
// SVG 1.1 say that if we fail to resolve a mask, we should draw the
// object unmasked.
// Left patinResult.maskSurface empty, the caller should paint all
// Left paintResult.maskSurface empty, the caller should paint all
// masked content as if this mask is an opaque white one(no mask).
paintResult.transparentBlackMask =
!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
@ -760,16 +760,19 @@ private:
gfxContext* mContext;
};
void
bool
nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
{
nsSVGUtils::MaskUsage maskUsage;
nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
maskUsage);
if (!maskUsage.shouldDoSomething()) {
return false;
}
nsIFrame* frame = aParams.frame;
if (!ValidateSVGFrame(frame)) {
return;
return false;
}
gfxContext& ctx = aParams.ctx;
@ -819,7 +822,7 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
ctx.SetColor(Color(1.0, 1.0, 1.0, 1.0));
ctx.Fill();
return;
return true;
}
}
@ -852,6 +855,8 @@ nsSVGIntegrationUtils::PaintMask(const PaintFramesParams& aParams)
&clipMaskTransform, maskSurface,
ctx.CurrentMatrix());
}
return true;
}
void

View File

@ -168,9 +168,10 @@ public:
/**
* Paint mask of non-SVG frame onto a given context, aParams.ctx.
* aParams.ctx must contain an A8 surface.
* aParams.ctx must contain an A8 surface. Returns false if the mask
* didn't get painted and should be ignored at the call site.
*/
static void
static bool
PaintMask(const PaintFramesParams& aParams);
/**

View File

@ -613,6 +613,14 @@ public:
: shouldGenerateMaskLayer(false), shouldGenerateClipMaskLayer(false),
shouldApplyClipPath(false), shouldApplyBasicShape(false), opacity(0.0)
{ }
bool shouldDoSomething() {
return shouldGenerateMaskLayer
|| shouldGenerateClipMaskLayer
|| shouldApplyClipPath
|| shouldApplyBasicShape
|| opacity != 1.0;
}
};
static void DetermineMaskUsage(nsIFrame* aFrame, bool aHandleOpacity,