layout/svg/nsSVGIntegrationUtils.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 // Main header first:
     7 #include "nsSVGIntegrationUtils.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "gfxDrawable.h"
    11 #include "nsCSSAnonBoxes.h"
    12 #include "nsDisplayList.h"
    13 #include "nsFilterInstance.h"
    14 #include "nsLayoutUtils.h"
    15 #include "nsRenderingContext.h"
    16 #include "nsSVGClipPathFrame.h"
    17 #include "nsSVGEffects.h"
    18 #include "nsSVGElement.h"
    19 #include "nsSVGFilterPaintCallback.h"
    20 #include "nsSVGMaskFrame.h"
    21 #include "nsSVGPaintServerFrame.h"
    22 #include "nsSVGUtils.h"
    23 #include "FrameLayerBuilder.h"
    24 #include "BasicLayers.h"
    25 #include "mozilla/gfx/Point.h"
    27 using namespace mozilla;
    28 using namespace mozilla::layers;
    30 // ----------------------------------------------------------------------
    32 /**
    33  * This class is used to get the pre-effects visual overflow rect of a frame,
    34  * or, in the case of a frame with continuations, to collect the union of the
    35  * pre-effects visual overflow rects of all the continuations. The result is
    36  * relative to the origin (top left corner of the border box) of the frame, or,
    37  * if the frame has continuations, the origin of the  _first_ continuation.
    38  */
    39 class PreEffectsVisualOverflowCollector : public nsLayoutUtils::BoxCallback
    40 {
    41 public:
    42   /**
    43    * If the pre-effects visual overflow rect of the frame being examined
    44    * happens to be known, it can be passed in as aCurrentFrame and its
    45    * pre-effects visual overflow rect can be passed in as
    46    * aCurrentFrameOverflowArea. This is just an optimization to save a
    47    * frame property lookup - these arguments are optional.
    48    */
    49   PreEffectsVisualOverflowCollector(nsIFrame* aFirstContinuation,
    50                                     nsIFrame* aCurrentFrame,
    51                                     const nsRect& aCurrentFrameOverflowArea)
    52     : mFirstContinuation(aFirstContinuation)
    53     , mCurrentFrame(aCurrentFrame)
    54     , mCurrentFrameOverflowArea(aCurrentFrameOverflowArea)
    55   {
    56     NS_ASSERTION(!mFirstContinuation->GetPrevContinuation(),
    57                  "We want the first continuation here");
    58   }
    60   virtual void AddBox(nsIFrame* aFrame) MOZ_OVERRIDE {
    61     nsRect overflow = (aFrame == mCurrentFrame) ?
    62       mCurrentFrameOverflowArea : GetPreEffectsVisualOverflowRect(aFrame);
    63     mResult.UnionRect(mResult, overflow + aFrame->GetOffsetTo(mFirstContinuation));
    64   }
    66   nsRect GetResult() const {
    67     return mResult;
    68   }
    70 private:
    72   static nsRect GetPreEffectsVisualOverflowRect(nsIFrame* aFrame) {
    73     nsRect* r = static_cast<nsRect*>
    74       (aFrame->Properties().Get(nsIFrame::PreEffectsBBoxProperty()));
    75     if (r) {
    76       return *r;
    77     }
    78     // Despite the fact that we're invoked for frames with SVG effects applied,
    79     // we can actually get here. All continuations and IB split siblings of a
    80     // frame with SVG effects applied will have the PreEffectsBBoxProperty
    81     // property set on them. Therefore, the frames that are passed to us will
    82     // always have that property set...well, with one exception. If the frames
    83     // for an element with SVG effects applied have been subject to an "IB
    84     // split", then the block frame(s) that caused the split will have been
    85     // wrapped in anonymous, inline-block, nsBlockFrames of pseudo-type
    86     // nsCSSAnonBoxes::mozAnonymousBlock. These "IB split sibling" anonymous
    87     // blocks will have the PreEffectsBBoxProperty property set on them, but
    88     // they will never be passed to us. Instead, we'll be passed the block
    89     // children that they wrap, which don't have the PreEffectsBBoxProperty
    90     // property set on them. This is actually okay. What we care about is
    91     // collecting the _pre_ effects visual overflow rects of the frames to
    92     // which the SVG effects have been applied. Since the IB split results in
    93     // any overflow rect adjustments for transforms, effects, etc. taking
    94     // place on the anonymous block wrappers, the wrapped children are left
    95     // with their overflow rects unaffected. In other words, calling
    96     // GetVisualOverflowRect() on the children will return their pre-effects
    97     // visual overflow rects, just as we need.
    98     //
    99     // A couple of tests that demonstrate the IB split and cause us to get here
   100     // are:
   101     //
   102     //  * reftests/svg/svg-integration/clipPath-html-06.xhtml
   103     //  * reftests/svg/svg-integration/clipPath-html-06-extref.xhtml
   104     //
   105     // If we ever got passed a frame with the PreTransformOverflowAreasProperty
   106     // property set, that would be bad, since then our GetVisualOverflowRect()
   107     // call would give us the post-effects, and post-transform, overflow rect.
   108     //
   109     NS_ASSERTION(aFrame->GetParent()->StyleContext()->GetPseudo() ==
   110                    nsCSSAnonBoxes::mozAnonymousBlock,
   111                  "How did we getting here, then?");
   112     NS_ASSERTION(!aFrame->Properties().Get(
   113                    aFrame->PreTransformOverflowAreasProperty()),
   114                  "GetVisualOverflowRect() won't return the pre-effects rect!");
   115     return aFrame->GetVisualOverflowRect();
   116   }
   118   nsIFrame*     mFirstContinuation;
   119   nsIFrame*     mCurrentFrame;
   120   const nsRect& mCurrentFrameOverflowArea;
   121   nsRect        mResult;
   122 };
   124 /**
   125  * Gets the union of the pre-effects visual overflow rects of all of a frame's
   126  * continuations, in "user space".
   127  */
   128 static nsRect
   129 GetPreEffectsVisualOverflowUnion(nsIFrame* aFirstContinuation,
   130                                  nsIFrame* aCurrentFrame,
   131                                  const nsRect& aCurrentFramePreEffectsOverflow,
   132                                  const nsPoint& aFirstContinuationToUserSpace)
   133 {
   134   NS_ASSERTION(!aFirstContinuation->GetPrevContinuation(),
   135                "Need first continuation here");
   136   PreEffectsVisualOverflowCollector collector(aFirstContinuation,
   137                                               aCurrentFrame,
   138                                               aCurrentFramePreEffectsOverflow);
   139   // Compute union of all overflow areas relative to aFirstContinuation:
   140   nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector);
   141   // Return the result in user space:
   142   return collector.GetResult() + aFirstContinuationToUserSpace;
   143 }
   146 bool
   147 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
   148 {
   149   // Even when SVG display lists are disabled, returning true for SVG frames
   150   // does not adversely affect any of our callers. Therefore we don't bother
   151   // checking the SDL prefs here, since we don't know if we're being called for
   152   // painting or hit-testing anyway.
   153   const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   154   return (style->HasFilters() || style->mClipPath || style->mMask);
   155 }
   157 // For non-SVG frames, this gives the offset to the frame's "user space".
   158 // For SVG frames, this returns a zero offset.
   159 static nsPoint
   160 GetOffsetToBoundingBox(nsIFrame* aFrame)
   161 {
   162   if ((aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
   163     // Do NOT call GetAllInFlowRectsUnion for SVG - it will get the
   164     // covered region relative to the nsSVGOuterSVGFrame, which is absolutely
   165     // not what we want. SVG frames are always in user space, so they have
   166     // no offset adjustment to make.
   167     return nsPoint();
   168   }
   169   // We could allow aFrame to be any continuation, but since that would require
   170   // a GetPrevContinuation() virtual call and conditional returns, and since
   171   // all our current consumers always pass in the first continuation, we don't
   172   // currently bother.
   173   NS_ASSERTION(!aFrame->GetPrevContinuation(), "Not first continuation");
   175   // The GetAllInFlowRectsUnion() call gets the union of the frame border-box
   176   // rects over all continuations, relative to the origin (top-left of the
   177   // border box) of its second argument (here, aFrame, the first continuation).
   178   return -nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame).TopLeft();
   179 }
   181 /* static */ nsSize
   182 nsSVGIntegrationUtils::GetContinuationUnionSize(nsIFrame* aNonSVGFrame)
   183 {
   184   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
   185                "SVG frames should not get here");
   186   nsIFrame* firstFrame =
   187     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
   188   return nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame).Size();
   189 }
   191 /* static */ gfx::Size
   192 nsSVGIntegrationUtils::GetSVGCoordContextForNonSVGFrame(nsIFrame* aNonSVGFrame)
   193 {
   194   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
   195                "SVG frames should not get here");
   196   nsIFrame* firstFrame =
   197     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
   198   nsRect r = nsLayoutUtils::GetAllInFlowRectsUnion(firstFrame, firstFrame);
   199   nsPresContext* presContext = firstFrame->PresContext();
   200   return gfx::Size(presContext->AppUnitsToFloatCSSPixels(r.width),
   201                    presContext->AppUnitsToFloatCSSPixels(r.height));
   202 }
   204 gfxRect
   205 nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(nsIFrame* aNonSVGFrame)
   206 {
   207   NS_ASSERTION(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG),
   208                "SVG frames should not get here");
   209   nsIFrame* firstFrame =
   210     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
   211   // 'r' is in "user space":
   212   nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
   213                                               GetOffsetToBoundingBox(firstFrame));
   214   return nsLayoutUtils::RectToGfxRect(r,
   215            aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
   216 }
   218 // XXX Since we're called during reflow, this method is broken for frames with
   219 // continuations. When we're called for a frame with continuations, we're
   220 // called for each continuation in turn as it's reflowed. However, it isn't
   221 // until the last continuation is reflowed that this method's
   222 // GetOffsetToBoundingBox() and GetPreEffectsVisualOverflowUnion() calls will
   223 // obtain valid border boxes for all the continuations. As a result, we'll
   224 // end up returning bogus post-filter visual overflow rects for all the prior
   225 // continuations. Unfortunately, by the time the last continuation is
   226 // reflowed, it's too late to go back and set and propagate the overflow
   227 // rects on the previous continuations.
   228 //
   229 // The reason that we need to pass an override bbox to
   230 // GetPreEffectsVisualOverflowUnion rather than just letting it call into our
   231 // GetSVGBBoxForNonSVGFrame method is because we get called by
   232 // ComputeEffectsRect when it has been called with
   233 // aStoreRectProperties set to false. In this case the pre-effects visual
   234 // overflow rect that it has been passed may be different to that stored on
   235 // aFrame, resulting in a different bbox.
   236 //
   237 // XXXjwatt The pre-effects visual overflow rect passed to
   238 // ComputeEffectsRect won't include continuation overflows, so
   239 // for frames with continuation the following filter analysis will likely end
   240 // up being carried out with a bbox created as if the frame didn't have
   241 // continuations.
   242 //
   243 // XXXjwatt Using aPreEffectsOverflowRect to create the bbox isn't really right
   244 // for SVG frames, since for SVG frames the SVG spec defines the bbox to be
   245 // something quite different to the pre-effects visual overflow rect. However,
   246 // we're essentially calculating an invalidation area here, and using the
   247 // pre-effects overflow rect will actually overestimate that area which, while
   248 // being a bit wasteful, isn't otherwise a problem.
   249 //
   250 nsRect
   251   nsSVGIntegrationUtils::
   252     ComputePostEffectsVisualOverflowRect(nsIFrame* aFrame,
   253                                          const nsRect& aPreEffectsOverflowRect)
   254 {
   255   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
   256                  "Don't call this on SVG child frames");
   258   nsIFrame* firstFrame =
   259     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   260   nsSVGEffects::EffectProperties effectProperties =
   261     nsSVGEffects::GetEffectProperties(firstFrame);
   262   if (!effectProperties.HasValidFilter()) {
   263     return aPreEffectsOverflowRect;
   264   }
   266   // Create an override bbox - see comment above:
   267   nsPoint firstFrameToBoundingBox = GetOffsetToBoundingBox(firstFrame);
   268   // overrideBBox is in "user space", in _CSS_ pixels:
   269   // XXX Why are we rounding out to pixel boundaries? We don't do that in
   270   // GetSVGBBoxForNonSVGFrame, and it doesn't appear to be necessary.
   271   gfxRect overrideBBox =
   272     nsLayoutUtils::RectToGfxRect(
   273       GetPreEffectsVisualOverflowUnion(firstFrame, aFrame,
   274                                        aPreEffectsOverflowRect,
   275                                        firstFrameToBoundingBox),
   276       aFrame->PresContext()->AppUnitsPerCSSPixel());
   277   overrideBBox.RoundOut();
   279   nsRect overflowRect =
   280     nsFilterInstance::GetPostFilterBounds(firstFrame, &overrideBBox);
   282   // Return overflowRect relative to aFrame, rather than "user space":
   283   return overflowRect - (aFrame->GetOffsetTo(firstFrame) + firstFrameToBoundingBox);
   284 }
   286 nsIntRegion
   287 nsSVGIntegrationUtils::AdjustInvalidAreaForSVGEffects(nsIFrame* aFrame,
   288                                                       const nsPoint& aToReferenceFrame,
   289                                                       const nsIntRegion& aInvalidRegion)
   290 {
   291   if (aInvalidRegion.IsEmpty()) {
   292     return nsIntRect();
   293   }
   295   // Don't bother calling GetEffectProperties; the filter property should
   296   // already have been set up during reflow/ComputeFrameEffectsRect
   297   nsIFrame* firstFrame =
   298     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   299   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
   300   if (!prop || !prop->IsInObserverLists()) {
   301     return aInvalidRegion;
   302   }
   304   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
   306   if (!prop || !prop->ReferencesValidResources()) {
   307     // The frame is either not there or not currently available,
   308     // perhaps because we're in the middle of tearing stuff down.
   309     // Be conservative, return our visual overflow rect relative
   310     // to the reference frame.
   311     nsRect overflow = aFrame->GetVisualOverflowRect() + aToReferenceFrame;
   312     return overflow.ToOutsidePixels(appUnitsPerDevPixel);
   313   }
   315   // Convert aInvalidRegion into bounding box frame space in app units:
   316   nsPoint toBoundingBox =
   317     aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   318   // The initial rect was relative to the reference frame, so we need to
   319   // remove that offset to get a rect relative to the current frame.
   320   toBoundingBox -= aToReferenceFrame;
   321   nsRegion preEffectsRegion = aInvalidRegion.ToAppUnits(appUnitsPerDevPixel).MovedBy(toBoundingBox);
   323   // Adjust the dirty area for effects, and shift it back to being relative to
   324   // the reference frame.
   325   nsRegion result = nsFilterInstance::GetPostFilterDirtyArea(firstFrame,
   326     preEffectsRegion).MovedBy(-toBoundingBox);
   327   // Return the result, in pixels relative to the reference frame.
   328   return result.ToOutsidePixels(appUnitsPerDevPixel);
   329 }
   331 nsRect
   332 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(nsIFrame* aFrame,
   333                                                        const nsRect& aDirtyRect)
   334 {
   335   // Don't bother calling GetEffectProperties; the filter property should
   336   // already have been set up during reflow/ComputeFrameEffectsRect
   337   nsIFrame* firstFrame =
   338     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   339   nsSVGFilterProperty *prop = nsSVGEffects::GetFilterProperty(firstFrame);
   340   if (!prop || !prop->ReferencesValidResources()) {
   341     return aDirtyRect;
   342   }
   344   // Convert aDirtyRect into "user space" in app units:
   345   nsPoint toUserSpace =
   346     aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   347   nsRect postEffectsRect = aDirtyRect + toUserSpace;
   349   // Return ther result, relative to aFrame, not in user space:
   350   return nsFilterInstance::GetPreFilterNeededArea(firstFrame, postEffectsRect).GetBounds()
   351     - toUserSpace;
   352 }
   354 bool
   355 nsSVGIntegrationUtils::HitTestFrameForEffects(nsIFrame* aFrame, const nsPoint& aPt)
   356 {
   357   nsIFrame* firstFrame =
   358     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   359   // Convert aPt to user space:
   360   nsPoint toUserSpace;
   361   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
   362     // XXXmstange Isn't this wrong for svg:use and innerSVG frames?
   363     toUserSpace = aFrame->GetPosition();
   364   } else {
   365     toUserSpace =
   366       aFrame->GetOffsetTo(firstFrame) + GetOffsetToBoundingBox(firstFrame);
   367   }
   368   nsPoint pt = aPt + toUserSpace;
   369   return nsSVGUtils::HitTestClip(firstFrame, pt);
   370 }
   372 class RegularFramePaintCallback : public nsSVGFilterPaintCallback
   373 {
   374 public:
   375   RegularFramePaintCallback(nsDisplayListBuilder* aBuilder,
   376                             LayerManager* aManager,
   377                             const nsPoint& aOffset)
   378     : mBuilder(aBuilder), mLayerManager(aManager),
   379       mOffset(aOffset) {}
   381   virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
   382                      const nsIntRect* aDirtyRect,
   383                      nsIFrame* aTransformRoot) MOZ_OVERRIDE
   384   {
   385     BasicLayerManager* basic = static_cast<BasicLayerManager*>(mLayerManager);
   386     basic->SetTarget(aContext->ThebesContext());
   387     nsRenderingContext::AutoPushTranslation push(aContext, -mOffset);
   388     mLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, mBuilder);
   389   }
   391 private:
   392   nsDisplayListBuilder* mBuilder;
   393   LayerManager* mLayerManager;
   394   nsPoint mOffset;
   395 };
   397 void
   398 nsSVGIntegrationUtils::PaintFramesWithEffects(nsRenderingContext* aCtx,
   399                                               nsIFrame* aFrame,
   400                                               const nsRect& aDirtyRect,
   401                                               nsDisplayListBuilder* aBuilder,
   402                                               LayerManager *aLayerManager)
   403 {
   404 #ifdef DEBUG
   405   NS_ASSERTION(!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
   406                (NS_SVGDisplayListPaintingEnabled() &&
   407                 !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)),
   408                "Should not use nsSVGIntegrationUtils on this SVG frame");
   409 #endif
   411   /* SVG defines the following rendering model:
   412    *
   413    *  1. Render geometry
   414    *  2. Apply filter
   415    *  3. Apply clipping, masking, group opacity
   416    *
   417    * We follow this, but perform a couple of optimizations:
   418    *
   419    * + Use cairo's clipPath when representable natively (single object
   420    *   clip region).
   421    *
   422    * + Merge opacity and masking if both used together.
   423    */
   425   const nsIContent* content = aFrame->GetContent();
   426   bool hasSVGLayout = (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT);
   427   if (hasSVGLayout) {
   428     nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
   429     if (!svgChildFrame || !aFrame->GetContent()->IsSVG()) {
   430       NS_ASSERTION(false, "why?");
   431       return;
   432     }
   433     if (!static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   434       return; // The SVG spec says not to draw _anything_
   435     }
   436   }
   438   float opacity = aFrame->StyleDisplay()->mOpacity;
   439   if (opacity == 0.0f) {
   440     return;
   441   }
   442   if (opacity != 1.0f &&
   443       hasSVGLayout && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
   444     opacity = 1.0f;
   445   }
   447   /* Properties are added lazily and may have been removed by a restyle,
   448      so make sure all applicable ones are set again. */
   449   nsIFrame* firstFrame =
   450     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
   451   nsSVGEffects::EffectProperties effectProperties =
   452     nsSVGEffects::GetEffectProperties(firstFrame);
   454   bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
   455   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
   456   nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
   457   if (!isOK) {
   458     return; // Some resource is missing. We shouldn't paint anything.
   459   }
   461   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
   463   gfxContext* gfx = aCtx->ThebesContext();
   464   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
   466   nsPoint firstFrameOffset = GetOffsetToBoundingBox(firstFrame);
   467   nsPoint offsetToBoundingBox = aBuilder->ToReferenceFrame(firstFrame) - firstFrameOffset;
   468   if (!firstFrame->IsFrameOfType(nsIFrame::eSVG)) {
   469     /* Snap the offset if the reference frame is not a SVG frame,
   470      * since other frames will be snapped to pixel when rendering. */
   471     offsetToBoundingBox = nsPoint(
   472       aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.x),
   473       aFrame->PresContext()->RoundAppUnitsToNearestDevPixels(offsetToBoundingBox.y));
   474   }
   476   // After applying only "offsetToBoundingBox", aCtx would have its origin at
   477   // the top left corner of aFrame's bounding box (over all continuations).
   478   // However, SVG painting needs the origin to be located at the origin of the
   479   // SVG frame's "user space", i.e. the space in which, for example, the
   480   // frame's BBox lives.
   481   // SVG geometry frames and foreignObject frames apply their own offsets, so
   482   // their position is relative to their user space. So for these frame types,
   483   // if we want aCtx to be in user space, we first need to subtract the
   484   // frame's position so that SVG painting can later add it again and the
   485   // frame is painted in the right place.
   487   gfxPoint toUserSpaceGfx = nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(aFrame);
   488   nsPoint toUserSpace(nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.x)),
   489                       nsPresContext::CSSPixelsToAppUnits(float(toUserSpaceGfx.y)));
   490   nsPoint offsetToUserSpace = offsetToBoundingBox - toUserSpace;
   492   NS_ASSERTION(hasSVGLayout || offsetToBoundingBox == offsetToUserSpace,
   493                "For non-SVG frames there shouldn't be any additional offset");
   495   aCtx->Translate(offsetToUserSpace);
   497   gfxMatrix cssPxToDevPxMatrix = GetCSSPxToDevPxMatrix(aFrame);
   499   bool complexEffects = false;
   500   /* Check if we need to do additional operations on this child's
   501    * rendering, which necessitates rendering into another surface. */
   502   if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
   503       || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
   504     complexEffects = true;
   505     gfx->Save();
   506     aCtx->IntersectClip(aFrame->GetVisualOverflowRectRelativeToSelf() +
   507                         toUserSpace);
   508     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   509   }
   511   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
   512    * we can just do normal painting and get it clipped appropriately.
   513    */
   514   if (clipPathFrame && isTrivialClip) {
   515     gfx->Save();
   516     clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
   517   }
   519   /* Paint the child */
   520   if (effectProperties.HasValidFilter()) {
   521     RegularFramePaintCallback callback(aBuilder, aLayerManager,
   522                                        offsetToUserSpace);
   524     nsRegion dirtyRegion = aDirtyRect - offsetToBoundingBox;
   525     nsFilterInstance::PaintFilteredFrame(aCtx, aFrame, &callback, &dirtyRegion);
   526   } else {
   527     gfx->SetMatrix(matrixAutoSaveRestore.Matrix());
   528     aLayerManager->EndTransaction(FrameLayerBuilder::DrawThebesLayer, aBuilder);
   529     aCtx->Translate(offsetToUserSpace);
   530   }
   532   if (clipPathFrame && isTrivialClip) {
   533     gfx->Restore();
   534   }
   536   /* No more effects, we're done. */
   537   if (!complexEffects) {
   538     return;
   539   }
   541   gfx->PopGroupToSource();
   543   nsRefPtr<gfxPattern> maskSurface =
   544     maskFrame ? maskFrame->ComputeMaskAlpha(aCtx, aFrame,
   545                                             cssPxToDevPxMatrix, opacity) : nullptr;
   547   nsRefPtr<gfxPattern> clipMaskSurface;
   548   if (clipPathFrame && !isTrivialClip) {
   549     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   551     nsresult rv = clipPathFrame->ClipPaint(aCtx, aFrame, cssPxToDevPxMatrix);
   552     clipMaskSurface = gfx->PopGroup();
   554     if (NS_SUCCEEDED(rv) && clipMaskSurface) {
   555       // Still more set after clipping, so clip to another surface
   556       if (maskSurface || opacity != 1.0f) {
   557         gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   558         gfx->Mask(clipMaskSurface);
   559         gfx->PopGroupToSource();
   560       } else {
   561         gfx->Mask(clipMaskSurface);
   562       }
   563     }
   564   }
   566   if (maskSurface) {
   567     gfx->Mask(maskSurface);
   568   } else if (opacity != 1.0f) {
   569     gfx->Paint(opacity);
   570   }
   572   gfx->Restore();
   573 }
   575 gfxMatrix
   576 nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(nsIFrame* aNonSVGFrame)
   577 {
   578   int32_t appUnitsPerDevPixel = aNonSVGFrame->PresContext()->AppUnitsPerDevPixel();
   579   float devPxPerCSSPx =
   580     1 / nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPixel);
   582   return gfxMatrix(devPxPerCSSPx, 0.0,
   583                    0.0, devPxPerCSSPx,
   584                    0.0, 0.0);
   585 }
   587 class PaintFrameCallback : public gfxDrawingCallback {
   588 public:
   589   PaintFrameCallback(nsIFrame* aFrame,
   590                      const nsSize aPaintServerSize,
   591                      const gfxIntSize aRenderSize,
   592                      uint32_t aFlags)
   593    : mFrame(aFrame)
   594    , mPaintServerSize(aPaintServerSize)
   595    , mRenderSize(aRenderSize)
   596    , mFlags (aFlags)
   597   {}
   598   virtual bool operator()(gfxContext* aContext,
   599                             const gfxRect& aFillRect,
   600                             const GraphicsFilter& aFilter,
   601                             const gfxMatrix& aTransform) MOZ_OVERRIDE;
   602 private:
   603   nsIFrame* mFrame;
   604   nsSize mPaintServerSize;
   605   gfxIntSize mRenderSize;
   606   uint32_t mFlags;
   607 };
   609 bool
   610 PaintFrameCallback::operator()(gfxContext* aContext,
   611                                const gfxRect& aFillRect,
   612                                const GraphicsFilter& aFilter,
   613                                const gfxMatrix& aTransform)
   614 {
   615   if (mFrame->GetStateBits() & NS_FRAME_DRAWING_AS_PAINTSERVER)
   616     return false;
   618   mFrame->AddStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   620   nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
   621   context->Init(mFrame->PresContext()->DeviceContext(), aContext);
   622   aContext->Save();
   624   // Clip to aFillRect so that we don't paint outside.
   625   aContext->NewPath();
   626   aContext->Rectangle(aFillRect);
   627   aContext->Clip();
   629   aContext->Multiply(gfxMatrix(aTransform).Invert());
   631   // nsLayoutUtils::PaintFrame will anchor its painting at mFrame. But we want
   632   // to have it anchored at the top left corner of the bounding box of all of
   633   // mFrame's continuations. So we add a translation transform.
   634   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   635   nsPoint offset = GetOffsetToBoundingBox(mFrame);
   636   gfxPoint devPxOffset = gfxPoint(offset.x, offset.y) / appUnitsPerDevPixel;
   637   aContext->Multiply(gfxMatrix().Translate(devPxOffset));
   639   gfxSize paintServerSize =
   640     gfxSize(mPaintServerSize.width, mPaintServerSize.height) /
   641       mFrame->PresContext()->AppUnitsPerDevPixel();
   643   // nsLayoutUtils::PaintFrame wants to render with paintServerSize, but we
   644   // want it to render with mRenderSize, so we need to set up a scale transform.
   645   gfxFloat scaleX = mRenderSize.width / paintServerSize.width;
   646   gfxFloat scaleY = mRenderSize.height / paintServerSize.height;
   647   gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
   648   aContext->Multiply(scaleMatrix);
   650   // Draw.
   651   nsRect dirty(-offset.x, -offset.y,
   652                mPaintServerSize.width, mPaintServerSize.height);
   654   uint32_t flags = nsLayoutUtils::PAINT_IN_TRANSFORM |
   655                    nsLayoutUtils::PAINT_ALL_CONTINUATIONS;
   656   if (mFlags & nsSVGIntegrationUtils::FLAG_SYNC_DECODE_IMAGES) {
   657     flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
   658   }
   659   nsLayoutUtils::PaintFrame(context, mFrame,
   660                             dirty, NS_RGBA(0, 0, 0, 0),
   661                             flags);
   663   aContext->Restore();
   665   mFrame->RemoveStateBits(NS_FRAME_DRAWING_AS_PAINTSERVER);
   667   return true;
   668 }
   670 /* static */ already_AddRefed<gfxDrawable>
   671 nsSVGIntegrationUtils::DrawableFromPaintServer(nsIFrame*         aFrame,
   672                                                nsIFrame*         aTarget,
   673                                                const nsSize&     aPaintServerSize,
   674                                                const gfxIntSize& aRenderSize,
   675                                                const gfxMatrix&  aContextMatrix,
   676                                                uint32_t          aFlags)
   677 {
   678   // aPaintServerSize is the size that would be filled when using
   679   // background-repeat:no-repeat and background-size:auto. For normal background
   680   // images, this would be the intrinsic size of the image; for gradients and
   681   // patterns this would be the whole target frame fill area.
   682   // aRenderSize is what we will be actually filling after accounting for
   683   // background-size.
   684   if (aFrame->IsFrameOfType(nsIFrame::eSVGPaintServer)) {
   685     // aFrame is either a pattern or a gradient. These fill the whole target
   686     // frame by default, so aPaintServerSize is the whole target background fill
   687     // area.
   688     nsSVGPaintServerFrame* server =
   689       static_cast<nsSVGPaintServerFrame*>(aFrame);
   691     gfxRect overrideBounds(0, 0,
   692                            aPaintServerSize.width, aPaintServerSize.height);
   693     overrideBounds.ScaleInverse(aFrame->PresContext()->AppUnitsPerDevPixel());
   694     nsRefPtr<gfxPattern> pattern =
   695       server->GetPaintServerPattern(aTarget, aContextMatrix,
   696                                     &nsStyleSVG::mFill, 1.0, &overrideBounds);
   698     if (!pattern)
   699       return nullptr;
   701     // pattern is now set up to fill aPaintServerSize. But we want it to
   702     // fill aRenderSize, so we need to add a scaling transform.
   703     // We couldn't just have set overrideBounds to aRenderSize - it would have
   704     // worked for gradients, but for patterns it would result in a different
   705     // pattern size.
   706     gfxFloat scaleX = overrideBounds.Width() / aRenderSize.width;
   707     gfxFloat scaleY = overrideBounds.Height() / aRenderSize.height;
   708     gfxMatrix scaleMatrix = gfxMatrix().Scale(scaleX, scaleY);
   709     pattern->SetMatrix(scaleMatrix.Multiply(pattern->GetMatrix()));
   710     nsRefPtr<gfxDrawable> drawable =
   711       new gfxPatternDrawable(pattern, aRenderSize);
   712     return drawable.forget();
   713   }
   715   // We don't want to paint into a surface as long as we don't need to, so we
   716   // set up a drawing callback.
   717   nsRefPtr<gfxDrawingCallback> cb =
   718     new PaintFrameCallback(aFrame, aPaintServerSize, aRenderSize, aFlags);
   719   nsRefPtr<gfxDrawable> drawable = new gfxCallbackDrawable(cb, aRenderSize);
   720   return drawable.forget();
   721 }

mercurial