layout/svg/nsSVGContainerFrame.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 "nsSVGContainerFrame.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "nsCSSFrameConstructor.h"
    11 #include "nsSVGEffects.h"
    12 #include "nsSVGElement.h"
    13 #include "nsSVGUtils.h"
    14 #include "nsSVGAnimatedTransformList.h"
    15 #include "SVGTextFrame.h"
    16 #include "RestyleManager.h"
    18 using namespace mozilla;
    20 NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
    21   NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
    22 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrameBase)
    24 NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame)
    25   NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame)
    26   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
    27 NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame)
    29 nsIFrame*
    30 NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
    31                         nsStyleContext* aContext)
    32 {
    33   nsIFrame *frame = new (aPresShell) nsSVGContainerFrame(aContext);
    34   // If we were called directly, then the frame is for a <defs> or
    35   // an unknown element type. In both cases we prevent the content
    36   // from displaying directly.
    37   frame->AddStateBits(NS_FRAME_IS_NONDISPLAY);
    38   return frame;
    39 }
    41 NS_IMPL_FRAMEARENA_HELPERS(nsSVGContainerFrame)
    42 NS_IMPL_FRAMEARENA_HELPERS(nsSVGDisplayContainerFrame)
    44 nsresult
    45 nsSVGContainerFrame::AppendFrames(ChildListID  aListID,
    46                                   nsFrameList& aFrameList)
    47 {
    48   return InsertFrames(aListID, mFrames.LastChild(), aFrameList);  
    49 }
    51 nsresult
    52 nsSVGContainerFrame::InsertFrames(ChildListID aListID,
    53                                   nsIFrame* aPrevFrame,
    54                                   nsFrameList& aFrameList)
    55 {
    56   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
    57   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
    58                "inserting after sibling frame with different parent");
    60   mFrames.InsertFrames(this, aPrevFrame, aFrameList);
    62   return NS_OK;
    63 }
    65 nsresult
    66 nsSVGContainerFrame::RemoveFrame(ChildListID aListID,
    67                                  nsIFrame* aOldFrame)
    68 {
    69   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
    71   mFrames.DestroyFrame(aOldFrame);
    72   return NS_OK;
    73 }
    75 bool
    76 nsSVGContainerFrame::UpdateOverflow()
    77 {
    78   if (mState & NS_FRAME_IS_NONDISPLAY) {
    79     // We don't maintain overflow rects.
    80     // XXX It would have be better if the restyle request hadn't even happened.
    81     return false;
    82   }
    83   return nsSVGContainerFrameBase::UpdateOverflow();
    84 }
    86 /**
    87  * Traverses a frame tree, marking any SVGTextFrame frames as dirty
    88  * and calling InvalidateRenderingObservers() on it.
    89  *
    90  * The reason that this helper exists is because SVGTextFrame is special.
    91  * None of the other SVG frames ever need to be reflowed when they have the
    92  * NS_FRAME_IS_NONDISPLAY bit set on them because their PaintSVG methods
    93  * (and those of any containers that they can validly be contained within) do
    94  * not make use of mRect or overflow rects. "em" lengths, etc., are resolved
    95  * as those elements are painted.
    96  *
    97  * SVGTextFrame is different because its anonymous block and inline frames
    98  * need to be reflowed in order to get the correct metrics when things like
    99  * inherited font-size of an ancestor changes, or a delayed webfont loads and
   100  * applies.
   101  *
   102  * We assume that any change that requires the anonymous kid of an
   103  * SVGTextFrame to reflow will result in an NS_FRAME_IS_DIRTY reflow. When
   104  * that reflow reaches an NS_FRAME_IS_NONDISPLAY frame it would normally
   105  * stop, but this helper looks for any SVGTextFrame descendants of such
   106  * frames and marks them NS_FRAME_IS_DIRTY so that the next time that they are
   107  * painted their anonymous kid will first get the necessary reflow.
   108  */
   109 /* static */ void
   110 nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer)
   111 {
   112   NS_ASSERTION(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY,
   113                "expected aContainer to be NS_FRAME_IS_DIRTY");
   114   NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
   115                !aContainer->IsFrameOfType(nsIFrame::eSVG),
   116                "it is wasteful to call ReflowSVGNonDisplayText on a container "
   117                "frame that is not NS_FRAME_IS_NONDISPLAY");
   118   for (nsIFrame* kid = aContainer->GetFirstPrincipalChild(); kid;
   119        kid = kid->GetNextSibling()) {
   120     nsIAtom* type = kid->GetType();
   121     if (type == nsGkAtoms::svgTextFrame) {
   122       static_cast<SVGTextFrame*>(kid)->ReflowSVGNonDisplayText();
   123     } else {
   124       if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
   125           type == nsGkAtoms::svgForeignObjectFrame ||
   126           !kid->IsFrameOfType(nsIFrame::eSVG)) {
   127         ReflowSVGNonDisplayText(kid);
   128       }
   129     }
   130   }
   131 }
   133 void
   134 nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
   135                                  nsIFrame* aParent,
   136                                  nsIFrame* aPrevInFlow)
   137 {
   138   if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
   139     AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
   140   }
   141   nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
   142 }
   144 void
   145 nsSVGDisplayContainerFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   146                                              const nsRect&           aDirtyRect,
   147                                              const nsDisplayListSet& aLists)
   148 {
   149   // mContent could be a XUL element so check for an SVG element before casting
   150   if (mContent->IsSVG() &&
   151       !static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
   152     return;
   153   }
   154   return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
   155 }
   157 nsresult
   158 nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID,
   159                                          nsIFrame* aPrevFrame,
   160                                          nsFrameList& aFrameList)
   161 {
   162   // memorize first old frame after insertion point
   163   // XXXbz once again, this would work a lot better if the nsIFrame
   164   // methods returned framelist iterators....
   165   nsIFrame* nextFrame = aPrevFrame ?
   166     aPrevFrame->GetNextSibling() : GetChildList(aListID).FirstChild();
   167   nsIFrame* firstNewFrame = aFrameList.FirstChild();
   169   // Insert the new frames
   170   nsSVGContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
   172   // If we are not a non-display SVG frame and we do not have a bounds update
   173   // pending, then we need to schedule one for our new children:
   174   if (!(GetStateBits() &
   175         (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN |
   176          NS_FRAME_IS_NONDISPLAY))) {
   177     for (nsIFrame* kid = firstNewFrame; kid != nextFrame;
   178          kid = kid->GetNextSibling()) {
   179       nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   180       if (SVGFrame) {
   181         NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
   182                           "Check for this explicitly in the |if|, then");
   183         bool isFirstReflow = (kid->GetStateBits() & NS_FRAME_FIRST_REFLOW);
   184         // Remove bits so that ScheduleBoundsUpdate will work:
   185         kid->RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
   186                              NS_FRAME_HAS_DIRTY_CHILDREN);
   187         // No need to invalidate the new kid's old bounds, so we just use
   188         // nsSVGUtils::ScheduleBoundsUpdate.
   189         nsSVGUtils::ScheduleReflowSVG(kid);
   190         if (isFirstReflow) {
   191           // Add back the NS_FRAME_FIRST_REFLOW bit:
   192           kid->AddStateBits(NS_FRAME_FIRST_REFLOW);
   193         }
   194       }
   195     }
   196   }
   198   return NS_OK;
   199 }
   201 nsresult
   202 nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
   203                                         nsIFrame* aOldFrame)
   204 {
   205   nsSVGEffects::InvalidateRenderingObservers(aOldFrame);
   207   // nsSVGContainerFrame::RemoveFrame doesn't call down into
   208   // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
   209   // need to schedule a repaint and schedule an update to our overflow rects.
   210   SchedulePaint();
   211   PresContext()->RestyleManager()->PostRestyleEvent(
   212     mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
   214   nsresult rv = nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame);
   216   if (!(GetStateBits() & (NS_FRAME_IS_NONDISPLAY | NS_STATE_IS_OUTER_SVG))) {
   217     nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this);
   218   }
   220   return rv;
   221 }
   223 bool
   224 nsSVGDisplayContainerFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
   225                                              gfx::Matrix *aFromParentTransform) const
   226 {
   227   bool foundTransform = false;
   229   // Check if our parent has children-only transforms:
   230   nsIFrame *parent = GetParent();
   231   if (parent &&
   232       parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
   233     foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
   234                        HasChildrenOnlyTransform(aFromParentTransform);
   235   }
   237   // mContent could be a XUL element so check for an SVG element before casting
   238   if (mContent->IsSVG()) {
   239     nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   240     nsSVGAnimatedTransformList* transformList =
   241       content->GetAnimatedTransformList();
   242     if ((transformList && transformList->HasTransform()) ||
   243         content->GetAnimateMotionTransform()) {
   244       if (aOwnTransform) {
   245         *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
   246                                     nsSVGElement::eUserSpaceToParent));
   247       }
   248       foundTransform = true;
   249     }
   250   }
   251   return foundTransform;
   252 }
   254 //----------------------------------------------------------------------
   255 // nsISVGChildFrame methods
   257 nsresult
   258 nsSVGDisplayContainerFrame::PaintSVG(nsRenderingContext* aContext,
   259                                      const nsIntRect *aDirtyRect,
   260                                      nsIFrame* aTransformRoot)
   261 {
   262   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
   263                (mState & NS_FRAME_IS_NONDISPLAY) ||
   264                PresContext()->IsGlyph(),
   265                "If display lists are enabled, only painting of non-display "
   266                "SVG should take this code path");
   268   const nsStyleDisplay *display = StyleDisplay();
   269   if (display->mOpacity == 0.0)
   270     return NS_OK;
   272   for (nsIFrame* kid = mFrames.FirstChild(); kid;
   273        kid = kid->GetNextSibling()) {
   274     nsSVGUtils::PaintFrameWithEffects(aContext, aDirtyRect, kid, aTransformRoot);
   275   }
   277   return NS_OK;
   278 }
   280 nsIFrame*
   281 nsSVGDisplayContainerFrame::GetFrameForPoint(const nsPoint &aPoint)
   282 {
   283   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
   284                (mState & NS_FRAME_IS_NONDISPLAY),
   285                "If display lists are enabled, only hit-testing of a "
   286                "clipPath's contents should take this code path");
   287   return nsSVGUtils::HitTestChildren(this, aPoint);
   288 }
   290 nsRect
   291 nsSVGDisplayContainerFrame::GetCoveredRegion()
   292 {
   293   return nsSVGUtils::GetCoveredRegion(mFrames);
   294 }
   296 void
   297 nsSVGDisplayContainerFrame::ReflowSVG()
   298 {
   299   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
   300                "This call is probably a wasteful mistake");
   302   NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
   303                     "ReflowSVG mechanism not designed for this");
   305   NS_ABORT_IF_FALSE(GetType() != nsGkAtoms::svgOuterSVGFrame,
   306                     "Do not call on outer-<svg>");
   308   if (!nsSVGUtils::NeedsReflowSVG(this)) {
   309     return;
   310   }
   312   // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
   313   // then our outer-<svg> has previously had its initial reflow. In that case
   314   // we need to make sure that that bit has been removed from ourself _before_
   315   // recursing over our children to ensure that they know too. Otherwise, we
   316   // need to remove it _after_ recursing over our children so that they know
   317   // the initial reflow is currently underway.
   319   bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
   321   bool outerSVGHasHadFirstReflow =
   322     (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
   324   if (outerSVGHasHadFirstReflow) {
   325     mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
   326   }
   328   nsOverflowAreas overflowRects;
   330   for (nsIFrame* kid = mFrames.FirstChild(); kid;
   331        kid = kid->GetNextSibling()) {
   332     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   333     if (SVGFrame) {
   334       NS_ABORT_IF_FALSE(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
   335                         "Check for this explicitly in the |if|, then");
   336       kid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
   337       SVGFrame->ReflowSVG();
   339       // We build up our child frame overflows here instead of using
   340       // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
   341       // frame list, and we're iterating over that list now anyway.
   342       ConsiderChildOverflow(overflowRects, kid);
   343     } else {
   344       // Inside a non-display container frame, we might have some
   345       // SVGTextFrames.  We need to cause those to get reflowed in
   346       // case they are the target of a rendering observer.
   347       NS_ASSERTION(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY,
   348                    "expected kid to be a NS_FRAME_IS_NONDISPLAY frame");
   349       if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) {
   350         nsSVGContainerFrame* container = do_QueryFrame(kid);
   351         if (container && container->GetContent()->IsSVG()) {
   352           ReflowSVGNonDisplayText(container);
   353         }
   354       }
   355     }
   356   }
   358   // <svg> can create an SVG viewport with an offset due to its
   359   // x/y/width/height attributes, and <use> can introduce an offset with an
   360   // empty mRect (any width/height is copied to an anonymous <svg> child).
   361   // Other than that containers should not set mRect since all other offsets
   362   // come from transforms, which are accounted for by nsDisplayTransform.
   363   // Note that we rely on |overflow:visible| to allow display list items to be
   364   // created for our children.
   365   NS_ABORT_IF_FALSE(mContent->Tag() == nsGkAtoms::svg ||
   366                     (mContent->Tag() == nsGkAtoms::use &&
   367                      mRect.Size() == nsSize(0,0)) ||
   368                     mRect.IsEqualEdges(nsRect()),
   369                     "Only inner-<svg>/<use> is expected to have mRect set");
   371   if (isFirstReflow) {
   372     // Make sure we have our filter property (if any) before calling
   373     // FinishAndStoreOverflow (subsequent filter changes are handled off
   374     // nsChangeHint_UpdateEffects):
   375     nsSVGEffects::UpdateEffects(this);
   376   }
   378   FinishAndStoreOverflow(overflowRects, mRect.Size());
   380   // Remove state bits after FinishAndStoreOverflow so that it doesn't
   381   // invalidate on first reflow:
   382   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
   383               NS_FRAME_HAS_DIRTY_CHILDREN);
   384 }  
   386 void
   387 nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
   388 {
   389   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
   390                     "Invalidation logic may need adjusting");
   392   nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
   393 }
   395 SVGBBox
   396 nsSVGDisplayContainerFrame::GetBBoxContribution(
   397   const Matrix &aToBBoxUserspace,
   398   uint32_t aFlags)
   399 {
   400   SVGBBox bboxUnion;
   402   nsIFrame* kid = mFrames.FirstChild();
   403   while (kid) {
   404     nsIContent *content = kid->GetContent();
   405     nsISVGChildFrame* svgKid = do_QueryFrame(kid);
   406     // content could be a XUL element so check for an SVG element before casting
   407     if (svgKid && (!content->IsSVG() ||
   408                    static_cast<const nsSVGElement*>(content)->HasValidDimensions())) {
   410       gfxMatrix transform = gfx::ThebesMatrix(aToBBoxUserspace);
   411       if (content->IsSVG()) {
   412         transform = static_cast<nsSVGElement*>(content)->
   413                       PrependLocalTransformsTo(transform);
   414       }
   415       // We need to include zero width/height vertical/horizontal lines, so we have
   416       // to use UnionEdges.
   417       bboxUnion.UnionEdges(svgKid->GetBBoxContribution(gfx::ToMatrix(transform), aFlags));
   418     }
   419     kid = kid->GetNextSibling();
   420   }
   422   return bboxUnion;
   423 }

mercurial