layout/svg/nsSVGPathGeometryFrame.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 "nsSVGPathGeometryFrame.h"
     9 // Keep others in (case-insensitive) order:
    10 #include "gfxContext.h"
    11 #include "gfxPlatform.h"
    12 #include "gfxSVGGlyphs.h"
    13 #include "nsDisplayList.h"
    14 #include "nsGkAtoms.h"
    15 #include "nsRenderingContext.h"
    16 #include "nsSVGEffects.h"
    17 #include "nsSVGIntegrationUtils.h"
    18 #include "nsSVGMarkerFrame.h"
    19 #include "nsSVGPathGeometryElement.h"
    20 #include "nsSVGUtils.h"
    21 #include "mozilla/ArrayUtils.h"
    22 #include "SVGAnimatedTransformList.h"
    23 #include "SVGGraphicsElement.h"
    25 using namespace mozilla;
    26 using namespace mozilla::gfx;
    28 //----------------------------------------------------------------------
    29 // Implementation
    31 nsIFrame*
    32 NS_NewSVGPathGeometryFrame(nsIPresShell* aPresShell,
    33                            nsStyleContext* aContext)
    34 {
    35   return new (aPresShell) nsSVGPathGeometryFrame(aContext);
    36 }
    38 NS_IMPL_FRAMEARENA_HELPERS(nsSVGPathGeometryFrame)
    40 //----------------------------------------------------------------------
    41 // nsQueryFrame methods
    43 NS_QUERYFRAME_HEAD(nsSVGPathGeometryFrame)
    44   NS_QUERYFRAME_ENTRY(nsISVGChildFrame)
    45   NS_QUERYFRAME_ENTRY(nsSVGPathGeometryFrame)
    46 NS_QUERYFRAME_TAIL_INHERITING(nsSVGPathGeometryFrameBase)
    48 //----------------------------------------------------------------------
    49 // Display list item:
    51 class nsDisplaySVGPathGeometry : public nsDisplayItem {
    52 public:
    53   nsDisplaySVGPathGeometry(nsDisplayListBuilder* aBuilder,
    54                            nsSVGPathGeometryFrame* aFrame)
    55     : nsDisplayItem(aBuilder, aFrame)
    56   {
    57     MOZ_COUNT_CTOR(nsDisplaySVGPathGeometry);
    58     NS_ABORT_IF_FALSE(aFrame, "Must have a frame!");
    59   }
    60 #ifdef NS_BUILD_REFCNT_LOGGING
    61   virtual ~nsDisplaySVGPathGeometry() {
    62     MOZ_COUNT_DTOR(nsDisplaySVGPathGeometry);
    63   }
    64 #endif
    66   NS_DISPLAY_DECL_NAME("nsDisplaySVGPathGeometry", TYPE_SVG_PATH_GEOMETRY)
    68   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    69                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
    70   virtual void Paint(nsDisplayListBuilder* aBuilder,
    71                      nsRenderingContext* aCtx);
    72 };
    74 void
    75 nsDisplaySVGPathGeometry::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
    76                                   HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
    77 {
    78   nsSVGPathGeometryFrame *frame = static_cast<nsSVGPathGeometryFrame*>(mFrame);
    79   nsPoint pointRelativeToReferenceFrame = aRect.Center();
    80   // ToReferenceFrame() includes frame->GetPosition(), our user space position.
    81   nsPoint userSpacePt = pointRelativeToReferenceFrame -
    82                           (ToReferenceFrame() - frame->GetPosition());
    83   if (frame->GetFrameForPoint(userSpacePt)) {
    84     aOutFrames->AppendElement(frame);
    85   }
    86 }
    88 void
    89 nsDisplaySVGPathGeometry::Paint(nsDisplayListBuilder* aBuilder,
    90                                 nsRenderingContext* aCtx)
    91 {
    92   // ToReferenceFrame includes our mRect offset, but painting takes
    93   // account of that too. To avoid double counting, we subtract that
    94   // here.
    95   nsPoint offset = ToReferenceFrame() - mFrame->GetPosition();
    97   aCtx->PushState();
    98   aCtx->Translate(offset);
    99   static_cast<nsSVGPathGeometryFrame*>(mFrame)->PaintSVG(aCtx, nullptr);
   100   aCtx->PopState();
   101 }
   103 //----------------------------------------------------------------------
   104 // nsIFrame methods
   106 void
   107 nsSVGPathGeometryFrame::Init(nsIContent* aContent,
   108                              nsIFrame* aParent,
   109                              nsIFrame* aPrevInFlow)
   110 {
   111   AddStateBits(aParent->GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD);
   112   nsSVGPathGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
   113 }
   115 nsresult
   116 nsSVGPathGeometryFrame::AttributeChanged(int32_t         aNameSpaceID,
   117                                          nsIAtom*        aAttribute,
   118                                          int32_t         aModType)
   119 {
   120   // We don't invalidate for transform changes (the layers code does that).
   121   // Also note that SVGTransformableElement::GetAttributeChangeHint will
   122   // return nsChangeHint_UpdateOverflow for "transform" attribute changes
   123   // and cause DoApplyRenderingChangeToTree to make the SchedulePaint call.
   125   if (aNameSpaceID == kNameSpaceID_None &&
   126       (static_cast<nsSVGPathGeometryElement*>
   127                   (mContent)->AttributeDefinesGeometry(aAttribute))) {
   128     nsSVGEffects::InvalidateRenderingObservers(this);
   129     nsSVGUtils::ScheduleReflowSVG(this);
   130   }
   131   return NS_OK;
   132 }
   134 /* virtual */ void
   135 nsSVGPathGeometryFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
   136 {
   137   nsSVGPathGeometryFrameBase::DidSetStyleContext(aOldStyleContext);
   139   if (aOldStyleContext) {
   140     float oldOpacity = aOldStyleContext->PeekStyleDisplay()->mOpacity;
   141     float newOpacity = StyleDisplay()->mOpacity;
   142     if (newOpacity != oldOpacity &&
   143         nsSVGUtils::CanOptimizeOpacity(this)) {
   144       // nsIFrame::BuildDisplayListForStackingContext() is not going to create an
   145       // nsDisplayOpacity display list item, so DLBI won't invalidate for us.
   146       InvalidateFrame();
   147     }
   148   }
   149 }
   151 nsIAtom *
   152 nsSVGPathGeometryFrame::GetType() const
   153 {
   154   return nsGkAtoms::svgPathGeometryFrame;
   155 }
   157 bool
   158 nsSVGPathGeometryFrame::IsSVGTransformed(gfx::Matrix *aOwnTransform,
   159                                          gfx::Matrix *aFromParentTransform) const
   160 {
   161   bool foundTransform = false;
   163   // Check if our parent has children-only transforms:
   164   nsIFrame *parent = GetParent();
   165   if (parent &&
   166       parent->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
   167     foundTransform = static_cast<nsSVGContainerFrame*>(parent)->
   168                        HasChildrenOnlyTransform(aFromParentTransform);
   169   }
   171   nsSVGElement *content = static_cast<nsSVGElement*>(mContent);
   172   nsSVGAnimatedTransformList* transformList =
   173     content->GetAnimatedTransformList();
   174   if ((transformList && transformList->HasTransform()) ||
   175       content->GetAnimateMotionTransform()) {
   176     if (aOwnTransform) {
   177       *aOwnTransform = gfx::ToMatrix(content->PrependLocalTransformsTo(gfxMatrix(),
   178                                   nsSVGElement::eUserSpaceToParent));
   179     }
   180     foundTransform = true;
   181   }
   182   return foundTransform;
   183 }
   185 void
   186 nsSVGPathGeometryFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
   187                                          const nsRect&           aDirtyRect,
   188                                          const nsDisplayListSet& aLists)
   189 {
   190   if (!static_cast<const nsSVGElement*>(mContent)->HasValidDimensions()) {
   191     return;
   192   }
   193   aLists.Content()->AppendNewToTop(
   194     new (aBuilder) nsDisplaySVGPathGeometry(aBuilder, this));
   195 }
   197 //----------------------------------------------------------------------
   198 // nsISVGChildFrame methods
   200 nsresult
   201 nsSVGPathGeometryFrame::PaintSVG(nsRenderingContext *aContext,
   202                                  const nsIntRect *aDirtyRect,
   203                                  nsIFrame* aTransformRoot)
   204 {
   205   if (!StyleVisibility()->IsVisible())
   206     return NS_OK;
   208   uint32_t paintOrder = StyleSVG()->mPaintOrder;
   209   if (paintOrder == NS_STYLE_PAINT_ORDER_NORMAL) {
   210     Render(aContext, eRenderFill | eRenderStroke, aTransformRoot);
   211     PaintMarkers(aContext);
   212   } else {
   213     while (paintOrder) {
   214       uint32_t component =
   215         paintOrder & ((1 << NS_STYLE_PAINT_ORDER_BITWIDTH) - 1);
   216       switch (component) {
   217         case NS_STYLE_PAINT_ORDER_FILL:
   218           Render(aContext, eRenderFill, aTransformRoot);
   219           break;
   220         case NS_STYLE_PAINT_ORDER_STROKE:
   221           Render(aContext, eRenderStroke, aTransformRoot);
   222           break;
   223         case NS_STYLE_PAINT_ORDER_MARKERS:
   224           PaintMarkers(aContext);
   225           break;
   226       }
   227       paintOrder >>= NS_STYLE_PAINT_ORDER_BITWIDTH;
   228     }
   229   }
   231   return NS_OK;
   232 }
   234 nsIFrame*
   235 nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint)
   236 {
   237   gfxMatrix canvasTM = GetCanvasTM(FOR_HIT_TESTING);
   238   if (canvasTM.IsSingular()) {
   239     return nullptr;
   240   }
   241   uint16_t fillRule, hitTestFlags;
   242   if (GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) {
   243     hitTestFlags = SVG_HIT_TEST_FILL;
   244     fillRule = StyleSVG()->mClipRule;
   245   } else {
   246     hitTestFlags = GetHitTestFlags();
   247     // XXX once bug 614732 is fixed, aPoint won't need any conversion in order
   248     // to compare it with mRect.
   249     nsPoint point =
   250       nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext());
   251     if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) &&
   252                           !mRect.Contains(point)))
   253       return nullptr;
   254     fillRule = StyleSVG()->mFillRule;
   255   }
   257   bool isHit = false;
   259   nsRefPtr<gfxContext> tmpCtx =
   260     new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
   262   GeneratePath(tmpCtx, ToMatrix(canvasTM));
   263   gfxPoint userSpacePoint =
   264     tmpCtx->DeviceToUser(gfxPoint(PresContext()->AppUnitsToGfxUnits(aPoint.x),
   265                                   PresContext()->AppUnitsToGfxUnits(aPoint.y)));
   267   if (fillRule == NS_STYLE_FILL_RULE_EVENODD)
   268     tmpCtx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   269   else
   270     tmpCtx->SetFillRule(gfxContext::FILL_RULE_WINDING);
   272   if (hitTestFlags & SVG_HIT_TEST_FILL)
   273     isHit = tmpCtx->PointInFill(userSpacePoint);
   274   if (!isHit && (hitTestFlags & SVG_HIT_TEST_STROKE)) {
   275     nsSVGUtils::SetupCairoStrokeGeometry(this, tmpCtx);
   276     // tmpCtx's matrix may have transformed by SetupCairoStrokeGeometry
   277     // if there is a non-scaling stroke. We need to transform userSpacePoint
   278     // so that everything is using the same co-ordinate system.
   279     userSpacePoint =
   280       nsSVGUtils::GetStrokeTransform(this).Invert().Transform(userSpacePoint);
   281     isHit = tmpCtx->PointInStroke(userSpacePoint);
   282   }
   284   if (isHit && nsSVGUtils::HitTestClip(this, aPoint))
   285     return this;
   287   return nullptr;
   288 }
   290 nsRect
   291 nsSVGPathGeometryFrame::GetCoveredRegion()
   292 {
   293   return nsSVGUtils::TransformFrameRectToOuterSVG(
   294            mRect, GetCanvasTM(FOR_OUTERSVG_TM), PresContext());
   295 }
   297 void
   298 nsSVGPathGeometryFrame::ReflowSVG()
   299 {
   300   NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this),
   301                "This call is probably a wasteful mistake");
   303   NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY),
   304                     "ReflowSVG mechanism not designed for this");
   306   if (!nsSVGUtils::NeedsReflowSVG(this)) {
   307     return;
   308   }
   310   uint32_t flags = nsSVGUtils::eBBoxIncludeFill |
   311                    nsSVGUtils::eBBoxIncludeStroke |
   312                    nsSVGUtils::eBBoxIncludeMarkers;
   313   // Our "visual" overflow rect needs to be valid for building display lists
   314   // for hit testing, which means that for certain values of 'pointer-events'
   315   // it needs to include the geometry of the fill or stroke even when the fill/
   316   // stroke don't actually render (e.g. when stroke="none" or
   317   // stroke-opacity="0"). GetHitTestFlags() accounts for 'pointer-events'.
   318   uint16_t hitTestFlags = GetHitTestFlags();
   319   if ((hitTestFlags & SVG_HIT_TEST_FILL)) {
   320    flags |= nsSVGUtils::eBBoxIncludeFillGeometry;
   321   }
   322   if ((hitTestFlags & SVG_HIT_TEST_STROKE)) {
   323    flags |= nsSVGUtils::eBBoxIncludeStrokeGeometry;
   324   }
   326   // We'd like to just pass the identity matrix to GetBBoxContribution, but if
   327   // this frame's user space size is _very_ large/small then the extents we
   328   // obtain below might have overflowed or otherwise be broken. This would
   329   // cause us to end up with a broken mRect and visual overflow rect and break
   330   // painting of this frame. This is particularly noticeable if the transforms
   331   // between us and our nsSVGOuterSVGFrame scale this frame to a reasonable
   332   // size. To avoid this we sadly have to do extra work to account for the
   333   // transforms between us and our nsSVGOuterSVGFrame, even though the
   334   // overwhelming number of SVGs will never have this problem.
   335   // XXX Will Azure eventually save us from having to do this?
   336   gfxSize scaleFactors = GetCanvasTM(FOR_OUTERSVG_TM).ScaleFactors(true);
   337   bool applyScaling = fabs(scaleFactors.width) >= 1e-6 &&
   338                       fabs(scaleFactors.height) >= 1e-6;
   339   gfx::Matrix scaling;
   340   if (applyScaling) {
   341     scaling.Scale(scaleFactors.width, scaleFactors.height);
   342   }
   343   gfxRect extent = GetBBoxContribution(scaling, flags).ToThebesRect();
   344   if (applyScaling) {
   345     extent.Scale(1 / scaleFactors.width, 1 / scaleFactors.height);
   346   }
   347   mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent,
   348             PresContext()->AppUnitsPerCSSPixel());
   350   if (mState & NS_FRAME_FIRST_REFLOW) {
   351     // Make sure we have our filter property (if any) before calling
   352     // FinishAndStoreOverflow (subsequent filter changes are handled off
   353     // nsChangeHint_UpdateEffects):
   354     nsSVGEffects::UpdateEffects(this);
   355   }
   357   nsRect overflow = nsRect(nsPoint(0,0), mRect.Size());
   358   nsOverflowAreas overflowAreas(overflow, overflow);
   359   FinishAndStoreOverflow(overflowAreas, mRect.Size());
   361   mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY |
   362               NS_FRAME_HAS_DIRTY_CHILDREN);
   364   // Invalidate, but only if this is not our first reflow (since if it is our
   365   // first reflow then we haven't had our first paint yet).
   366   if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
   367     InvalidateFrame();
   368   }
   369 }
   371 void
   372 nsSVGPathGeometryFrame::NotifySVGChanged(uint32_t aFlags)
   373 {
   374   NS_ABORT_IF_FALSE(aFlags & (TRANSFORM_CHANGED | COORD_CONTEXT_CHANGED),
   375                     "Invalidation logic may need adjusting");
   377   // Changes to our ancestors may affect how we render when we are rendered as
   378   // part of our ancestor (specifically, if our coordinate context changes size
   379   // and we have percentage lengths defining our geometry, then we need to be
   380   // reflowed). However, ancestor changes cannot affect how we render when we
   381   // are rendered as part of any rendering observers that we may have.
   382   // Therefore no need to notify rendering observers here.
   384   // Don't try to be too smart trying to avoid the ScheduleReflowSVG calls
   385   // for the stroke properties examined below. Checking HasStroke() is not
   386   // enough, since what we care about is whether we include the stroke in our
   387   // overflow rects or not, and we sometimes deliberately include stroke
   388   // when it's not visible. See the complexities of GetBBoxContribution.
   390   if (aFlags & COORD_CONTEXT_CHANGED) {
   391     // Stroke currently contributes to our mRect, which is why we have to take
   392     // account of stroke-width here. Note that we do not need to take account
   393     // of stroke-dashoffset since, although that can have a percentage value
   394     // that is resolved against our coordinate context, it does not affect our
   395     // mRect.
   396     if (static_cast<nsSVGPathGeometryElement*>(mContent)->GeometryDependsOnCoordCtx() ||
   397         StyleSVG()->mStrokeWidth.HasPercent()) {
   398       nsSVGUtils::ScheduleReflowSVG(this);
   399     }
   400   }
   402   if ((aFlags & TRANSFORM_CHANGED) &&
   403       StyleSVGReset()->mVectorEffect ==
   404         NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
   405     // Stroke currently contributes to our mRect, and our stroke depends on
   406     // the transform to our outer-<svg> if |vector-effect:non-scaling-stroke|.
   407     nsSVGUtils::ScheduleReflowSVG(this);
   408   } 
   409 }
   411 SVGBBox
   412 nsSVGPathGeometryFrame::GetBBoxContribution(const Matrix &aToBBoxUserspace,
   413                                             uint32_t aFlags)
   414 {
   415   SVGBBox bbox;
   417   if (aToBBoxUserspace.IsSingular()) {
   418     // XXX ReportToConsole
   419     return bbox;
   420   }
   422   nsRefPtr<gfxContext> tmpCtx =
   423     new gfxContext(gfxPlatform::GetPlatform()->ScreenReferenceSurface());
   425   GeneratePath(tmpCtx, aToBBoxUserspace);
   426   tmpCtx->IdentityMatrix();
   428   // Be careful when replacing the following logic to get the fill and stroke
   429   // extents independently (instead of computing the stroke extents from the
   430   // path extents). You may think that you can just use the stroke extents if
   431   // there is both a fill and a stroke. In reality it's necessary to calculate
   432   // both the fill and stroke extents, and take the union of the two. There are
   433   // two reasons for this:
   434   //
   435   // # Due to stroke dashing, in certain cases the fill extents could actually
   436   //   extend outside the stroke extents.
   437   // # If the stroke is very thin, cairo won't paint any stroke, and so the
   438   //   stroke bounds that it will return will be empty.
   440   gfxRect pathExtents = tmpCtx->GetUserPathExtent();
   442   // Account for fill:
   443   if ((aFlags & nsSVGUtils::eBBoxIncludeFillGeometry) ||
   444       ((aFlags & nsSVGUtils::eBBoxIncludeFill) &&
   445        StyleSVG()->mFill.mType != eStyleSVGPaintType_None)) {
   446     bbox = pathExtents;
   447   }
   449   // Account for stroke:
   450   if ((aFlags & nsSVGUtils::eBBoxIncludeStrokeGeometry) ||
   451       ((aFlags & nsSVGUtils::eBBoxIncludeStroke) &&
   452        nsSVGUtils::HasStroke(this))) {
   453     // We can't use tmpCtx->GetUserStrokeExtent() since it doesn't work for
   454     // device space extents. Instead we approximate the stroke extents from
   455     // pathExtents using PathExtentsToMaxStrokeExtents.
   456     if (pathExtents.Width() <= 0 && pathExtents.Height() <= 0) {
   457       // We have a zero length path, but it may still have non-empty stroke
   458       // bounds depending on the value of stroke-linecap. We need to fix up
   459       // pathExtents before it can be used with PathExtentsToMaxStrokeExtents
   460       // though, because if pathExtents is empty, its position will not have
   461       // been set. Happily we can use tmpCtx->GetUserStrokeExtent() to find
   462       // the center point of the extents even though it gets the extents wrong.
   463       nsSVGUtils::SetupCairoStrokeBBoxGeometry(this, tmpCtx);
   464       pathExtents.MoveTo(tmpCtx->GetUserStrokeExtent().Center());
   465       pathExtents.SizeTo(0, 0);
   466     }
   467     bbox.UnionEdges(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents,
   468                                                               this,
   469                                                               ThebesMatrix(aToBBoxUserspace)));
   470   }
   472   // Account for markers:
   473   if ((aFlags & nsSVGUtils::eBBoxIncludeMarkers) != 0 &&
   474       static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
   476     float strokeWidth = nsSVGUtils::GetStrokeWidth(this);
   477     MarkerProperties properties = GetMarkerProperties(this);
   479     if (properties.MarkersExist()) {
   480       nsTArray<nsSVGMark> marks;
   481       static_cast<nsSVGPathGeometryElement*>(mContent)->GetMarkPoints(&marks);
   482       uint32_t num = marks.Length();
   484       // These are in the same order as the nsSVGMark::Type constants.
   485       nsSVGMarkerFrame* markerFrames[] = {
   486         properties.GetMarkerStartFrame(),
   487         properties.GetMarkerMidFrame(),
   488         properties.GetMarkerEndFrame(),
   489       };
   490       PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
   492       for (uint32_t i = 0; i < num; i++) {
   493         nsSVGMark& mark = marks[i];
   494         nsSVGMarkerFrame* frame = markerFrames[mark.type];
   495         if (frame) {
   496           SVGBBox mbbox =
   497             frame->GetMarkBBoxContribution(aToBBoxUserspace, aFlags, this,
   498                                            &marks[i], strokeWidth);
   499           bbox.UnionEdges(mbbox);
   500         }
   501       }
   502     }
   503   }
   505   return bbox;
   506 }
   508 //----------------------------------------------------------------------
   509 // nsSVGPathGeometryFrame methods:
   511 gfxMatrix
   512 nsSVGPathGeometryFrame::GetCanvasTM(uint32_t aFor, nsIFrame* aTransformRoot)
   513 {
   514   if (!(GetStateBits() & NS_FRAME_IS_NONDISPLAY) && !aTransformRoot) {
   515     if ((aFor == FOR_PAINTING && NS_SVGDisplayListPaintingEnabled()) ||
   516         (aFor == FOR_HIT_TESTING && NS_SVGDisplayListHitTestingEnabled())) {
   517       return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(this);
   518     }
   519   }
   521   NS_ASSERTION(mParent, "null parent");
   523   nsSVGContainerFrame *parent = static_cast<nsSVGContainerFrame*>(mParent);
   524   dom::SVGGraphicsElement *content = static_cast<dom::SVGGraphicsElement*>(mContent);
   526   return content->PrependLocalTransformsTo(
   527       this == aTransformRoot ? gfxMatrix() :
   528                                parent->GetCanvasTM(aFor, aTransformRoot));
   529 }
   531 nsSVGPathGeometryFrame::MarkerProperties
   532 nsSVGPathGeometryFrame::GetMarkerProperties(nsSVGPathGeometryFrame *aFrame)
   533 {
   534   NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
   536   MarkerProperties result;
   537   const nsStyleSVG *style = aFrame->StyleSVG();
   538   result.mMarkerStart =
   539     nsSVGEffects::GetMarkerProperty(style->mMarkerStart, aFrame,
   540                                     nsSVGEffects::MarkerBeginProperty());
   541   result.mMarkerMid =
   542     nsSVGEffects::GetMarkerProperty(style->mMarkerMid, aFrame,
   543                                     nsSVGEffects::MarkerMiddleProperty());
   544   result.mMarkerEnd =
   545     nsSVGEffects::GetMarkerProperty(style->mMarkerEnd, aFrame,
   546                                     nsSVGEffects::MarkerEndProperty());
   547   return result;
   548 }
   550 nsSVGMarkerFrame *
   551 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerStartFrame()
   552 {
   553   if (!mMarkerStart)
   554     return nullptr;
   555   return static_cast<nsSVGMarkerFrame *>
   556     (mMarkerStart->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   557 }
   559 nsSVGMarkerFrame *
   560 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerMidFrame()
   561 {
   562   if (!mMarkerMid)
   563     return nullptr;
   564   return static_cast<nsSVGMarkerFrame *>
   565     (mMarkerMid->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   566 }
   568 nsSVGMarkerFrame *
   569 nsSVGPathGeometryFrame::MarkerProperties::GetMarkerEndFrame()
   570 {
   571   if (!mMarkerEnd)
   572     return nullptr;
   573   return static_cast<nsSVGMarkerFrame *>
   574     (mMarkerEnd->GetReferencedFrame(nsGkAtoms::svgMarkerFrame, nullptr));
   575 }
   577 void
   578 nsSVGPathGeometryFrame::Render(nsRenderingContext *aContext,
   579                                uint32_t aRenderComponents,
   580                                nsIFrame* aTransformRoot)
   581 {
   582   gfxContext *gfx = aContext->ThebesContext();
   584   uint16_t renderMode = SVGAutoRenderState::GetRenderMode(aContext);
   586   switch (StyleSVG()->mShapeRendering) {
   587   case NS_STYLE_SHAPE_RENDERING_OPTIMIZESPEED:
   588   case NS_STYLE_SHAPE_RENDERING_CRISPEDGES:
   589     gfx->SetAntialiasMode(gfxContext::MODE_ALIASED);
   590     break;
   591   default:
   592     gfx->SetAntialiasMode(gfxContext::MODE_COVERAGE);
   593     break;
   594   }
   596   if (renderMode != SVGAutoRenderState::NORMAL) {
   597     NS_ABORT_IF_FALSE(renderMode == SVGAutoRenderState::CLIP ||
   598                       renderMode == SVGAutoRenderState::CLIP_MASK,
   599                       "Unknown render mode");
   601     // In the case that |renderMode == SVGAutoRenderState::CLIP| then we don't
   602     // use the path we generate here until further up the call stack when
   603     // nsSVGClipPathFrame::Clip calls gfxContext::Clip. That's a problem for
   604     // Moz2D which emits paths in user space (unlike cairo which emits paths in
   605     // device space). gfxContext has hacks to deal with code changing the
   606     // transform then using the current path when it is backed by Moz2D, but
   607     // Moz2D itself does not since that would fundamentally go against its API.
   608     // Therefore we do not want to Save()/Restore() the gfxContext here in the
   609     // SVGAutoRenderState::CLIP case since that would block us from killing off
   610     // gfxContext and using Moz2D directly. Not bothering to Save()/Restore()
   611     // is actually okay, since we know that doesn't matter in the
   612     // SVGAutoRenderState::CLIP case (at least for the current implementation).
   613     gfxContextMatrixAutoSaveRestore autoSaveRestore;
   614     // For now revent back to doing the save even for CLIP to fix bug 959128.
   615     // Undo in bug 987193.
   616     //if (renderMode != SVGAutoRenderState::CLIP) {
   617       autoSaveRestore.SetContext(gfx);
   618     //}
   620     GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
   622     // We used to call gfx->Restore() here, since for the
   623     // SVGAutoRenderState::CLIP case it is important to leave the fill rule
   624     // that we set below untouched so that the value is still set when return
   625     // to gfxContext::Clip() further up the call stack. Since we no longer
   626     // call gfx->Save() in the SVGAutoRenderState::CLIP case we don't need to
   627     // worry that autoSaveRestore will delay the Restore() call for the
   628     // CLIP_MASK case until we exit this function.
   630     gfxContext::FillRule oldFillRull = gfx->CurrentFillRule();
   632     if (StyleSVG()->mClipRule == NS_STYLE_FILL_RULE_EVENODD)
   633       gfx->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
   634     else
   635       gfx->SetFillRule(gfxContext::FILL_RULE_WINDING);
   637     if (renderMode == SVGAutoRenderState::CLIP_MASK) {
   638       gfx->SetColor(gfxRGBA(1.0f, 1.0f, 1.0f, 1.0f));
   639       gfx->Fill();
   640       gfx->SetFillRule(oldFillRull); // restore, but only for CLIP_MASK
   641       gfx->NewPath();
   642     }
   644     return;
   645   }
   647   gfxContextAutoSaveRestore autoSaveRestore(gfx);
   649   GeneratePath(gfx, ToMatrix(GetCanvasTM(FOR_PAINTING, aTransformRoot)));
   651   gfxTextContextPaint *contextPaint =
   652     (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
   654   if ((aRenderComponents & eRenderFill) &&
   655       nsSVGUtils::SetupCairoFillPaint(this, gfx, contextPaint)) {
   656     gfx->Fill();
   657   }
   659   if ((aRenderComponents & eRenderStroke) &&
   660        nsSVGUtils::SetupCairoStroke(this, gfx, contextPaint)) {
   661     gfx->Stroke();
   662   }
   664   gfx->NewPath();
   665 }
   667 void
   668 nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
   669                                      const Matrix &aTransform)
   670 {
   671   if (aTransform.IsSingular()) {
   672     aContext->IdentityMatrix();
   673     aContext->NewPath();
   674     return;
   675   }
   677   aContext->MultiplyAndNudgeToIntegers(ThebesMatrix(aTransform));
   679   // Hack to let SVGPathData::ConstructPath know if we have square caps:
   680   const nsStyleSVG* style = StyleSVG();
   681   if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
   682     aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
   683   }
   685   aContext->NewPath();
   686   static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
   687 }
   689 void
   690 nsSVGPathGeometryFrame::PaintMarkers(nsRenderingContext* aContext)
   691 {
   692   gfxTextContextPaint *contextPaint =
   693     (gfxTextContextPaint*)aContext->GetUserData(&gfxTextContextPaint::sUserDataKey);
   695   if (static_cast<nsSVGPathGeometryElement*>(mContent)->IsMarkable()) {
   696     MarkerProperties properties = GetMarkerProperties(this);
   698     if (properties.MarkersExist()) {
   699       float strokeWidth = nsSVGUtils::GetStrokeWidth(this, contextPaint);
   701       nsTArray<nsSVGMark> marks;
   702       static_cast<nsSVGPathGeometryElement*>
   703                  (mContent)->GetMarkPoints(&marks);
   705       uint32_t num = marks.Length();
   706       if (num) {
   707         // These are in the same order as the nsSVGMark::Type constants.
   708         nsSVGMarkerFrame* markerFrames[] = {
   709           properties.GetMarkerStartFrame(),
   710           properties.GetMarkerMidFrame(),
   711           properties.GetMarkerEndFrame(),
   712         };
   713         PR_STATIC_ASSERT(MOZ_ARRAY_LENGTH(markerFrames) == nsSVGMark::eTypeCount);
   715         for (uint32_t i = 0; i < num; i++) {
   716           nsSVGMark& mark = marks[i];
   717           nsSVGMarkerFrame* frame = markerFrames[mark.type];
   718           if (frame) {
   719             frame->PaintMark(aContext, this, &mark, strokeWidth);
   720           }
   721         }
   722       }
   723     }
   724   }
   725 }
   727 uint16_t
   728 nsSVGPathGeometryFrame::GetHitTestFlags()
   729 {
   730   return nsSVGUtils::GetGeometryHitTestFlags(this);
   731 }

mercurial