LCOV - code coverage report
Current view: top level - layout/svg - nsSVGContainerFrame.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 91 163 55.8 %
Date: 2017-07-14 16:53:18 Functions: 12 19 63.2 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : // Main header first:
       7             : #include "nsSVGContainerFrame.h"
       8             : 
       9             : // Keep others in (case-insensitive) order:
      10             : #include "DrawResult.h"
      11             : #include "mozilla/RestyleManager.h"
      12             : #include "mozilla/RestyleManagerInlines.h"
      13             : #include "nsCSSFrameConstructor.h"
      14             : #include "nsSVGEffects.h"
      15             : #include "nsSVGElement.h"
      16             : #include "nsSVGUtils.h"
      17             : #include "nsSVGAnimatedTransformList.h"
      18             : #include "SVGTextFrame.h"
      19             : 
      20             : using namespace mozilla;
      21             : using namespace mozilla::image;
      22             : 
      23         323 : NS_QUERYFRAME_HEAD(nsSVGContainerFrame)
      24          13 :   NS_QUERYFRAME_ENTRY(nsSVGContainerFrame)
      25         310 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
      26             : 
      27         327 : NS_QUERYFRAME_HEAD(nsSVGDisplayContainerFrame)
      28           0 :   NS_QUERYFRAME_ENTRY(nsSVGDisplayContainerFrame)
      29          78 :   NS_QUERYFRAME_ENTRY(nsSVGDisplayableFrame)
      30         249 : NS_QUERYFRAME_TAIL_INHERITING(nsSVGContainerFrame)
      31             : 
      32             : nsIFrame*
      33           5 : NS_NewSVGContainerFrame(nsIPresShell* aPresShell,
      34             :                         nsStyleContext* aContext)
      35             : {
      36             :   nsIFrame* frame =
      37           5 :     new (aPresShell) nsSVGContainerFrame(aContext, nsSVGContainerFrame::kClassID);
      38             :   // If we were called directly, then the frame is for a <defs> or
      39             :   // an unknown element type. In both cases we prevent the content
      40             :   // from displaying directly.
      41           5 :   frame->AddStateBits(NS_FRAME_IS_NONDISPLAY);
      42           5 :   return frame;
      43             : }
      44             : 
      45           5 : NS_IMPL_FRAMEARENA_HELPERS(nsSVGContainerFrame)
      46             : 
      47             : void
      48           0 : nsSVGContainerFrame::AppendFrames(ChildListID  aListID,
      49             :                                   nsFrameList& aFrameList)
      50             : {
      51           0 :   InsertFrames(aListID, mFrames.LastChild(), aFrameList);
      52           0 : }
      53             : 
      54             : void
      55          13 : nsSVGContainerFrame::InsertFrames(ChildListID aListID,
      56             :                                   nsIFrame* aPrevFrame,
      57             :                                   nsFrameList& aFrameList)
      58             : {
      59          13 :   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
      60          13 :   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
      61             :                "inserting after sibling frame with different parent");
      62             : 
      63          13 :   mFrames.InsertFrames(this, aPrevFrame, aFrameList);
      64          13 : }
      65             : 
      66             : void
      67           0 : nsSVGContainerFrame::RemoveFrame(ChildListID aListID,
      68             :                                  nsIFrame* aOldFrame)
      69             : {
      70           0 :   NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
      71             : 
      72           0 :   mFrames.DestroyFrame(aOldFrame);
      73           0 : }
      74             : 
      75             : bool
      76           0 : nsSVGContainerFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
      77             : {
      78           0 :   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           0 :     return false;
      82             :   }
      83           0 :   return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
      84             : }
      85             : 
      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             :  * However, we only need to do this work if we were reflowed with
     103             :  * NS_FRAME_IS_DIRTY, which implies that all descendants are dirty.  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
     107             :  * are painted their anonymous kid will first get the necessary reflow.
     108             :  */
     109             : /* static */ void
     110          29 : nsSVGContainerFrame::ReflowSVGNonDisplayText(nsIFrame* aContainer)
     111             : {
     112          29 :   if (!(aContainer->GetStateBits() & NS_FRAME_IS_DIRTY)) {
     113           0 :     return;
     114             :   }
     115          29 :   NS_ASSERTION((aContainer->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
     116             :                !aContainer->IsFrameOfType(nsIFrame::eSVG),
     117             :                "it is wasteful to call ReflowSVGNonDisplayText on a container "
     118             :                "frame that is not NS_FRAME_IS_NONDISPLAY");
     119          82 :   for (nsIFrame* kid : aContainer->PrincipalChildList()) {
     120          53 :     LayoutFrameType type = kid->Type();
     121          53 :     if (type == LayoutFrameType::SVGText) {
     122           0 :       static_cast<SVGTextFrame*>(kid)->ReflowSVGNonDisplayText();
     123             :     } else {
     124         143 :       if (kid->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer) ||
     125          90 :           type == LayoutFrameType::SVGForeignObject ||
     126          37 :           !kid->IsFrameOfType(nsIFrame::eSVG)) {
     127          16 :         ReflowSVGNonDisplayText(kid);
     128             :       }
     129             :     }
     130             :   }
     131             : }
     132             : 
     133             : void
     134          78 : nsSVGDisplayContainerFrame::Init(nsIContent*       aContent,
     135             :                                  nsContainerFrame* aParent,
     136             :                                  nsIFrame*         aPrevInFlow)
     137             : {
     138          78 :   if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
     139          56 :     AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
     140             :   }
     141          78 :   nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
     142          78 : }
     143             : 
     144             : void
     145          43 : 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          86 :   if (mContent->IsSVGElement() &&
     151          43 :       !static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
     152           0 :     return;
     153             :   }
     154          43 :   DisplayOutline(aBuilder, aLists);
     155          43 :   return BuildDisplayListForNonBlockChildren(aBuilder, aDirtyRect, aLists);
     156             : }
     157             : 
     158             : void
     159          10 : nsSVGDisplayContainerFrame::InsertFrames(ChildListID aListID,
     160             :                                          nsIFrame* aPrevFrame,
     161             :                                          nsFrameList& aFrameList)
     162             : {
     163             :   // memorize first old frame after insertion point
     164             :   // XXXbz once again, this would work a lot better if the nsIFrame
     165             :   // methods returned framelist iterators....
     166          13 :   nsIFrame* nextFrame = aPrevFrame ?
     167          13 :     aPrevFrame->GetNextSibling() : GetChildList(aListID).FirstChild();
     168          10 :   nsIFrame* firstNewFrame = aFrameList.FirstChild();
     169             : 
     170             :   // Insert the new frames
     171          10 :   nsSVGContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList);
     172             : 
     173             :   // If we are not a non-display SVG frame and we do not have a bounds update
     174             :   // pending, then we need to schedule one for our new children:
     175          10 :   if (!(GetStateBits() &
     176             :         (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN |
     177             :          NS_FRAME_IS_NONDISPLAY))) {
     178           0 :     for (nsIFrame* kid = firstNewFrame; kid != nextFrame;
     179             :          kid = kid->GetNextSibling()) {
     180           0 :       nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
     181           0 :       if (SVGFrame) {
     182           0 :         MOZ_ASSERT(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
     183             :                    "Check for this explicitly in the |if|, then");
     184           0 :         bool isFirstReflow = (kid->GetStateBits() & NS_FRAME_FIRST_REFLOW);
     185             :         // Remove bits so that ScheduleBoundsUpdate will work:
     186           0 :         kid->RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
     187           0 :                              NS_FRAME_HAS_DIRTY_CHILDREN);
     188             :         // No need to invalidate the new kid's old bounds, so we just use
     189             :         // nsSVGUtils::ScheduleBoundsUpdate.
     190           0 :         nsSVGUtils::ScheduleReflowSVG(kid);
     191           0 :         if (isFirstReflow) {
     192             :           // Add back the NS_FRAME_FIRST_REFLOW bit:
     193           0 :           kid->AddStateBits(NS_FRAME_FIRST_REFLOW);
     194             :         }
     195             :       }
     196             :     }
     197             :   }
     198          10 : }
     199             : 
     200             : void
     201           0 : nsSVGDisplayContainerFrame::RemoveFrame(ChildListID aListID,
     202             :                                         nsIFrame* aOldFrame)
     203             : {
     204           0 :   nsSVGEffects::InvalidateRenderingObservers(aOldFrame);
     205             : 
     206             :   // nsSVGContainerFrame::RemoveFrame doesn't call down into
     207             :   // nsContainerFrame::RemoveFrame, so it doesn't call FrameNeedsReflow. We
     208             :   // need to schedule a repaint and schedule an update to our overflow rects.
     209           0 :   SchedulePaint();
     210           0 :   PresContext()->RestyleManager()->PostRestyleEvent(
     211           0 :     mContent->AsElement(), nsRestyleHint(0), nsChangeHint_UpdateOverflow);
     212             : 
     213           0 :   nsSVGContainerFrame::RemoveFrame(aListID, aOldFrame);
     214             : 
     215           0 :   if (!(GetStateBits() & (NS_FRAME_IS_NONDISPLAY | NS_STATE_IS_OUTER_SVG))) {
     216           0 :     nsSVGUtils::NotifyAncestorsOfFilterRegionChange(this);
     217             :   }
     218           0 : }
     219             : 
     220             : bool
     221         405 : nsSVGDisplayContainerFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
     222             :                                              gfx::Matrix *aFromParentTransform) const
     223             : {
     224         405 :   bool foundTransform = false;
     225             : 
     226             :   // Check if our parent has children-only transforms:
     227         405 :   nsIFrame *parent = GetParent();
     228         810 :   if (parent &&
     229         405 :       parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
     230             :     foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
     231         405 :                        HasChildrenOnlyTransform(aFromParentTransform);
     232             :   }
     233             : 
     234             :   // mContent could be a XUL element so check for an SVG element before casting
     235         405 :   if (mContent->IsSVGElement()) {
     236         405 :     nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
     237             :     nsSVGAnimatedTransformList* transformList =
     238         405 :       content->GetAnimatedTransformList();
     239         480 :     if ((transformList && transformList->HasTransform()) ||
     240          75 :         content->GetAnimateMotionTransform()) {
     241         330 :       if (aOwnTransform) {
     242             :         *aOwnTransform = gfx::ToMatrix(
     243          84 :                            content->PrependLocalTransformsTo(
     244         126 :                              gfxMatrix(), eUserSpaceToParent));
     245             :       }
     246         330 :       foundTransform = true;
     247             :     }
     248             :   }
     249         405 :   return foundTransform;
     250             : }
     251             : 
     252             : //----------------------------------------------------------------------
     253             : // nsSVGDisplayableFrame methods
     254             : 
     255             : void
     256           0 : nsSVGDisplayContainerFrame::PaintSVG(gfxContext& aContext,
     257             :                                      const gfxMatrix& aTransform,
     258             :                                      imgDrawingParams& aImgParams,
     259             :                                      const nsIntRect *aDirtyRect)
     260             : {
     261           0 :   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
     262             :                (mState & NS_FRAME_IS_NONDISPLAY) ||
     263             :                PresContext()->IsGlyph(),
     264             :                "If display lists are enabled, only painting of non-display "
     265             :                "SVG should take this code path");
     266             : 
     267           0 :   if (StyleEffects()->mOpacity == 0.0) {
     268           0 :     return;
     269             :   }
     270             : 
     271           0 :   gfxMatrix matrix = aTransform;
     272           0 :   if (GetContent()->IsSVGElement()) { // must check before cast
     273           0 :     matrix = static_cast<const nsSVGElement*>(GetContent())->
     274           0 :                PrependLocalTransformsTo(matrix, eChildToUserSpace);
     275           0 :     if (matrix.IsSingular()) {
     276           0 :       return;
     277             :     }
     278             :   }
     279             : 
     280           0 :   for (nsIFrame* kid = mFrames.FirstChild(); kid;
     281             :        kid = kid->GetNextSibling()) {
     282           0 :     gfxMatrix m = matrix;
     283             :     // PaintFrameWithEffects() expects the transform that is passed to it to
     284             :     // include the transform to the passed frame's user space, so add it:
     285           0 :     const nsIContent* content = kid->GetContent();
     286           0 :     if (content->IsSVGElement()) { // must check before cast
     287           0 :       const nsSVGElement* element = static_cast<const nsSVGElement*>(content);
     288           0 :       if (!element->HasValidDimensions()) {
     289           0 :         continue; // nothing to paint for kid
     290             :       }
     291           0 :       m = element->PrependLocalTransformsTo(m, eUserSpaceToParent);
     292           0 :       if (m.IsSingular()) {
     293           0 :         continue;
     294             :       }
     295             :     }
     296           0 :     nsSVGUtils::PaintFrameWithEffects(kid, aContext, m, aImgParams, aDirtyRect);
     297             :   }
     298             : }
     299             : 
     300             : nsIFrame*
     301           0 : nsSVGDisplayContainerFrame::GetFrameForPoint(const gfxPoint& aPoint)
     302             : {
     303           0 :   NS_ASSERTION(!NS_SVGDisplayListHitTestingEnabled() ||
     304             :                (mState & NS_FRAME_IS_NONDISPLAY),
     305             :                "If display lists are enabled, only hit-testing of a "
     306             :                "clipPath's contents should take this code path");
     307           0 :   return nsSVGUtils::HitTestChildren(this, aPoint);
     308             : }
     309             : 
     310             : void
     311          96 : nsSVGDisplayContainerFrame::ReflowSVG()
     312             : {
     313          96 :   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
     314             :                "This call is probably a wasteful mistake");
     315             : 
     316          96 :   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
     317             :              "ReflowSVG mechanism not designed for this");
     318             : 
     319          96 :   MOZ_ASSERT(!IsSVGOuterSVGFrame(), "Do not call on outer-<svg>");
     320             : 
     321          96 :   if (!nsSVGUtils::NeedsReflowSVG(this)) {
     322           2 :     return;
     323             :   }
     324             : 
     325             :   // If the NS_FRAME_FIRST_REFLOW bit has been removed from our parent frame,
     326             :   // then our outer-<svg> has previously had its initial reflow. In that case
     327             :   // we need to make sure that that bit has been removed from ourself _before_
     328             :   // recursing over our children to ensure that they know too. Otherwise, we
     329             :   // need to remove it _after_ recursing over our children so that they know
     330             :   // the initial reflow is currently underway.
     331             : 
     332          94 :   bool isFirstReflow = (mState & NS_FRAME_FIRST_REFLOW);
     333             : 
     334             :   bool outerSVGHasHadFirstReflow =
     335          94 :     (GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW) == 0;
     336             : 
     337          94 :   if (outerSVGHasHadFirstReflow) {
     338          44 :     mState &= ~NS_FRAME_FIRST_REFLOW; // tell our children
     339             :   }
     340             : 
     341         188 :   nsOverflowAreas overflowRects;
     342             : 
     343         246 :   for (nsIFrame* kid = mFrames.FirstChild(); kid;
     344             :        kid = kid->GetNextSibling()) {
     345         152 :     nsSVGDisplayableFrame* SVGFrame = do_QueryFrame(kid);
     346         152 :     if (SVGFrame) {
     347         139 :       MOZ_ASSERT(!(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY),
     348             :                  "Check for this explicitly in the |if|, then");
     349         139 :       kid->AddStateBits(mState & NS_FRAME_IS_DIRTY);
     350         139 :       SVGFrame->ReflowSVG();
     351             : 
     352             :       // We build up our child frame overflows here instead of using
     353             :       // nsLayoutUtils::UnionChildOverflow since SVG frame's all use the same
     354             :       // frame list, and we're iterating over that list now anyway.
     355         139 :       ConsiderChildOverflow(overflowRects, kid);
     356             :     } else {
     357             :       // Inside a non-display container frame, we might have some
     358             :       // SVGTextFrames.  We need to cause those to get reflowed in
     359             :       // case they are the target of a rendering observer.
     360          13 :       NS_ASSERTION(kid->GetStateBits() & NS_FRAME_IS_NONDISPLAY,
     361             :                    "expected kid to be a NS_FRAME_IS_NONDISPLAY frame");
     362          13 :       if (kid->GetStateBits() & NS_FRAME_IS_DIRTY) {
     363          13 :         nsSVGContainerFrame* container = do_QueryFrame(kid);
     364          13 :         if (container && container->GetContent()->IsSVGElement()) {
     365          13 :           ReflowSVGNonDisplayText(container);
     366             :         }
     367             :       }
     368             :     }
     369             :   }
     370             : 
     371             :   // <svg> can create an SVG viewport with an offset due to its
     372             :   // x/y/width/height attributes, and <use> can introduce an offset with an
     373             :   // empty mRect (any width/height is copied to an anonymous <svg> child).
     374             :   // Other than that containers should not set mRect since all other offsets
     375             :   // come from transforms, which are accounted for by nsDisplayTransform.
     376             :   // Note that we rely on |overflow:visible| to allow display list items to be
     377             :   // created for our children.
     378          94 :   MOZ_ASSERT(mContent->IsAnyOfSVGElements(nsGkAtoms::svg, nsGkAtoms::symbol) ||
     379             :              (mContent->IsSVGElement(nsGkAtoms::use) &&
     380             :               mRect.Size() == nsSize(0,0)) ||
     381             :              mRect.IsEqualEdges(nsRect()),
     382             :              "Only inner-<svg>/<use> is expected to have mRect set");
     383             : 
     384          94 :   if (isFirstReflow) {
     385             :     // Make sure we have our filter property (if any) before calling
     386             :     // FinishAndStoreOverflow (subsequent filter changes are handled off
     387             :     // nsChangeHint_UpdateEffects):
     388          50 :     nsSVGEffects::UpdateEffects(this);
     389             :   }
     390             : 
     391          94 :   FinishAndStoreOverflow(overflowRects, mRect.Size());
     392             : 
     393             :   // Remove state bits after FinishAndStoreOverflow so that it doesn't
     394             :   // invalidate on first reflow:
     395             :   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
     396          94 :               NS_FRAME_HAS_DIRTY_CHILDREN);
     397             : }
     398             : 
     399             : void
     400          25 : nsSVGDisplayContainerFrame::NotifySVGChanged(uint32_t aFlags)
     401             : {
     402          25 :   MOZ_ASSERT(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
     403             :              "Invalidation logic may need adjusting");
     404             : 
     405          25 :   nsSVGUtils::NotifyChildrenOfSVGChange(this, aFlags);
     406          25 : }
     407             : 
     408             : SVGBBox
     409           0 : nsSVGDisplayContainerFrame::GetBBoxContribution(
     410             :   const Matrix &aToBBoxUserspace,
     411             :   uint32_t aFlags)
     412             : {
     413           0 :   SVGBBox bboxUnion;
     414             : 
     415           0 :   nsIFrame* kid = mFrames.FirstChild();
     416           0 :   while (kid) {
     417           0 :     nsIContent *content = kid->GetContent();
     418           0 :     nsSVGDisplayableFrame* svgKid = do_QueryFrame(kid);
     419             :     // content could be a XUL element so check for an SVG element before casting
     420           0 :     if (svgKid && (!content->IsSVGElement() ||
     421           0 :                    static_cast<const nsSVGElement*>(content)->HasValidDimensions())) {
     422             : 
     423           0 :       gfxMatrix transform = gfx::ThebesMatrix(aToBBoxUserspace);
     424           0 :       if (content->IsSVGElement()) {
     425             :         transform = static_cast<nsSVGElement*>(content)->
     426           0 :                       PrependLocalTransformsTo(transform);
     427             :       }
     428             :       // We need to include zero width/height vertical/horizontal lines, so we have
     429             :       // to use UnionEdges.
     430           0 :       bboxUnion.UnionEdges(svgKid->GetBBoxContribution(gfx::ToMatrix(transform), aFlags));
     431             :     }
     432           0 :     kid = kid->GetNextSibling();
     433             :   }
     434             : 
     435           0 :   return bboxUnion;
     436             : }

Generated by: LCOV version 1.13