virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
                    const nsIntRect* aDirtyRect)
 {
   BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
   basic->SetTarget(aContext->ThebesContext());
   nsRenderingContext::AutoPushTranslation push(aContext, -mOffset);
   mLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, mBuilder);
 }
Beispiel #2
0
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup()
{
  BasicLayerManager* manager =
    static_cast<BasicLayerManager*>(mWidget->GetLayerManager());
  if (manager) {
    NS_ASSERTION(manager->GetBackendType() == LayerManager::LAYERS_BASIC,
      "AutoLayerManagerSetup instantiated for non-basic layer backend!");
    manager->SetDefaultTarget(nsnull, BasicLayerManager::BUFFER_NONE);
  }
}
nsBaseWidget::AutoLayerManagerSetup::~AutoLayerManagerSetup()
{
  BasicLayerManager* manager =
    static_cast<BasicLayerManager*>(mWidget->GetLayerManager());
  if (manager) {
    NS_ASSERTION(manager->GetBackendType() == LAYERS_BASIC,
      "AutoLayerManagerSetup instantiated for non-basic layer backend!");
    manager->SetDefaultTarget(nullptr);
    manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE, ROTATION_0);
  }
}
Beispiel #4
0
nsBaseWidget::AutoLayerManagerSetup::AutoLayerManagerSetup(
    nsBaseWidget* aWidget, gfxContext* aTarget,
    BasicLayerManager::BufferMode aDoubleBuffering)
  : mWidget(aWidget)
{
  BasicLayerManager* manager =
    static_cast<BasicLayerManager*>(mWidget->GetLayerManager());
  if (manager) {
    NS_ASSERTION(manager->GetBackendType() == LayerManager::LAYERS_BASIC,
      "AutoLayerManagerSetup instantiated for non-basic layer backend!");
    manager->SetDefaultTarget(aTarget, aDoubleBuffering);
  }
}
  virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
                     const gfxMatrix& aTransform,
                     const nsIntRect* aDirtyRect,
                     imgDrawingParams& aImgParams) override
  {
    BasicLayerManager* basic = mLayerManager->AsBasicLayerManager();
    RefPtr<gfxContext> oldCtx = basic->GetTarget();
    basic->SetTarget(&aContext);

    gfxContextMatrixAutoSaveRestore autoSR(&aContext);
    aContext.SetMatrixDouble(aContext.CurrentMatrixDouble().PreTranslate(-mUserSpaceToFrameSpaceOffset));

    mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
    basic->SetTarget(oldCtx);
  }
  virtual void Paint(gfxContext& aContext, nsIFrame *aTarget,
                     const gfxMatrix& aTransform,
                     const nsIntRect* aDirtyRect) override
  {
    BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
    basic->SetTarget(&aContext);

    gfxPoint devPixelOffset =
      nsLayoutUtils::PointToGfxPoint(-mOffset,
                                     aTarget->PresContext()->AppUnitsPerDevPixel());

    gfxContextMatrixAutoSaveRestore autoSR(&aContext);
    aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffset));

    mLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, mBuilder);
  }
