layout/svg/nsSVGInnerSVGFrame.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 "nsSVGInnerSVGFrame.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "gfxContext.h"
    11 #include "nsIFrame.h"
    12 #include "nsISVGChildFrame.h"
    13 #include "nsRenderingContext.h"
    14 #include "nsSVGContainerFrame.h"
    15 #include "nsSVGEffects.h"
    16 #include "nsSVGIntegrationUtils.h"
    17 #include "mozilla/dom/SVGSVGElement.h"
    19 using namespace mozilla;
    20 using namespace mozilla::dom;
    22 nsIFrame*
    23 NS_NewSVGInnerSVGFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
    24 {
    25   return new (aPresShell) nsSVGInnerSVGFrame(aContext);
    26 }
    28 NS_IMPL_FRAMEARENA_HELPERS(nsSVGInnerSVGFrame)
    30 //----------------------------------------------------------------------
    31 // nsIFrame methods
    33 NS_QUERYFRAME_HEAD(nsSVGInnerSVGFrame)
    34   NS_QUERYFRAME_ENTRY(nsSVGInnerSVGFrame)
    35   NS_QUERYFRAME_ENTRY(nsISVGSVGFrame)
    36 NS_QUERYFRAME_TAIL_INHERITING(nsSVGInnerSVGFrameBase)
    38 #ifdef DEBUG
    39 void
    40 nsSVGInnerSVGFrame::Init(nsIContent* aContent,
    41                          nsIFrame* aParent,
    42                          nsIFrame* aPrevInFlow)
    43 {
    44   NS_ASSERTION(aContent->IsSVG(nsGkAtoms::svg),
    45                "Content is not an SVG 'svg' element!");
    47   nsSVGInnerSVGFrameBase::Init(aContent, aParent, aPrevInFlow);
    48 }
    49 #endif /* DEBUG */
    51 nsIAtom *
    52 nsSVGInnerSVGFrame::GetType() const
    53 {
    54   return nsGkAtoms::svgInnerSVGFrame;
    55 }
    57 //----------------------------------------------------------------------
    58 // nsISVGChildFrame methods
    60 nsresult
    61 nsSVGInnerSVGFrame::PaintSVG(nsRenderingContext *aContext,
    62                              const nsIntRect *aDirtyRect,
    63                              nsIFrame* aTransformRoot)
    64 {
    65   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
    66                (mState & NS_FRAME_IS_NONDISPLAY),
    67                "If display lists are enabled, only painting of non-display "
    68                "SVG should take this code path");
    70   gfxContextAutoSaveRestore autoSR;
    72   if (StyleDisplay()->IsScrollableOverflow()) {
    73     float x, y, width, height;
    74     static_cast<SVGSVGElement*>(mContent)->
    75       GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
    77     if (width <= 0 || height <= 0) {
    78       return NS_OK;
    79     }
    81     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
    82     gfxMatrix clipTransform = parent->GetCanvasTM(FOR_PAINTING, aTransformRoot);
    84     gfxContext *gfx = aContext->ThebesContext();
    85     autoSR.SetContext(gfx);
    86     gfxRect clipRect =
    87       nsSVGUtils::GetClipRectForFrame(this, x, y, width, height);
    88     nsSVGUtils::SetClipRect(gfx, clipTransform, clipRect);
    89   }
    91   return nsSVGInnerSVGFrameBase::PaintSVG(aContext, aDirtyRect);
    92 }
    94 void
    95 nsSVGInnerSVGFrame::ReflowSVG()
    96 {
    97   // mRect must be set before FinishAndStoreOverflow is called in order
    98   // for our overflow areas to be clipped correctly.
    99   float x, y, width, height;
   100   static_cast<SVGSVGElement*>(mContent)->
   101     GetAnimatedLengthValues(&x, &y, &width, &height, nullptr);
   102   mRect = nsLayoutUtils::RoundGfxRectToAppRect(
   103                            gfxRect(x, y, width, height),
   104                            PresContext()->AppUnitsPerCSSPixel());
   106   // If we have a filter, we need to invalidate ourselves because filter
   107   // output can change even if none of our descendants need repainting.
   108   if (StyleSVGReset()->HasFilters()) {
   109     InvalidateFrame();
   110   }
   112   nsSVGInnerSVGFrameBase::ReflowSVG();
   113 }
   115 void
   116 nsSVGInnerSVGFrame::NotifySVGChanged(uint32_t aFlags)
   117 {
   118   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
   119                     "Invalidation logic may need adjusting");
   121   if (aFlags & COORD_CONTEXT_CHANGED) {
   123     SVGSVGElement *svg = static_cast<SVGSVGElement*>(mContent);
   125     bool xOrYIsPercentage =
   126       svg->mLengthAttributes[SVGSVGElement::ATTR_X].IsPercentage() ||
   127       svg->mLengthAttributes[SVGSVGElement::ATTR_Y].IsPercentage();
   128     bool widthOrHeightIsPercentage =
   129       svg->mLengthAttributes[SVGSVGElement::ATTR_WIDTH].IsPercentage() ||
   130       svg->mLengthAttributes[SVGSVGElement::ATTR_HEIGHT].IsPercentage();
   132     if (xOrYIsPercentage || widthOrHeightIsPercentage) {
   133       // Ancestor changes can't affect how we render from the perspective of
   134       // any rendering observers that we may have, so we don't need to
   135       // invalidate them. We also don't need to invalidate ourself, since our
   136       // changed ancestor will have invalidated its entire area, which includes
   137       // our area.
   138       // For perf reasons we call this before calling NotifySVGChanged() below.
   139       nsSVGUtils::ScheduleReflowSVG(this);
   140     }
   142     // Coordinate context changes affect mCanvasTM if we have a
   143     // percentage 'x' or 'y', or if we have a percentage 'width' or 'height' AND
   144     // a 'viewBox'.
   146     if (!(aFlags & TRANSFORM_CHANGED) &&
   147         (xOrYIsPercentage ||
   148          (widthOrHeightIsPercentage && svg->HasViewBoxRect()))) {
   149       aFlags |= TRANSFORM_CHANGED;
   150     }
   152     if (svg->HasViewBoxRect() || !widthOrHeightIsPercentage) {
   153       // Remove COORD_CONTEXT_CHANGED, since we establish the coordinate
   154       // context for our descendants and this notification won't change its
   155       // dimensions:
   156       aFlags &= ~COORD_CONTEXT_CHANGED;
   158       if (!aFlags) {
   159         return; // No notification flags left
   160       }
   161     }
   162   }
   164   if (aFlags & TRANSFORM_CHANGED) {
   165     // make sure our cached transform matrix gets (lazily) updated
   166     mCanvasTM = nullptr;
   167   }
   169   nsSVGInnerSVGFrameBase::NotifySVGChanged(aFlags);
   170 }
   172 nsresult
   173 nsSVGInnerSVGFrame::AttributeChanged(int32_t  aNameSpaceID,
   174                                      nsIAtom* aAttribute,
   175                                      int32_t  aModType)
   176 {
   177   if (aNameSpaceID == kNameSpaceID_None &&
   178       !(GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   180     SVGSVGElement* content = static_cast<SVGSVGElement*>(mContent);
   182     if (aAttribute == nsGkAtoms::width ||
   183         aAttribute == nsGkAtoms::height) {
   184       nsSVGEffects::InvalidateRenderingObservers(this);
   185       nsSVGUtils::ScheduleReflowSVG(this);
   187       if (content->HasViewBoxOrSyntheticViewBox()) {
   188         // make sure our cached transform matrix gets (lazily) updated
   189         mCanvasTM = nullptr;
   190         content->ChildrenOnlyTransformChanged();
   191         nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
   192       } else {
   193         uint32_t flags = COORD_CONTEXT_CHANGED;
   194         if (mCanvasTM && mCanvasTM->IsSingular()) {
   195           mCanvasTM = nullptr;
   196           flags |= TRANSFORM_CHANGED;
   197         }
   198         nsSVGUtils::NotifyChildrenOfSVGChange(this, flags);
   199       }
   201     } else if (aAttribute == nsGkAtoms::transform ||
   202                aAttribute == nsGkAtoms::preserveAspectRatio ||
   203                aAttribute == nsGkAtoms::viewBox ||
   204                aAttribute == nsGkAtoms::x ||
   205                aAttribute == nsGkAtoms::y) {
   206       // make sure our cached transform matrix gets (lazily) updated
   207       mCanvasTM = nullptr;
   209       nsSVGUtils::NotifyChildrenOfSVGChange(
   210           this, aAttribute == nsGkAtoms::viewBox ?
   211                   TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED : TRANSFORM_CHANGED);
   213       // We don't invalidate for transform changes (the layers code does that).
   214       // Also note that SVGTransformableElement::GetAttributeChangeHint will
   215       // return nsChangeHint_UpdateOverflow for "transform" attribute changes
   216       // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
   218       if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y) {
   219         nsSVGEffects::InvalidateRenderingObservers(this);
   220         nsSVGUtils::ScheduleReflowSVG(this);
   221       } else if (aAttribute == nsGkAtoms::viewBox ||
   222                  (aAttribute == nsGkAtoms::preserveAspectRatio &&
   223                   content->HasViewBoxOrSyntheticViewBox())) {
   224         content->ChildrenOnlyTransformChanged();
   225         // SchedulePaint sets a global state flag so we only need to call it once
   226         // (on ourself is fine), not once on each child (despite bug 828240).
   227         SchedulePaint();
   228       }
   229     }
   230   }
   232   return NS_OK;
   233 }
   235 nsIFrame*
   236 nsSVGInnerSVGFrame::GetFrameForPoint(const nsPoint &aPoint)
   237 {
   238   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
   239                (mState & NS_FRAME_IS_NONDISPLAY),
   240                "If display lists are enabled, only hit-testing of non-display "
   241                "SVG should take this code path");
   243   if (StyleDisplay()->IsScrollableOverflow()) {
   244     nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   245     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
   247     float clipX, clipY, clipWidth, clipHeight;
   248     content->GetAnimatedLengthValues(&clipX, &clipY, &clipWidth, &clipHeight, nullptr);
   250     if (!nsSVGUtils::HitTestRect(gfx::ToMatrix(parent->GetCanvasTM(FOR_HIT_TESTING)),
   251                                  clipX, clipY, clipWidth, clipHeight,
   252                                  PresContext()->AppUnitsToDevPixels(aPoint.x),
   253                                  PresContext()->AppUnitsToDevPixels(aPoint.y))) {
   254       return nullptr;
   255     }
   256   }
   258   return nsSVGInnerSVGFrameBase::GetFrameForPoint(aPoint);
   259 }
   261 //----------------------------------------------------------------------
   262 // nsISVGSVGFrame methods:
   264 void
   265 nsSVGInnerSVGFrame::NotifyViewportOrTransformChanged(uint32_t aFlags)
   266 {
   267   // The dimensions of inner-<svg> frames are purely defined by their "width"
   268   // and "height" attributes, and transform changes can only occur as a result
   269   // of changes to their "width", "height", "viewBox" or "preserveAspectRatio"
   270   // attributes. Changes to all of these attributes are handled in
   271   // AttributeChanged(), so we should never be called.
   272   NS_ERROR("Not called for nsSVGInnerSVGFrame");
   273 }
   275 //----------------------------------------------------------------------
   276 // nsSVGContainerFrame methods:
   278 gfxMatrix
   279 nsSVGInnerSVGFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
   280 {
   281   if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
   282     if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
   283         (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
   284       return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
   285     }
   286   }
   287   if (!mCanvasTM) {
   288     NS_ASSERTION(mParent, "null parent");
   290     nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
   291     SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
   293     gfxMatrix tm = content->PrependLocalTransformsTo(
   294         this == aTransformRoot ? gfxMatrix() :
   295                                  parent->GetCanvasTM(aFor, aTransformRoot));
   297     mCanvasTM = new gfxMatrix(tm);
   298   }
   299   return *mCanvasTM;
   300 }
   302 bool
   303 nsSVGInnerSVGFrame::HasChildrenOnlyTransform(gfx::Matrix *aTransform) const
   304 {
   305   SVGSVGElement *content = static_cast<SVGSVGElement*>(mContent);
   307   if (content->HasViewBoxOrSyntheticViewBox()) {
   308     // XXX Maybe return false if the transform is the identity transform?
   309     if (aTransform) {
   310       *aTransform = content->GetViewBoxTransform();
   311     }
   312     return true;
   313   }
   314   return false;
   315 }

mercurial