Bug 1322286 - Part 1. Check maskFrame pointer value before dereference. draft
authorcku <cku@mozilla.com>
Tue, 06 Dec 2016 17:09:11 -1000
changeset 450592 7d6aed808a90eeb8b4b97aa2246cc215261bb924
parent 450591 34a1ab064cb5b868fa75cb74d052e978eb34d6c1
child 450593 397076e911ccd9bd78400a3baf735a5b2481bdcc
push id38911
push userbmo:cku@mozilla.com
push dateSat, 17 Dec 2016 03:26:49 +0000
bugs1322286
milestone53.0a1
Bug 1322286 - Part 1. Check maskFrame pointer value before dereference. MozReview-Commit-ID: oXVxT8h1vz
layout/svg/nsSVGUtils.cpp
--- a/layout/svg/nsSVGUtils.cpp
+++ b/layout/svg/nsSVGUtils.cpp
@@ -758,34 +758,42 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
 
   DrawResult result = DrawResult::SUCCESS;
 
   /* Check if we need to do additional operations on this child's
    * rendering, which necessitates rendering into another surface. */
   bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
                              maskUsage.shouldGenerateClipMaskLayer ||
                              maskUsage.shouldGenerateMaskLayer);
+  bool shouldPushMask = false;
 
   if (shouldGenerateMask) {
     Matrix maskTransform;
     RefPtr<SourceSurface> maskSurface;
 
-    if (maskUsage.shouldGenerateMaskLayer) {
+    // maskFrame can be nullptr even if maskUsage.shouldGenerateMaskLayer is
+    // true. That happens when a user gives an unresolvable mask-id, such as
+    //   mask:url()
+    //   mask:url(#id-which-does-not-exist)
+    // Since we only uses nsSVGUtils with SVG elements, not like mask on an
+    // HTML element, we should treat an unresolvable mask as no-mask here.
+    if (maskUsage.shouldGenerateMaskLayer && maskFrame) {
       uint8_t maskMode =
         aFrame->StyleSVGReset()->mMask.mLayers[0].mMaskMode;
       nsSVGMaskFrame::MaskParams params(&aContext, aFrame, aTransform,
                                         maskUsage.opacity, &maskTransform,
                                         maskMode);
       Tie(result, maskSurface) = maskFrame->GetMaskForMaskedFrame(params);
 
       if (!maskSurface) {
         // Either entire surface is clipped out, or gfx buffer allocation
         // failure in nsSVGMaskFrame::GetMaskForMaskedFrame.
         return result;
       }
+      shouldPushMask = true;
     }
 
     if (maskUsage.shouldGenerateClipMaskLayer) {
       Matrix clippedMaskTransform;
       DrawResult clipMaskResult;
       RefPtr<SourceSurface> clipMaskSurface;
       Tie(clipMaskResult, clipMaskSurface) =
         clipPathFrame->GetClipMask(aContext, aFrame, aTransform,
@@ -795,23 +803,31 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
       if (clipMaskSurface) {
         maskSurface = clipMaskSurface;
         maskTransform = clippedMaskTransform;
       } else {
         // Either entire surface is clipped out, or gfx buffer allocation
         // failure in nsSVGClipPathFrame::GetClipMask.
         return result;
       }
+      shouldPushMask = true;
+    }
+
+    if (!maskUsage.shouldGenerateClipMaskLayer &&
+        !maskUsage.shouldGenerateMaskLayer) {
+      shouldPushMask = true;
     }
 
     // SVG mask multiply opacity into maskSurface already, so we do not bother
     // to apply opacity again.
-    float opacity = maskFrame ? 1.0 : maskUsage.opacity;
-    target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity,
-                                  maskSurface, maskTransform);
+    if (shouldPushMask) {
+      target->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
+                                    maskFrame ? 1.0 : maskUsage.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.
    */
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     if (maskUsage.shouldApplyClipPath) {
       clipPathFrame->ApplyClipPath(aContext, aFrame, aTransform);
@@ -849,17 +865,17 @@ nsSVGUtils::PaintFrameWithEffects(nsIFra
   } else {
     result = svgChildFrame->PaintSVG(*target, aTransform, aDirtyRect);
   }
 
   if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
     aContext.PopClip();
   }
 
-  if (shouldGenerateMask) {
+  if (shouldPushMask) {
     target->PopGroupAndBlend();
   }
 
   if (blender.ShouldCreateDrawTargetForBlend()) {
     MOZ_ASSERT(target != &aContext);
     blender.BlendToTarget();
   }