Beispiel #7
0
LayerManager *
nsWindow::GetLayerManager(PLayerTransactionChild* aShadowManager,
                          LayersBackend aBackendHint,
                          LayerManagerPersistence aPersistence,
                          bool* aAllowRetaining)
{
    if (aAllowRetaining)
        *aAllowRetaining = true;
    if (mLayerManager) {
        // This layer manager might be used for painting outside of DoDraw(), so we need
        // to set the correct rotation on it.
        if (mLayerManager->GetBackendType() == LAYERS_BASIC) {
            BasicLayerManager* manager =
                static_cast<BasicLayerManager*>(mLayerManager.get());
            manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE,
                                                   ScreenRotation(EffectiveScreenRotation()));
        } else if (mLayerManager->GetBackendType() == LAYERS_CLIENT) {
            ClientLayerManager* manager =
                static_cast<ClientLayerManager*>(mLayerManager.get());
            manager->SetDefaultTargetConfiguration(mozilla::layers::BUFFER_NONE,
                                                   ScreenRotation(EffectiveScreenRotation()));
        }
        return mLayerManager;
    }

    // Set mUseLayersAcceleration here to make it consistent with
    // nsBaseWidget::GetLayerManager
    mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration);
    nsWindow *topWindow = sTopWindows[0];

    if (!topWindow) {
        LOGW(" -- no topwindow\n");
        return nullptr;
    }

    if (sUsingOMTC) {
        CreateCompositor();
        if (mLayerManager)
            return mLayerManager;
    }

    if (mUseLayersAcceleration) {
        DebugOnly<nsIntRect> fbBounds = gScreenBounds;
        if (!sGLContext) {
            sGLContext = GLContextProvider::CreateForWindow(this);
        }

        MOZ_ASSERT(fbBounds.value == gScreenBounds);
        if (sGLContext) {
            nsRefPtr<LayerManagerOGL> layerManager = new LayerManagerOGL(this);

            if (layerManager->Initialize(sGLContext)) {
                mLayerManager = layerManager;
                return mLayerManager;
            } else {
                LOGW("Could not create OGL LayerManager");
            }
        } else {
            LOGW("GL context was not created");
        }
    }

    // Fall back to software rendering.
    sFramebufferOpen = Framebuffer::Open();
    if (sFramebufferOpen) {
        LOG("Falling back to framebuffer software rendering");
    } else {
        LOGE("Failed to mmap fb(?!?), aborting ...");
        NS_RUNTIMEABORT("Can't open GL context and can't fall back on /dev/graphics/fb0 ...");
    }

    mLayerManager = new ClientLayerManager(this);
    mUseLayersAcceleration = false;

    return mLayerManager;
}
void
nsSVGIntegrationUtils::PaintFramesWithEffects(gfxContext& aContext,
                                              nsIFrame* aFrame,
                                              const nsRect& aDirtyRect,
                                              const nsRect& aBorderArea,
                                              nsDisplayListBuilder* aBuilder,
                                              LayerManager *aLayerManager)
{
#ifdef DEBUG
  NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
               (NS_SVGDisplayListPaintingEnabled() &&
                !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
               "Should not use nsSVGIntegrationUtils on this SVG frame");
#endif

  /* SVG defines the following rendering model:
   *
   *  1. Render geometry
   *  2. Apply filter
   *  3. Apply clipping, masking, group opacity
   *
   * We follow this, but perform a couple of optimizations:
   *
   * + Use cairo's clipPath when representable natively (single object
   *   clip region).
   *
   * + Merge opacity and masking if both used together.
   */

  const nsIContent* content = aFrame->GetContent();
  bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
  if (hasSVGLayout) {
    nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
    if (!svgChildFrame || !aFrame->GetContent()->IsSVGElement()) {
      NS_ASSERTION(false, "why?");
      return;
    }
    if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
      return; // The SVG spec says not to draw _anything_
    }
  }

  float opacity = aFrame->StyleDisplay()->mOpacity;
  if (opacity == 0.0f) {
    return;
  }
  if (opacity != 1.0f &&
      hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
    opacity = 1.0f;
  }

  /* Properties are added lazily and may have been removed by a restyle,
     so make sure all applicable ones are set again. */
  nsIFrame* firstFrame =
    nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
  nsSVGEffects::EffectProperties effectProperties =
    nsSVGEffects::GetEffectProperties(firstFrame);

  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);

  bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;

  DrawTarget* drawTarget = aContext.GetDrawTarget();
  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&aContext);

  nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
  nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
  if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
    /* Snap the offset if the reference frame is not a SVG frame,
     * since other frames will be snapped to pixel when rendering. */
    offsetToBoundingBox = nsPoint(
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
      aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
  }

  // After applying only "offsetToBoundingBox", aCtx would have its origin at
  // the top left corner of aFrame's bounding box (over all continuations).
  // However, SVG painting needs the origin to be located at the origin of the
  // SVG frame's "user space", i.e. the space in which, for example, the
  // frame's BBox lives.
  // SVG geometry frames and foreignObject frames apply their own offsets, so
  // their position is relative to their user space. So for these frame types,
  // if we want aCtx to be in user space, we first need to subtract the
  // frame's position so that SVG painting can later add it again and the
  // frame is painted in the right place.

  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
  nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
                      nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
  nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;

  NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
               "For non-SVG frames there shouldn't be any additional offset");

  gfxPoint devPixelOffsetToUserSpace =
    nsLayoutUtils::PointToGfxPoint(offsetToUserSpace,
                                   aFrame->PresContext()->AppUnitsPerDevPixel());
  aContext.SetMatrix(aContext.CurrentMatrix().Translate(devPixelOffsetToUserSpace));

  gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);

  const nsStyleSVGReset *svgReset = firstFrame->StyleSVGReset();
  // Keep moving forward even if svgMaskFrame is nullptr or isOK is false.
  // This source is not a svg mask, but it still can be a correct mask image.
  nsSVGMaskFrame *svgMaskFrame = effectProperties.GetMaskFrame(&isOK);

  bool complexEffects = false;
  bool hasValidLayers = svgReset->mMask.HasLayerWithImage();

  // These are used if we require a temporary surface for a custom blend mode.
  RefPtr<gfxContext> target = &aContext;
  IntPoint targetOffset;

  /* 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)
      || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL
      || svgMaskFrame || hasValidLayers) {
    complexEffects = true;

    aContext.Save();
    nsRect clipRect =
      aFrame->GetVisualOverflowRectRelativeToSelf() + toUserSpace;
    aContext.Clip(NSRectToSnappedRect(clipRect,
                                  aFrame->PresContext()->AppUnitsPerDevPixel(),
                                  *drawTarget));

    Matrix maskTransform;
    RefPtr<SourceSurface> maskSurface;
    if (svgMaskFrame) {
      maskSurface = svgMaskFrame->GetMaskForMaskedFrame(&aContext,
                                                     aFrame,
                                                     cssPxToDevPxMatrix,
                                                     opacity,
                                                     &maskTransform);
    } else if (hasValidLayers) {
      gfxRect clipRect = aContext.GetClipExtents();
      {
        gfxContextMatrixAutoSaveRestore matRestore(&aContext);

        aContext.SetMatrix(gfxMatrix());
        clipRect = aContext.GetClipExtents();
      }
      IntRect drawRect = RoundedOut(ToRect(clipRect));
      RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::A8);
      if (!targetDT) {
        aContext.Restore();
        return;
      }

      RefPtr<gfxContext> target = new gfxContext(targetDT);
      target->SetMatrix(matrixAutoSaveRestore.Matrix() * gfxMatrix::Translation(-drawRect.TopLeft()));

      // Generate mask surface.
      uint32_t flags = aBuilder->GetBackgroundPaintFlags() |
                       nsCSSRendering::PAINTBG_MASK_IMAGE;
      nsRenderingContext rc(target);
      nsCSSRendering::PaintBackgroundWithSC(aFrame->PresContext(),
                                            rc,
                                            aFrame,
                                            aDirtyRect,
                                            aBorderArea,
                                            firstFrame->StyleContext(),
                                            *aFrame->StyleBorder(),
                                            flags,
                                            nullptr,
                                            -1);
      maskSurface = targetDT->Snapshot();

      // Compute mask transform.
      Matrix mat = ToMatrix(aContext.CurrentMatrix());
      mat.Invert();
      maskTransform = Matrix::Translation(drawRect.x, drawRect.y) * mat;
    }

    if ((svgMaskFrame || hasValidLayers) && !maskSurface) {
      // Entire surface is clipped out.
      aContext.Restore();
      return;
    }

    if (aFrame->StyleDisplay()->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(&aContext);

        aContext.SetMatrix(gfxMatrix());
        clipRect = aContext.GetClipExtents();
      }

      IntRect drawRect = RoundedOut(ToRect(clipRect));

      RefPtr<DrawTarget> targetDT = aContext.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
      if (!targetDT) {
        aContext.Restore();
        return;
      }
      target = new gfxContext(targetDT);
      target->SetMatrix(aContext.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
      targetOffset = drawRect.TopLeft();
    }

    if (clipPathFrame && !isTrivialClip) {
      Matrix clippedMaskTransform;
      RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(aContext, aFrame, cssPxToDevPxMatrix,
                                                                         &clippedMaskTransform, maskSurface, maskTransform);

      if (clipMaskSurface) {
        maskSurface = clipMaskSurface;
        maskTransform = clippedMaskTransform;
      }
    }

    if (opacity != 1.0f || svgMaskFrame  || hasValidLayers ||
        (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.
   */
  if (clipPathFrame && isTrivialClip) {
    aContext.Save();
    clipPathFrame->ApplyClipPath(aContext, aFrame, cssPxToDevPxMatrix);
  }

  /* Paint the child */
  if (effectProperties.HasValidFilter()) {
    RegularFramePaintCallback callback(aBuilder, aLayerManager,
                                       offsetToUserSpace);

    nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
    gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
    nsFilterInstance::PaintFilteredFrame(aFrame, target->GetDrawTarget(),
                                         tm, &callback, &dirtyRegion);
  } else {
    target->SetMatrix(matrixAutoSaveRestore.Matrix());
    BasicLayerManager* basic = static_cast<BasicLayerManager*>(aLayerManager);
    RefPtr<gfxContext> oldCtx = basic->GetTarget();
    basic->SetTarget(target);
    aLayerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer, aBuilder);
    basic->SetTarget(oldCtx);
  }

  if (clipPathFrame && isTrivialClip) {
    aContext.Restore();
  }

  /* No more effects, we're done. */
  if (!complexEffects) {
    return;
  }

  if (opacity != 1.0f || svgMaskFrame || hasValidLayers ||
      (clipPathFrame && !isTrivialClip)) {
    target->PopGroupAndBlend();
  }

  if (aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
    RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
    target = nullptr;
    RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();

    aContext.SetMatrix(gfxMatrix()); // This will be restored right after.
    RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
    aContext.SetPattern(pattern);
    aContext.Paint();
  }

  aContext.Restore();
}
void
nsSVGIntegrationUtils::PaintFramesWithEffects(const PaintFramesParams& aParams)
{
#ifdef DEBUG
  NS_ASSERTION(!(aParams.frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
               (NS_SVGDisplayListPaintingEnabled() &&
                !(aParams.frame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
               "Should not use nsSVGIntegrationUtils on this SVG frame");
#endif

  /* SVG defines the following rendering model:
   *
   *  1. Render geometry
   *  2. Apply filter
   *  3. Apply clipping, masking, group opacity
   *
   * We follow this, but perform a couple of optimizations:
   *
   * + Use cairo's clipPath when representable natively (single object
   *   clip region).
   *
   * + Merge opacity and masking if both used together.
   */
  nsIFrame* frame = aParams.frame;
  const nsIContent* content = frame->GetContent();
  bool hasSVGLayout = (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
  if (hasSVGLayout) {
    nsISVGChildFrame *svgChildFrame = do_QueryFrame(frame);
    if (!svgChildFrame || !frame->GetContent()->IsSVGElement()) {
      NS_ASSERTION(false, "why?");
      return;
    }
    if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
      return; // The SVG spec says not to draw _anything_
    }
  }

  float opacity = frame->StyleEffects()->mOpacity;
  if (opacity == 0.0f) {
    return;
  }
  if (opacity != 1.0f &&
      (nsSVGUtils::CanOptimizeOpacity(frame) ||
       aParams.callerPaintsOpacity)) {
    opacity = 1.0f;
  }
  MOZ_ASSERT(!nsSVGUtils::CanOptimizeOpacity(frame) || !aParams.callerPaintsOpacity,
             "How can we be optimizing the opacity into the svg as well as having the caller paint it?");

  /* Properties are added lazily and may have been removed by a restyle,
     so make sure all applicable ones are set again. */
  nsIFrame* firstFrame =
    nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
  nsSVGEffects::EffectProperties effectProperties =
    nsSVGEffects::GetEffectProperties(firstFrame);

  bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);

  bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
  gfxContext& context = aParams.ctx;
  DrawTarget* drawTarget = context.GetDrawTarget();
  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);

  nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
  nsPoint offsetToBoundingBox = aParams.builder->ToReferenceFrame(firstFrame) - firstFrameOffset;
  if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
    /* Snap the offset if the reference frame is not a SVG frame,
     * since other frames will be snapped to pixel when rendering. */
    offsetToBoundingBox = nsPoint(
      frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
      frame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
  }

  // After applying only "offsetToBoundingBox", aCtx would have its origin at
  // the top left corner of frame's bounding box (over all continuations).
  // However, SVG painting needs the origin to be located at the origin of the
  // SVG frame's "user space", i.e. the space in which, for example, the
  // frame's BBox lives.
  // SVG geometry frames and foreignObject frames apply their own offsets, so
  // their position is relative to their user space. So for these frame types,
  // if we want aCtx to be in user space, we first need to subtract the
  // frame's position so that SVG painting can later add it again and the
  // frame is painted in the right place.

  gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(frame);
  nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
                      nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
  nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;

  NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
               "For non-SVG frames there shouldn't be any additional offset");

  gfxPoint devPixelOffsetToUserSpace =
    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;

  // 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) {
    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, offsetToUserSpace,
                          maskTransform, maskSurface);
    }

    if (shouldGenerateMaskLayer && !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);

        context.SetMatrix(gfxMatrix());
        clipRect = context.GetClipExtents();
      }

      IntRect drawRect = RoundedOut(ToRect(clipRect));

      RefPtr<DrawTarget> targetDT = context.GetDrawTarget()->CreateSimilarDrawTarget(drawRect.Size(), SurfaceFormat::B8G8R8A8);
      if (!targetDT || !targetDT->IsValid()) {
        context.Restore();
        return;
      }
      target = gfxContext::CreateOrNull(targetDT);
      MOZ_ASSERT(target); // already checked the draw target above
      target->SetMatrix(context.CurrentMatrix() * gfxMatrix::Translation(-drawRect.TopLeft()));
      targetOffset = drawRect.TopLeft();
    }

    if (clipPathFrame && !isTrivialClip) {
      Matrix clippedMaskTransform;
      RefPtr<SourceSurface> clipMaskSurface = clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
                                                                         &clippedMaskTransform, maskSurface, maskTransform);

      if (clipMaskSurface) {
        maskSurface = clipMaskSurface;
        maskTransform = clippedMaskTransform;
      }
    }

    if (opacity != 1.0f || shouldGenerateMaskLayer ||
        (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.
   */
  if (clipPathFrame && isTrivialClip) {
    context.Save();
    clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
  } else if (!clipPathFrame && svgReset->HasClipPath()) {
    context.Save();
    nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
  }

  /* Paint the child */
  if (effectProperties.HasValidFilter()) {
    RegularFramePaintCallback callback(aParams.builder, aParams.layerManager,
                                       offsetToUserSpace);

    nsRegion dirtyRegion = aParams.dirtyRect - offsetToBoundingBox;
    gfxMatrix tm = nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(frame);
    nsFilterInstance::PaintFilteredFrame(frame, target->GetDrawTarget(),
                                         tm, &callback, &dirtyRegion);
  } else {
    target->SetMatrix(matrixAutoSaveRestore.Matrix());
    BasicLayerManager* basic = static_cast<BasicLayerManager*>(aParams.layerManager);
    RefPtr<gfxContext> oldCtx = basic->GetTarget();
    basic->SetTarget(target);
    aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
                                          aParams.builder);
    basic->SetTarget(oldCtx);
  }

  if ((clipPathFrame && isTrivialClip) ||
      (!clipPathFrame && svgReset->HasClipPath())) {
    context.Restore();
  }

  /* No more effects, we're done. */
  if (!complexEffects) {
    return;
  }

  if (opacity != 1.0f || shouldGenerateMaskLayer ||
      (clipPathFrame && !isTrivialClip)) {
    target->PopGroupAndBlend();
  }

  if (frame->StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
    RefPtr<DrawTarget> targetDT = target->GetDrawTarget();
    target = nullptr;
    RefPtr<SourceSurface> targetSurf = targetDT->Snapshot();

    context.SetMatrix(gfxMatrix()); // This will be restored right after.
    RefPtr<gfxPattern> pattern = new gfxPattern(targetSurf, Matrix::Translation(targetOffset.x, targetOffset.y));
    context.SetPattern(pattern);
    context.Paint();
  }

  context.Restore();
}
void
nsSVGIntegrationUtils::PaintMaskAndClipPath(const PaintFramesParams& aParams)
{
  MOZ_ASSERT(UsingMaskOrClipPathForFrame(aParams.frame),
             "Should not use this method when no mask or clipPath effect"
             "on this frame");

  /* SVG defines the following rendering model:
   *
   *  1. Render geometry
   *  2. Apply filter
   *  3. Apply clipping, masking, group opacity
   *
   * We handle #3 here and perform a couple of optimizations:
   *
   * + Use cairo's clipPath when representable natively (single object
   *   clip region).
   *
   * + Merge opacity and masking if both used together.
   */
  nsIFrame* frame = aParams.frame;
  if (!ValidateSVGFrame(frame)) {
    return;
  }

  nsSVGUtils::MaskUsage maskUsage;
  nsSVGUtils::DetermineMaskUsage(aParams.frame, aParams.handleOpacity,
                                 maskUsage);

  if (maskUsage.opacity == 0.0f) {
    return;
  }

  gfxContext& context = aParams.ctx;
  gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(&context);

  /* Properties are added lazily and may have been removed by a restyle,
     so make sure all applicable ones are set again. */
  nsIFrame* firstFrame =
    nsLayoutUtils::FirstContinuationOrIBSplitSibling(frame);
  SVGObserverUtils::EffectProperties effectProperties =
    SVGObserverUtils::GetEffectProperties(firstFrame);

  nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame();

  gfxMatrix cssPxToDevPxMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(frame);
  nsTArray<nsSVGMaskFrame*> maskFrames = effectProperties.GetMaskFrames();

  bool shouldGenerateMask = (maskUsage.opacity != 1.0f ||
                             maskUsage.shouldGenerateClipMaskLayer ||
                             maskUsage.shouldGenerateMaskLayer);
  bool shouldPushMask = false;

  /* Check if we need to do additional operations on this child's
   * rendering, which necessitates rendering into another surface. */
  if (shouldGenerateMask) {
    gfxContextMatrixAutoSaveRestore matSR;

    Matrix maskTransform;
    RefPtr<SourceSurface> maskSurface;
    bool opacityApplied = false;

    if (maskUsage.shouldGenerateMaskLayer) {
      matSR.SetContext(&context);

      // For css-mask, we want to generate a mask for each continuation frame,
      // so we setup context matrix by the position of the current frame,
      // instead of the first continuation frame.
      EffectOffsets offsets = MoveContextOriginToUserSpace(frame, aParams);
      MaskPaintResult paintResult =
        CreateAndPaintMaskSurface(aParams, maskUsage.opacity,
                                  firstFrame->StyleContext(),
                                  maskFrames, offsets.offsetToUserSpace);

      if (paintResult.transparentBlackMask) {
        return;
      }

      maskSurface = paintResult.maskSurface;
      if (maskSurface) {
        shouldPushMask = true;
        maskTransform = paintResult.maskTransform;
        opacityApplied = paintResult.opacityApplied;
      }
    }

    if (maskUsage.shouldGenerateClipMaskLayer) {
      matSR.Restore();
      matSR.SetContext(&context);

      MoveContextOriginToUserSpace(firstFrame, aParams);
      Matrix clipMaskTransform;
      RefPtr<SourceSurface> clipMaskSurface =
        clipPathFrame->GetClipMask(context, frame, cssPxToDevPxMatrix,
                                   &clipMaskTransform, maskSurface,
                                  maskTransform);

      if (clipMaskSurface) {
        maskSurface = clipMaskSurface;
        maskTransform = clipMaskTransform;
      } else {
        // Either entire surface is clipped out, or gfx buffer allocation
        // failure in nsSVGClipPathFrame::GetClipMask.
        return;
      }

      shouldPushMask = true;
    }

    // opacity != 1.0f.
    if (!maskUsage.shouldGenerateClipMaskLayer &&
        !maskUsage.shouldGenerateMaskLayer) {
      MOZ_ASSERT(maskUsage.opacity != 1.0f);

      matSR.SetContext(&context);
      MoveContextOriginToUserSpace(firstFrame, aParams);
      shouldPushMask = true;
    }

    if (shouldPushMask) {
      if (aParams.layerManager->GetRoot()->GetContentFlags() &
          Layer::CONTENT_COMPONENT_ALPHA) {
        context.PushGroupAndCopyBackground(gfxContentType::COLOR_ALPHA,
                                           opacityApplied
                                             ? 1.0
                                             : maskUsage.opacity,
                                           maskSurface, maskTransform);
      } else {
        context.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA,
                                      opacityApplied ? 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) {
    gfxContextMatrixAutoSaveRestore matSR(&context);

    MoveContextOriginToUserSpace(firstFrame, aParams);

    MOZ_ASSERT(!maskUsage.shouldApplyClipPath ||
               !maskUsage.shouldApplyBasicShape);
    if (maskUsage.shouldApplyClipPath) {
      clipPathFrame->ApplyClipPath(context, frame, cssPxToDevPxMatrix);
    } else {
      nsCSSClipPathInstance::ApplyBasicShapeClip(context, frame);
    }
  }

  /* Paint the child */
  context.SetMatrix(matrixAutoSaveRestore.Matrix());
  BasicLayerManager* basic = aParams.layerManager->AsBasicLayerManager();
  RefPtr<gfxContext> oldCtx = basic->GetTarget();
  basic->SetTarget(&context);
  aParams.layerManager->EndTransaction(FrameLayerBuilder::DrawPaintedLayer,
                                       aParams.builder);
  basic->SetTarget(oldCtx);

  if (gfxPrefs::DrawMaskLayer()) {
    gfxContextAutoSaveRestore saver(&context);

    context.NewPath();
    gfxRect drawingRect =
      nsLayoutUtils::RectToGfxRect(aParams.borderArea,
                                   frame->PresContext()->AppUnitsPerDevPixel());
    context.Rectangle(drawingRect, true);
    Color overlayColor(0.0f, 0.0f, 0.0f, 0.8f);
    if (maskUsage.shouldGenerateMaskLayer) {
      overlayColor.r = 1.0f; // red represents css positioned mask.
    }
    if (maskUsage.shouldApplyClipPath ||
        maskUsage.shouldGenerateClipMaskLayer) {
      overlayColor.g = 1.0f; // green represents clip-path:<clip-source>.
    }
    if (maskUsage.shouldApplyBasicShape) {
      overlayColor.b = 1.0f; // blue represents
                             // clip-path:<basic-shape>||<geometry-box>.
    }

    context.SetColor(overlayColor);
    context.Fill();
  }

  if (maskUsage.shouldApplyClipPath || maskUsage.shouldApplyBasicShape) {
    context.PopClip();
  }

  if (shouldPushMask) {
    context.PopGroupAndBlend();
  }

}