--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -421,32 +421,32 @@ GenerateMaskSurface(const nsSVGIntegrati
gfxMatrix cssPxToDevPxMatrix =
nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aParams.frame);
gfxContext& ctx = aParams.ctx;
// There is only one SVG mask.
if (((aMaskFrames.Length() == 1) && aMaskFrames[0])) {
- aOutMaskSurface =
+ RefPtr<SourceSurface> maskSurface =
aMaskFrames[0]->GetMaskForMaskedFrame(&ctx, aParams.frame,
cssPxToDevPxMatrix, aOpacity,
&aOutMaskTransform,
svgReset->mMask.mLayers[0].mMaskMode);
- return;
+ return maskSurface.forget();
}
ctx.Save();
ctx.SetMatrix(gfxMatrix());
gfxRect clipExtents = ctx.GetClipExtents();
IntRect maskSurfaceRect = RoundedOut(ToRect(clipExtents));
ctx.Restore();
if (maskSurfaceRect.IsEmpty()) {
- return;
+ return nullptr;
}
// Mask composition result on CoreGraphic::A8 surface is not correct
// when mask-mode is not add(source over). Switch to skia when CG backend
// detected.
RefPtr<DrawTarget> maskDT =
(ctx.GetDrawTarget()->GetBackendType() == BackendType::COREGRAPHICS ||
@@ -507,20 +507,53 @@ GenerateMaskSurface(const nsSVGIntegrati
// FIXME We should use the return value, see bug 1258510.
Unused << nsCSSRendering::PaintBackgroundWithSC(params, aSC,
*aParams.frame->StyleBorder());
}
}
aOutMaskTransform = ToMatrix(maskSurfaceMatrix);
if (!aOutMaskTransform.Invert()) {
- return;
+ return nullptr;
}
- aOutMaskSurface = maskDT->Snapshot();
+ RefPtr<SourceSurface> maskSurface = maskDT->Snapshot();
+ return maskSurface.forget();
+}
+
+static bool
+ShouldCreateMaskSurface(const nsIFrame* aFrame,
+ const nsStyleSVGReset* aSVGReset,
+ const nsTArray<nsSVGMaskFrame *>& maskFrames,
+ bool& aHasUnresovlableMaskLayer)
+{
+ // For a HTML doc:
+ // According to css-masking spec, always create a mask surface when we
+ // have any item in maskFrame even if all of those items are
+ // non-resolvable <mask-sources> or <images>, we still need to create a
+ // transparent black mask layer under this condition.
+ // For a SVG doc:
+ // SVG 1.1 say that if we fail to resolve a mask, we should draw the
+ // object unmasked.
+ nsIDocument* currentDoc = aFrame->PresContext()->Document();
+ bool shouldCreateMaskSurface = currentDoc->IsSVGDocument()
+ ? maskFrames.Length() == 1 && maskFrames[0]
+ : maskFrames.Length() > 0;
+
+ aHasUnresovlableMaskLayer = false;
+ for (int i = maskFrames.Length() - 1; i >= 0 ; i--) {
+ nsSVGMaskFrame *maskFrame = maskFrames[i];
+
+ if (!maskFrame && !aSVGReset->mMask.mLayers[i].mImage.IsCompleted()) {
+ aHasUnresovlableMaskLayer = true;
+ break;
+ }
+ }
+
+ return shouldCreateMaskSurface;
}
void
nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams)
{
#ifdef DEBUG
NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
(NS_SVGDisplayListPaintingEnabled() &&
@@ -612,60 +645,68 @@ nsSVGIntegrationUtils::PaintFramesWithEf
nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
frame->PresContext()->AppUnitsPerDevPixel());
context.SetMatrix(context.CurrentMatrix().Translate(devPixelOffsetToUserSpace));
gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(frame);
const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
nsTArray<nsSVGMaskFrame *> maskFrames = effectProperties.GetMaskFrames();
- // For a HTML doc:
- // According to css-masking spec, always create a mask surface when we
- // have any item in maskFrame even if all of those items are
- // non-resolvable <mask-sources> or <images>, we still need to create a
- // transparent black mask layer under this condition.
- // For a SVG doc:
- // SVG 1.1 say that if we fail to resolve a mask, we should draw the
- // object unmasked.
- nsIDocument* currentDoc = frame->PresContext()->Document();
- bool shouldGenerateMaskLayer = currentDoc->IsSVGDocument()
- ? maskFrames.Length() == 1 && maskFrames[0]
- : maskFrames.Length() > 0;
+ bool shouldCreateMaskSurface = false, hasUnresovlableMaskLayer = false;
+ shouldCreateMaskSurface = ShouldCreateMaskSurface(frame, svgReset,
+ maskFrames,
+ hasUnresovlableMaskLayer);
+
+ if (shouldCreateMaskSurface && hasUnresovlableMaskLayer) {
+ // We have at least one mask layer not resolvable. Clear clipped
+ // region as if we push a transparent black mask into context.
+ //
+ // While getting an unresolvable mask layer, you do not need to care
+ // about any other SVG effects, since a transparent mask basically clean
+ // out everything.
+ gfxContextAutoSaveRestore saver(&context);
+
+ context.SetColor(Color(0.f, 0.f, 0.f, 0.f));
+ context.Paint();
+ return;
+ }
// These are used if we require a temporary surface for a custom blend mode.
RefPtr<gfxContext> target = &aParams.ctx;
IntPoint targetOffset;
bool complexEffects = false;
/* Check if we need to do additional operations on this child's
* rendering, which necessitates rendering into another surface. */
if (opacity != 1.0f || (clipPathFrame && !isTrivialClip)
|| frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL
- || shouldGenerateMaskLayer) {
+ || shouldCreateMaskSurface) {
complexEffects = true;
context.Save();
nsRect clipRect =
frame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
context.Clip(NSRectToSnappedRect(clipRect,
frame->PresContext()->AppUnitsPerDevPixel(),
*drawTarget));
Matrix maskTransform;
RefPtr<SourceSurface> maskSurface;
- if (shouldGenerateMaskLayer) {
- GenerateMaskSurface(aParams, opacity, firstFrame->StyleContext(),
- maskFrames, devPixelOffsetToUserSpace,
- maskTransform, maskSurface);
- }
-
- if (shouldGenerateMaskLayer && !maskSurface) {
- // Entire surface is clipped out.
- context.Restore();
- return;
+ if (shouldCreateMaskSurface) {
+ MOZ_ASSERT(!hasUnresovlableMaskLayer);
+ maskSurface = GenerateMaskSurface(aParams, opacity,
+ firstFrame->StyleContext(),
+ maskFrames,
+ devPixelOffsetToUserSpace,
+ maskTransform);
+ if (!maskSurface) {
+ // Entire surface is clipped out.
+ context.Restore();
+ return;
+ }
}
if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
// Create a temporary context to draw to so we can blend it back with
// another operator.
gfxRect clipRect;
{
gfxContextMatrixAutoSaveRestore matRestore(&context);
@@ -693,17 +734,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
&clippedMaskTransform, maskSurface, maskTransform);
if (clipMaskSurface) {
maskSurface = clipMaskSurface;
maskTransform = clippedMaskTransform;
}
}
- if (opacity != 1.0f || shouldGenerateMaskLayer ||
+ if (opacity != 1.0f || shouldCreateMaskSurface ||
(clipPathFrame && !isTrivialClip)) {
target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity, maskSurface, maskTransform);
}
}
/* If this frame has only a trivial clipPath, set up cairo's clipping now so
* we can just do normal painting and get it clipped appropriately.
*/
@@ -739,17 +780,17 @@ nsSVGIntegrationUtils::PaintFramesWithEf
context.Restore();
}
/* No more effects, we're done. */
if (!complexEffects) {
return;
}
- if (opacity != 1.0f || shouldGenerateMaskLayer ||
+ if (opacity != 1.0f || shouldCreateMaskSurface ||
(clipPathFrame && !isTrivialClip)) {
target->PopGroupAndBlend();
}
if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
target = nullptr;
RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();