layout/svg/nsSVGClipPathFrame.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 "nsSVGClipPathFrame.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "gfxContext.h"
    11 #include "nsGkAtoms.h"
    12 #include "nsRenderingContext.h"
    13 #include "mozilla/dom/SVGClipPathElement.h"
    14 #include "nsSVGEffects.h"
    15 #include "nsSVGUtils.h"
    17 using namespace mozilla::dom;
    19 //----------------------------------------------------------------------
    20 // Implementation
    22 nsIFrame*
    23 NS_NewSVGClipPathFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    24 {
    25   return new (aPresShell) nsSVGClipPathFrame(aContext);
    26 }
    28 NS_IMPL_FRAMEARENA_HELPERS(nsSVGClipPathFrame)
    30 nsresult
    31 nsSVGClipPathFrame::ClipPaint(nsRenderingContext* aContext,
    32                               nsIFrame* aParent,
    33                               const gfxMatrix &aMatrix)
    34 {
    35   // If the flag is set when we get here, it means this clipPath frame
    36   // has already been used painting the current clip, and the document
    37   // has a clip reference loop.
    38   if (mInUse) {
    39     NS_WARNING("Clip loop detected!");
    40     return NS_OK;
    41   }
    42   AutoClipPathReferencer clipRef(this);
    44   mClipParent = aParent;
    45   if (mClipParentMatrix) {
    46     *mClipParentMatrix = aMatrix;
    47   } else {
    48     mClipParentMatrix = new gfxMatrix(aMatrix);
    49   }
    51   gfxContext *gfx = aContext->ThebesContext();
    53   nsISVGChildFrame *singleClipPathChild = nullptr;
    55   if (IsTrivial(&singleClipPathChild)) {
    56     // Notify our child that it's painting as part of a clipPath, and that
    57     // we only require it to draw its path (it should skip filling, etc.):
    58     SVGAutoRenderState mode(aContext, SVGAutoRenderState::CLIP);
    60     if (!singleClipPathChild) {
    61       // We have no children - the spec says clip away everything:
    62       gfx->Rectangle(gfxRect());
    63     } else {
    64       singleClipPathChild->NotifySVGChanged(
    65                              nsISVGChildFrame::TRANSFORM_CHANGED);
    66       singleClipPathChild->PaintSVG(aContext, nullptr);
    67     }
    68     gfx->Clip();
    69     gfx->NewPath();
    70     return NS_OK;
    71   }
    73   // Seems like this is a non-trivial clipPath, so we need to use a clip mask.
    75   // Notify our children that they're painting into a clip mask:
    76   SVGAutoRenderState mode(aContext, SVGAutoRenderState::CLIP_MASK);
    78   // Check if this clipPath is itself clipped by another clipPath:
    79   nsSVGClipPathFrame *clipPathFrame =
    80     nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
    81   bool referencedClipIsTrivial;
    82   if (clipPathFrame) {
    83     referencedClipIsTrivial = clipPathFrame->IsTrivial();
    84     gfx->Save();
    85     if (referencedClipIsTrivial) {
    86       clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
    87     } else {
    88       gfx->PushGroup(gfxContentType::ALPHA);
    89     }
    90   }
    92   for (nsIFrame* kid = mFrames.FirstChild(); kid;
    93        kid = kid->GetNextSibling()) {
    94     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
    95     if (SVGFrame) {
    96       // The CTM of each frame referencing us can be different.
    97       SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
    99       bool isOK = true;
   100       nsSVGClipPathFrame *clipPathFrame =
   101         nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(&isOK);
   102       if (!isOK) {
   103         continue;
   104       }
   106       bool isTrivial;
   108       if (clipPathFrame) {
   109         isTrivial = clipPathFrame->IsTrivial();
   110         gfx->Save();
   111         if (isTrivial) {
   112           clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
   113         } else {
   114           gfx->PushGroup(gfxContentType::ALPHA);
   115         }
   116       }
   118       SVGFrame->PaintSVG(aContext, nullptr);
   120       if (clipPathFrame) {
   121         if (!isTrivial) {
   122           gfx->PopGroupToSource();
   124           nsRefPtr<gfxPattern> clipMaskSurface;
   125           gfx->PushGroup(gfxContentType::ALPHA);
   127           clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
   128           clipMaskSurface = gfx->PopGroup();
   130           if (clipMaskSurface) {
   131             gfx->Mask(clipMaskSurface);
   132           }
   133         }
   134         gfx->Restore();
   135       }
   136     }
   137   }
   139   if (clipPathFrame) {
   140     if (!referencedClipIsTrivial) {
   141       gfx->PopGroupToSource();
   143       nsRefPtr<gfxPattern> clipMaskSurface;
   144       gfx->PushGroup(gfxContentType::ALPHA);
   146       clipPathFrame->ClipPaint(aContext, aParent, aMatrix);
   147       clipMaskSurface = gfx->PopGroup();
   149       if (clipMaskSurface) {
   150         gfx->Mask(clipMaskSurface);
   151       }
   152     }
   153     gfx->Restore();
   154   }
   156   return NS_OK;
   157 }
   159 bool
   160 nsSVGClipPathFrame::ClipHitTest(nsIFrame* aParent,
   161                                 const gfxMatrix &aMatrix,
   162                                 const nsPoint &aPoint)
   163 {
   164   // If the flag is set when we get here, it means this clipPath frame
   165   // has already been used in hit testing against the current clip,
   166   // and the document has a clip reference loop.
   167   if (mInUse) {
   168     NS_WARNING("Clip loop detected!");
   169     return false;
   170   }
   171   AutoClipPathReferencer clipRef(this);
   173   mClipParent = aParent;
   174   if (mClipParentMatrix) {
   175     *mClipParentMatrix = aMatrix;
   176   } else {
   177     mClipParentMatrix = new gfxMatrix(aMatrix);
   178   }
   180   nsSVGClipPathFrame *clipPathFrame =
   181     nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr);
   182   if (clipPathFrame && !clipPathFrame->ClipHitTest(aParent, aMatrix, aPoint))
   183     return false;
   185   for (nsIFrame* kid = mFrames.FirstChild(); kid;
   186        kid = kid->GetNextSibling()) {
   187     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   188     if (SVGFrame) {
   189       // Notify the child frame that we may be working with a
   190       // different transform, so it can update its covered region
   191       // (used to shortcut hit testing).
   192       SVGFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
   194       if (SVGFrame->GetFrameForPoint(aPoint))
   195         return true;
   196     }
   197   }
   198   return false;
   199 }
   201 bool
   202 nsSVGClipPathFrame::IsTrivial(nsISVGChildFrame **aSingleChild)
   203 {
   204   // If the clip path is clipped then it's non-trivial
   205   if (nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(nullptr))
   206     return false;
   208   if (aSingleChild) {
   209     *aSingleChild = nullptr;
   210   }
   212   nsISVGChildFrame *foundChild = nullptr;
   214   for (nsIFrame* kid = mFrames.FirstChild(); kid;
   215        kid = kid->GetNextSibling()) {
   216     nsISVGChildFrame *svgChild = do_QueryFrame(kid);
   217     if (svgChild) {
   218       // We consider a non-trivial clipPath to be one containing
   219       // either more than one svg child and/or a svg container
   220       if (foundChild || svgChild->IsDisplayContainer())
   221         return false;
   223       // or where the child is itself clipped
   224       if (nsSVGEffects::GetEffectProperties(kid).GetClipPathFrame(nullptr))
   225         return false;
   227       foundChild = svgChild;
   228     }
   229   }
   230   if (aSingleChild) {
   231     *aSingleChild = foundChild;
   232   }
   233   return true;
   234 }
   236 bool
   237 nsSVGClipPathFrame::IsValid()
   238 {
   239   if (mInUse) {
   240     NS_WARNING("Clip loop detected!");
   241     return false;
   242   }
   243   AutoClipPathReferencer clipRef(this);
   245   bool isOK = true;
   246   nsSVGEffects::GetEffectProperties(this).GetClipPathFrame(&isOK);
   247   if (!isOK) {
   248     return false;
   249   }
   251   for (nsIFrame* kid = mFrames.FirstChild(); kid;
   252        kid = kid->GetNextSibling()) {
   254     nsIAtom *type = kid->GetType();
   256     if (type == nsGkAtoms::svgUseFrame) {
   257       for (nsIFrame* grandKid = kid->GetFirstPrincipalChild(); grandKid;
   258            grandKid = grandKid->GetNextSibling()) {
   260         nsIAtom *type = grandKid->GetType();
   262         if (type != nsGkAtoms::svgPathGeometryFrame &&
   263             type != nsGkAtoms::svgTextFrame) {
   264           return false;
   265         }
   266       }
   267       continue;
   268     }
   269     if (type != nsGkAtoms::svgPathGeometryFrame &&
   270         type != nsGkAtoms::svgTextFrame) {
   271       return false;
   272     }
   273   }
   274   return true;
   275 }
   277 nsresult
   278 nsSVGClipPathFrame::AttributeChanged(int32_t         aNameSpaceID,
   279                                      nsIAtom*        aAttribute,
   280                                      int32_t         aModType)
   281 {
   282   if (aNameSpaceID == kNameSpaceID_None) {
   283     if (aAttribute == nsGkAtoms::transform) {
   284       nsSVGEffects::InvalidateDirectRenderingObservers(this);
   285       nsSVGUtils::NotifyChildrenOfSVGChange(this,
   286                                             nsISVGChildFrame::TRANSFORM_CHANGED);
   287     }
   288     if (aAttribute == nsGkAtoms::clipPathUnits) {
   289       nsSVGEffects::InvalidateRenderingObservers(this);
   290     }
   291   }
   293   return nsSVGClipPathFrameBase::AttributeChanged(aNameSpaceID,
   294                                                   aAttribute, aModType);
   295 }
   297 void
   298 nsSVGClipPathFrame::Init(nsIContent* aContent,
   299                          nsIFrame* aParent,
   300                          nsIFrame* aPrevInFlow)
   301 {
   302   NS_ASSERTION(aContent->IsSVG(nsGkAtoms::clipPath),
   303                "Content is not an SVG clipPath!");
   305   AddStateBits(NS_STATE_SVG_CLIPPATH_CHILD);
   306   nsSVGClipPathFrameBase::Init(aContent, aParent, aPrevInFlow);
   307 }
   309 nsIAtom *
   310 nsSVGClipPathFrame::GetType() const
   311 {
   312   return nsGkAtoms::svgClipPathFrame;
   313 }
   315 gfxMatrix
   316 nsSVGClipPathFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
   317 {
   318   SVGClipPathElement *content = static_cast<SVGClipPathElement*>(mContent);
   320   gfxMatrix tm =
   321     content->PrependLocalTransformsTo(mClipParentMatrix ?
   322                                       *mClipParentMatrix : gfxMatrix());
   324   return nsSVGUtils::AdjustMatrixForUnits(tm,
   325                                           &content->mEnumAttributes[SVGClipPathElement::CLIPPATHUNITS],
   326                                           mClipParent);
   327 }

mercurial