layout/svg/nsSVGUtils.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 // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
     8 #include "nsSVGUtils.h"
     9 #include <algorithm>
    11 // Keep others in (case-insensitive) order:
    12 #include "gfx2DGlue.h"
    13 #include "gfxContext.h"
    14 #include "gfxMatrix.h"
    15 #include "gfxPlatform.h"
    16 #include "gfxRect.h"
    17 #include "gfxUtils.h"
    18 #include "mozilla/gfx/2D.h"
    19 #include "mozilla/Preferences.h"
    20 #include "nsCSSFrameConstructor.h"
    21 #include "nsDisplayList.h"
    22 #include "nsFilterInstance.h"
    23 #include "nsFrameList.h"
    24 #include "nsGkAtoms.h"
    25 #include "nsIContent.h"
    26 #include "nsIDocument.h"
    27 #include "nsIFrame.h"
    28 #include "nsIPresShell.h"
    29 #include "nsISVGChildFrame.h"
    30 #include "nsPresContext.h"
    31 #include "nsRenderingContext.h"
    32 #include "nsStyleCoord.h"
    33 #include "nsStyleStruct.h"
    34 #include "nsSVGClipPathFrame.h"
    35 #include "nsSVGContainerFrame.h"
    36 #include "nsSVGEffects.h"
    37 #include "nsSVGFilterPaintCallback.h"
    38 #include "nsSVGForeignObjectFrame.h"
    39 #include "gfxSVGGlyphs.h"
    40 #include "nsSVGInnerSVGFrame.h"
    41 #include "nsSVGIntegrationUtils.h"
    42 #include "nsSVGLength2.h"
    43 #include "nsSVGMaskFrame.h"
    44 #include "nsSVGOuterSVGFrame.h"
    45 #include "mozilla/dom/SVGPathElement.h"
    46 #include "nsSVGPathGeometryElement.h"
    47 #include "nsSVGPathGeometryFrame.h"
    48 #include "nsSVGPaintServerFrame.h"
    49 #include "mozilla/dom/SVGSVGElement.h"
    50 #include "nsTextFrame.h"
    51 #include "SVGContentUtils.h"
    52 #include "mozilla/unused.h"
    54 using namespace mozilla;
    55 using namespace mozilla::dom;
    56 using namespace mozilla::gfx;
    58 static bool sSVGDisplayListHitTestingEnabled;
    59 static bool sSVGDisplayListPaintingEnabled;
    61 bool
    62 NS_SVGDisplayListHitTestingEnabled()
    63 {
    64   return sSVGDisplayListHitTestingEnabled;
    65 }
    67 bool
    68 NS_SVGDisplayListPaintingEnabled()
    69 {
    70   return sSVGDisplayListPaintingEnabled;
    71 }
    73 // we only take the address of this:
    74 static mozilla::gfx::UserDataKey sSVGAutoRenderStateKey;
    76 SVGAutoRenderState::SVGAutoRenderState(nsRenderingContext *aContext,
    77                                        RenderMode aMode
    78                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
    79   : mContext(aContext)
    80   , mOriginalRenderState(nullptr)
    81   , mMode(aMode)
    82   , mPaintingToWindow(false)
    83 {
    84   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
    85   mOriginalRenderState = aContext->RemoveUserData(&sSVGAutoRenderStateKey);
    86   // We always remove ourselves from aContext before it dies, so
    87   // passing nullptr as the destroy function is okay.
    88   aContext->AddUserData(&sSVGAutoRenderStateKey, this, nullptr);
    89 }
    91 SVGAutoRenderState::~SVGAutoRenderState()
    92 {
    93   mContext->RemoveUserData(&sSVGAutoRenderStateKey);
    94   if (mOriginalRenderState) {
    95     mContext->AddUserData(&sSVGAutoRenderStateKey, mOriginalRenderState, nullptr);
    96   }
    97 }
    99 void
   100 SVGAutoRenderState::SetPaintingToWindow(bool aPaintingToWindow)
   101 {
   102   mPaintingToWindow = aPaintingToWindow;
   103 }
   105 /* static */ SVGAutoRenderState::RenderMode
   106 SVGAutoRenderState::GetRenderMode(nsRenderingContext *aContext)
   107 {
   108   void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
   109   if (state) {
   110     return static_cast<SVGAutoRenderState*>(state)->mMode;
   111   }
   112   return NORMAL;
   113 }
   115 /* static */ bool
   116 SVGAutoRenderState::IsPaintingToWindow(nsRenderingContext *aContext)
   117 {
   118   void *state = aContext->GetUserData(&sSVGAutoRenderStateKey);
   119   if (state) {
   120     return static_cast<SVGAutoRenderState*>(state)->mPaintingToWindow;
   121   }
   122   return false;
   123 }
   125 void
   126 nsSVGUtils::Init()
   127 {
   128   Preferences::AddBoolVarCache(&sSVGDisplayListHitTestingEnabled,
   129                                "svg.display-lists.hit-testing.enabled");
   131   Preferences::AddBoolVarCache(&sSVGDisplayListPaintingEnabled,
   132                                "svg.display-lists.painting.enabled");
   133 }
   135 nsSVGDisplayContainerFrame*
   136 nsSVGUtils::GetNearestSVGViewport(nsIFrame *aFrame)
   137 {
   138   NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
   139   if (aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
   140     return nullptr;
   141   }
   142   while ((aFrame = aFrame->GetParent())) {
   143     NS_ASSERTION(aFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
   144     if (aFrame->GetType() == nsGkAtoms::svgInnerSVGFrame ||
   145         aFrame->GetType() == nsGkAtoms::svgOuterSVGFrame) {
   146       return do_QueryFrame(aFrame);
   147     }
   148   }
   149   NS_NOTREACHED("This is not reached. It's only needed to compile.");
   150   return nullptr;
   151 }
   153 nsRect
   154 nsSVGUtils::GetPostFilterVisualOverflowRect(nsIFrame *aFrame,
   155                                             const nsRect &aPreFilterRect)
   156 {
   157   NS_ABORT_IF_FALSE(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT,
   158                     "Called on invalid frame type");
   160   nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
   161   if (!property || !property->ReferencesValidResources()) {
   162     return aPreFilterRect;
   163   }
   165   return nsFilterInstance::GetPostFilterBounds(aFrame, nullptr, &aPreFilterRect);
   166 }
   168 bool
   169 nsSVGUtils::OuterSVGIsCallingReflowSVG(nsIFrame *aFrame)
   170 {
   171   return GetOuterSVGFrame(aFrame)->IsCallingReflowSVG();
   172 }
   174 bool
   175 nsSVGUtils::AnyOuterSVGIsCallingReflowSVG(nsIFrame* aFrame)
   176 {
   177   nsSVGOuterSVGFrame* outer = GetOuterSVGFrame(aFrame);
   178   do {
   179     if (outer->IsCallingReflowSVG()) {
   180       return true;
   181     }
   182     outer = GetOuterSVGFrame(outer->GetParent());
   183   } while (outer);
   184   return false;
   185 }
   187 void
   188 nsSVGUtils::ScheduleReflowSVG(nsIFrame *aFrame)
   189 {
   190   NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
   191                     "Passed bad frame!");
   193   // If this is triggered, the callers should be fixed to call us before
   194   // ReflowSVG is called. If we try to mark dirty bits on frames while we're
   195   // in the process of removing them, things will get messed up.
   196   NS_ASSERTION(!OuterSVGIsCallingReflowSVG(aFrame),
   197                "Do not call under nsISVGChildFrame::ReflowSVG!");
   199   // We don't call nsSVGEffects::InvalidateRenderingObservers here because
   200   // we should only be called under InvalidateAndScheduleReflowSVG (which
   201   // calls InvalidateBounds) or nsSVGDisplayContainerFrame::InsertFrames
   202   // (at which point the frame has no observers).
   204   if (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) {
   205     return;
   206   }
   208   if (aFrame->GetStateBits() &
   209       (NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW)) {
   210     // Nothing to do if we're already dirty, or if the outer-<svg>
   211     // hasn't yet had its initial reflow.
   212     return;
   213   }
   215   nsSVGOuterSVGFrame *outerSVGFrame = nullptr;
   217   // We must not add dirty bits to the nsSVGOuterSVGFrame or else
   218   // PresShell::FrameNeedsReflow won't work when we pass it in below.
   219   if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
   220     outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(aFrame);
   221   } else {
   222     aFrame->AddStateBits(NS_FRAME_IS_DIRTY);
   224     nsIFrame *f = aFrame->GetParent();
   225     while (f && !(f->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
   226       if (f->GetStateBits() &
   227           (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN)) {
   228         return;
   229       }
   230       f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
   231       f = f->GetParent();
   232       NS_ABORT_IF_FALSE(f->IsFrameOfType(nsIFrame::eSVG),
   233                         "NS_STATE_IS_OUTER_SVG check above not valid!");
   234     }
   236     outerSVGFrame = static_cast<nsSVGOuterSVGFrame*>(f);
   238     NS_ABORT_IF_FALSE(outerSVGFrame &&
   239                       outerSVGFrame->GetType() == nsGkAtoms::svgOuterSVGFrame,
   240                       "Did not find nsSVGOuterSVGFrame!");
   241   }
   243   if (outerSVGFrame->GetStateBits() & NS_FRAME_IN_REFLOW) {
   244     // We're currently under an nsSVGOuterSVGFrame::Reflow call so there is no
   245     // need to call PresShell::FrameNeedsReflow, since we have an
   246     // nsSVGOuterSVGFrame::DidReflow call pending.
   247     return;
   248   }
   250   nsFrameState dirtyBit =
   251     (outerSVGFrame == aFrame ? NS_FRAME_IS_DIRTY : NS_FRAME_HAS_DIRTY_CHILDREN);
   253   aFrame->PresContext()->PresShell()->FrameNeedsReflow(
   254     outerSVGFrame, nsIPresShell::eResize, dirtyBit);
   255 }
   257 bool
   258 nsSVGUtils::NeedsReflowSVG(nsIFrame *aFrame)
   259 {
   260   NS_ABORT_IF_FALSE(aFrame->IsFrameOfType(nsIFrame::eSVG),
   261                     "SVG uses bits differently!");
   263   // The flags we test here may change, hence why we have this separate
   264   // function.
   265   return NS_SUBTREE_DIRTY(aFrame);
   266 }
   268 void
   269 nsSVGUtils::NotifyAncestorsOfFilterRegionChange(nsIFrame *aFrame)
   270 {
   271   NS_ABORT_IF_FALSE(!(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG),
   272                     "Not expecting to be called on the outer SVG Frame");
   274   aFrame = aFrame->GetParent();
   276   while (aFrame) {
   277     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)
   278       return;
   280     nsSVGFilterProperty *property = nsSVGEffects::GetFilterProperty(aFrame);
   281     if (property) {
   282       property->Invalidate();
   283     }
   284     aFrame = aFrame->GetParent();
   285   }
   286 }
   288 float
   289 nsSVGUtils::ObjectSpace(const gfxRect &aRect, const nsSVGLength2 *aLength)
   290 {
   291   float axis;
   293   switch (aLength->GetCtxType()) {
   294   case SVGContentUtils::X:
   295     axis = aRect.Width();
   296     break;
   297   case SVGContentUtils::Y:
   298     axis = aRect.Height();
   299     break;
   300   case SVGContentUtils::XY:
   301     axis = float(SVGContentUtils::ComputeNormalizedHypotenuse(
   302                    aRect.Width(), aRect.Height()));
   303     break;
   304   default:
   305     NS_NOTREACHED("unexpected ctx type");
   306     axis = 0.0f;
   307     break;
   308   }
   309   if (aLength->IsPercentage()) {
   310     // Multiply first to avoid precision errors:
   311     return axis * aLength->GetAnimValInSpecifiedUnits() / 100;
   312   }
   313   return aLength->GetAnimValue(static_cast<SVGSVGElement*>(nullptr)) * axis;
   314 }
   316 float
   317 nsSVGUtils::UserSpace(nsSVGElement *aSVGElement, const nsSVGLength2 *aLength)
   318 {
   319   return aLength->GetAnimValue(aSVGElement);
   320 }
   322 float
   323 nsSVGUtils::UserSpace(nsIFrame *aNonSVGContext, const nsSVGLength2 *aLength)
   324 {
   325   return aLength->GetAnimValue(aNonSVGContext);
   326 }
   328 nsSVGOuterSVGFrame *
   329 nsSVGUtils::GetOuterSVGFrame(nsIFrame *aFrame)
   330 {
   331   while (aFrame) {
   332     if (aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG) {
   333       return static_cast<nsSVGOuterSVGFrame*>(aFrame);
   334     }
   335     aFrame = aFrame->GetParent();
   336   }
   338   return nullptr;
   339 }
   341 nsIFrame*
   342 nsSVGUtils::GetOuterSVGFrameAndCoveredRegion(nsIFrame* aFrame, nsRect* aRect)
   343 {
   344   nsISVGChildFrame* svg = do_QueryFrame(aFrame);
   345   if (!svg)
   346     return nullptr;
   347   *aRect = (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ?
   348              nsRect(0, 0, 0, 0) : svg->GetCoveredRegion();
   349   return GetOuterSVGFrame(aFrame);
   350 }
   352 gfxMatrix
   353 nsSVGUtils::GetCanvasTM(nsIFrame *aFrame, uint32_t aFor,
   354                         nsIFrame* aTransformRoot)
   355 {
   356   // XXX yuck, we really need a common interface for GetCanvasTM
   358   if (!aFrame->IsFrameOfType(nsIFrame::eSVG)) {
   359     return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   360   }
   362   if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) &&
   363       !aTransformRoot) {
   364     if ((aFor == nsISVGChildFrame::FOR_PAINTING &&
   365          NS_SVGDisplayListPaintingEnabled()) ||
   366         (aFor == nsISVGChildFrame::FOR_HIT_TESTING &&
   367          NS_SVGDisplayListHitTestingEnabled())) {
   368       return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   369     }
   370   }
   372   nsIAtom* type = aFrame->GetType();
   373   if (type == nsGkAtoms::svgForeignObjectFrame) {
   374     return static_cast<nsSVGForeignObjectFrame*>(aFrame)->
   375         GetCanvasTM(aFor, aTransformRoot);
   376   }
   377   if (type == nsGkAtoms::svgOuterSVGFrame) {
   378     return nsSVGIntegrationUtils::GetCSSPxToDevPxMatrix(aFrame);
   379   }
   381   nsSVGContainerFrame *containerFrame = do_QueryFrame(aFrame);
   382   if (containerFrame) {
   383     return containerFrame->GetCanvasTM(aFor, aTransformRoot);
   384   }
   386   return static_cast<nsSVGPathGeometryFrame*>(aFrame)->
   387       GetCanvasTM(aFor, aTransformRoot);
   388 }
   390 gfxMatrix
   391 nsSVGUtils::GetUserToCanvasTM(nsIFrame *aFrame, uint32_t aFor)
   392 {
   393   NS_ASSERTION(aFor == nsISVGChildFrame::FOR_OUTERSVG_TM,
   394                "Unexpected aFor?");
   396   nsISVGChildFrame* svgFrame = do_QueryFrame(aFrame);
   397   NS_ASSERTION(svgFrame, "bad frame");
   399   gfxMatrix tm;
   400   if (svgFrame) {
   401     nsSVGElement *content = static_cast<nsSVGElement*>(aFrame->GetContent());
   402     tm = content->PrependLocalTransformsTo(
   403                     GetCanvasTM(aFrame->GetParent(), aFor),
   404                     nsSVGElement::eUserSpaceToParent);
   405   }
   406   return tm;
   407 }
   409 void 
   410 nsSVGUtils::NotifyChildrenOfSVGChange(nsIFrame *aFrame, uint32_t aFlags)
   411 {
   412   nsIFrame *kid = aFrame->GetFirstPrincipalChild();
   414   while (kid) {
   415     nsISVGChildFrame* SVGFrame = do_QueryFrame(kid);
   416     if (SVGFrame) {
   417       SVGFrame->NotifySVGChanged(aFlags); 
   418     } else {
   419       NS_ASSERTION(kid->IsFrameOfType(nsIFrame::eSVG) || kid->IsSVGText(),
   420                    "SVG frame expected");
   421       // recurse into the children of container frames e.g. <clipPath>, <mask>
   422       // in case they have child frames with transformation matrices
   423       if (kid->IsFrameOfType(nsIFrame::eSVG)) {
   424         NotifyChildrenOfSVGChange(kid, aFlags);
   425       }
   426     }
   427     kid = kid->GetNextSibling();
   428   }
   429 }
   431 // ************************************************************
   433 class SVGPaintCallback : public nsSVGFilterPaintCallback
   434 {
   435 public:
   436   virtual void Paint(nsRenderingContext *aContext, nsIFrame *aTarget,
   437                      const nsIntRect* aDirtyRect,
   438                      nsIFrame* aTransformRoot) MOZ_OVERRIDE
   439   {
   440     nsISVGChildFrame *svgChildFrame = do_QueryFrame(aTarget);
   441     NS_ASSERTION(svgChildFrame, "Expected SVG frame here");
   443     nsIntRect* dirtyRect = nullptr;
   444     nsIntRect tmpDirtyRect;
   446     // aDirtyRect is in user-space pixels, we need to convert to
   447     // outer-SVG-frame-relative device pixels.
   448     if (aDirtyRect) {
   449       gfxMatrix userToDeviceSpace =
   450         nsSVGUtils::GetCanvasTM(aTarget, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   451       if (userToDeviceSpace.IsSingular()) {
   452         return;
   453       }
   454       gfxRect dirtyBounds = userToDeviceSpace.TransformBounds(
   455         gfxRect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height));
   456       dirtyBounds.RoundOut();
   457       if (gfxUtils::GfxRectToIntRect(dirtyBounds, &tmpDirtyRect)) {
   458         dirtyRect = &tmpDirtyRect;
   459       }
   460     }
   462     svgChildFrame->PaintSVG(aContext, dirtyRect, aTransformRoot);
   463   }
   464 };
   466 void
   467 nsSVGUtils::PaintFrameWithEffects(nsRenderingContext *aContext,
   468                                   const nsIntRect *aDirtyRect,
   469                                   nsIFrame *aFrame,
   470                                   nsIFrame *aTransformRoot)
   471 {
   472   NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() ||
   473                (aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY) ||
   474                aFrame->PresContext()->IsGlyph(),
   475                "If display lists are enabled, only painting of non-display "
   476                "SVG should take this code path");
   478   nsISVGChildFrame *svgChildFrame = do_QueryFrame(aFrame);
   479   if (!svgChildFrame)
   480     return;
   482   float opacity = aFrame->StyleDisplay()->mOpacity;
   483   if (opacity == 0.0f)
   484     return;
   486   const nsIContent* content = aFrame->GetContent();
   487   if (content->IsSVG() &&
   488       !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   489     return;
   490   }
   492   /* Properties are added lazily and may have been removed by a restyle,
   493      so make sure all applicable ones are set again. */
   495   nsSVGEffects::EffectProperties effectProperties =
   496     nsSVGEffects::GetEffectProperties(aFrame);
   498   bool isOK = effectProperties.HasNoFilterOrHasValidFilter();
   500   if (aDirtyRect &&
   501       !(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   502     // Here we convert aFrame's paint bounds to outer-<svg> device space,
   503     // compare it to aDirtyRect, and return early if they don't intersect.
   504     // We don't do this optimization for nondisplay SVG since nondisplay
   505     // SVG doesn't maintain bounds/overflow rects.
   506     nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
   507     if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   508         aFrame->IsSVGText()) {
   509       // Unlike containers, leaf frames do not include GetPosition() in
   510       // GetCanvasTM().
   511       overflowRect = overflowRect + aFrame->GetPosition();
   512     }
   513     int32_t appUnitsPerDevPx = aFrame->PresContext()->AppUnitsPerDevPixel();
   514     gfxMatrix tm = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   515     if (aFrame->IsFrameOfType(nsIFrame::eSVG | nsIFrame::eSVGContainer)) {
   516       gfx::Matrix childrenOnlyTM;
   517       if (static_cast<nsSVGContainerFrame*>(aFrame)->
   518             HasChildrenOnlyTransform(&childrenOnlyTM)) {
   519         // Undo the children-only transform:
   520         tm = ThebesMatrix(childrenOnlyTM).Invert() * tm;
   521       }
   522     }
   523     nsIntRect bounds = TransformFrameRectToOuterSVG(overflowRect,
   524                          tm, aFrame->PresContext()).
   525                            ToOutsidePixels(appUnitsPerDevPx);
   526     if (!aDirtyRect->Intersects(bounds)) {
   527       return;
   528     }
   529   }
   531   /* SVG defines the following rendering model:
   532    *
   533    *  1. Render fill
   534    *  2. Render stroke
   535    *  3. Render markers
   536    *  4. Apply filter
   537    *  5. Apply clipping, masking, group opacity
   538    *
   539    * We follow this, but perform a couple of optimizations:
   540    *
   541    * + Use cairo's clipPath when representable natively (single object
   542    *   clip region).
   543    *
   544    * + Merge opacity and masking if both used together.
   545    */
   547   if (opacity != 1.0f && CanOptimizeOpacity(aFrame))
   548     opacity = 1.0f;
   550   gfxContext *gfx = aContext->ThebesContext();
   551   bool complexEffects = false;
   553   nsSVGClipPathFrame *clipPathFrame = effectProperties.GetClipPathFrame(&isOK);
   554   nsSVGMaskFrame *maskFrame = effectProperties.GetMaskFrame(&isOK);
   556   bool isTrivialClip = clipPathFrame ? clipPathFrame->IsTrivial() : true;
   558   if (!isOK) {
   559     // Some resource is invalid. We shouldn't paint anything.
   560     return;
   561   }
   563   gfxMatrix matrix;
   564   if (clipPathFrame || maskFrame)
   565     matrix = GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot);
   567   /* Check if we need to do additional operations on this child's
   568    * rendering, which necessitates rendering into another surface. */
   569   if (opacity != 1.0f || maskFrame || (clipPathFrame && !isTrivialClip)
   570       || aFrame->StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) {
   571     complexEffects = true;
   572     gfx->Save();
   573     if (!(aFrame->GetStateBits() & NS_FRAME_IS_NONDISPLAY)) {
   574       // aFrame has a valid visual overflow rect, so clip to it before calling
   575       // PushGroup() to minimize the size of the surfaces we'll composite:
   576       gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(gfx);
   577       gfx->Multiply(GetCanvasTM(aFrame, nsISVGChildFrame::FOR_PAINTING, aTransformRoot));
   578       nsRect overflowRect = aFrame->GetVisualOverflowRectRelativeToSelf();
   579       if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   580           aFrame->IsSVGText()) {
   581         // Unlike containers, leaf frames do not include GetPosition() in
   582         // GetCanvasTM().
   583         overflowRect = overflowRect + aFrame->GetPosition();
   584       }
   585       aContext->IntersectClip(overflowRect);
   586     }
   587     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   588   }
   590   /* If this frame has only a trivial clipPath, set up cairo's clipping now so
   591    * we can just do normal painting and get it clipped appropriately.
   592    */
   593   if (clipPathFrame && isTrivialClip) {
   594     gfx->Save();
   595     clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   596   }
   598   /* Paint the child */
   599   if (effectProperties.HasValidFilter()) {
   600     nsRegion* dirtyRegion = nullptr;
   601     nsRegion tmpDirtyRegion;
   602     if (aDirtyRect) {
   603       // aDirtyRect is in outer-<svg> device pixels, but the filter code needs
   604       // it in frame space.
   605       gfxMatrix userToDeviceSpace =
   606         GetUserToCanvasTM(aFrame, nsISVGChildFrame::FOR_OUTERSVG_TM);
   607       if (userToDeviceSpace.IsSingular()) {
   608         return;
   609       }
   610       gfxMatrix deviceToUserSpace = userToDeviceSpace;
   611       deviceToUserSpace.Invert();
   612       gfxRect dirtyBounds = deviceToUserSpace.TransformBounds(
   613                               gfxRect(aDirtyRect->x, aDirtyRect->y,
   614                                       aDirtyRect->width, aDirtyRect->height));
   615       tmpDirtyRegion =
   616         nsLayoutUtils::RoundGfxRectToAppRect(
   617           dirtyBounds, aFrame->PresContext()->AppUnitsPerCSSPixel()) -
   618         aFrame->GetPosition();
   619       dirtyRegion = &tmpDirtyRegion;
   620     }
   621     SVGPaintCallback paintCallback;
   622     nsFilterInstance::PaintFilteredFrame(aContext, aFrame, &paintCallback,
   623                                          dirtyRegion, aTransformRoot);
   624   } else {
   625     svgChildFrame->PaintSVG(aContext, aDirtyRect, aTransformRoot);
   626   }
   628   if (clipPathFrame && isTrivialClip) {
   629     gfx->Restore();
   630   }
   632   /* No more effects, we're done. */
   633   if (!complexEffects)
   634     return;
   636   gfx->PopGroupToSource();
   638   nsRefPtr<gfxPattern> maskSurface =
   639     maskFrame ? maskFrame->ComputeMaskAlpha(aContext, aFrame,
   640                                             matrix, opacity) : nullptr;
   642   nsRefPtr<gfxPattern> clipMaskSurface;
   643   if (clipPathFrame && !isTrivialClip) {
   644     gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   646     nsresult rv = clipPathFrame->ClipPaint(aContext, aFrame, matrix);
   647     clipMaskSurface = gfx->PopGroup();
   649     if (NS_SUCCEEDED(rv) && clipMaskSurface) {
   650       // Still more set after clipping, so clip to another surface
   651       if (maskSurface || opacity != 1.0f) {
   652         gfx->PushGroup(gfxContentType::COLOR_ALPHA);
   653         gfx->Mask(clipMaskSurface);
   654         gfx->PopGroupToSource();
   655       } else {
   656         gfx->Mask(clipMaskSurface);
   657       }
   658     }
   659   }
   661   if (maskSurface) {
   662     gfx->Mask(maskSurface);
   663   } else if (opacity != 1.0f) {
   664     gfx->Paint(opacity);
   665   }
   667   gfx->Restore();
   668 }
   670 bool
   671 nsSVGUtils::HitTestClip(nsIFrame *aFrame, const nsPoint &aPoint)
   672 {
   673   nsSVGEffects::EffectProperties props =
   674     nsSVGEffects::GetEffectProperties(aFrame);
   675   if (!props.mClipPath)
   676     return true;
   678   bool isOK = true;
   679   nsSVGClipPathFrame *clipPathFrame = props.GetClipPathFrame(&isOK);
   680   if (!isOK) {
   681     // clipPath is not a valid resource, so nothing gets painted, so
   682     // hit-testing must fail.
   683     return false;
   684   }
   685   if (!clipPathFrame) {
   686     // clipPath doesn't exist, ignore it.
   687     return true;
   688   }
   690   return clipPathFrame->ClipHitTest(aFrame, GetCanvasTM(aFrame,
   691                                     nsISVGChildFrame::FOR_HIT_TESTING), aPoint);
   692 }
   694 nsIFrame *
   695 nsSVGUtils::HitTestChildren(nsIFrame *aFrame, const nsPoint &aPoint)
   696 {
   697   // Traverse the list in reverse order, so that if we get a hit we know that's
   698   // the topmost frame that intersects the point; then we can just return it.
   699   nsIFrame* result = nullptr;
   700   for (nsIFrame* current = aFrame->PrincipalChildList().LastChild();
   701        current;
   702        current = current->GetPrevSibling()) {
   703     nsISVGChildFrame* SVGFrame = do_QueryFrame(current);
   704     if (SVGFrame) {
   705       const nsIContent* content = current->GetContent();
   706       if (content->IsSVG() &&
   707           !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   708         continue;
   709       }
   710       result = SVGFrame->GetFrameForPoint(aPoint);
   711       if (result)
   712         break;
   713     }
   714   }
   716   if (result && !HitTestClip(aFrame, aPoint))
   717     result = nullptr;
   719   return result;
   720 }
   722 nsRect
   723 nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames)
   724 {
   725   nsRect rect;
   727   for (nsIFrame* kid = aFrames.FirstChild();
   728        kid;
   729        kid = kid->GetNextSibling()) {
   730     nsISVGChildFrame* child = do_QueryFrame(kid);
   731     if (child) {
   732       nsRect childRect = child->GetCoveredRegion();
   733       rect.UnionRect(rect, childRect);
   734     }
   735   }
   737   return rect;
   738 }
   740 nsPoint
   741 nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint,
   742                                                const gfxMatrix& aFrameToCanvasTM,
   743                                                nsPresContext* aPresContext)
   744 {
   745   NS_ABORT_IF_FALSE(!aFrameToCanvasTM.IsSingular(),
   746                     "Callers must not pass a singular matrix");
   747   gfxMatrix canvasDevToFrameUserSpace = aFrameToCanvasTM;
   748   canvasDevToFrameUserSpace.Invert();
   749   gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) /
   750     aPresContext->AppUnitsPerDevPixel();
   751   gfxPoint userPt = canvasDevToFrameUserSpace.Transform(devPt);
   752   gfxPoint appPt = (userPt * aPresContext->AppUnitsPerCSSPixel()).Round();
   753   userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
   754   userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX));
   755   // now guaranteed to be safe:
   756   return nsPoint(nscoord(userPt.x), nscoord(userPt.y));
   757 }
   759 nsRect
   760 nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect,
   761                                          const gfxMatrix& aMatrix,
   762                                          nsPresContext* aPresContext)
   763 {
   764   gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height);
   765   r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel());
   766   return nsLayoutUtils::RoundGfxRectToAppRect(
   767     aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel());
   768 }
   770 gfxIntSize
   771 nsSVGUtils::ConvertToSurfaceSize(const gfxSize& aSize,
   772                                  bool *aResultOverflows)
   773 {
   774   gfxIntSize surfaceSize(ClampToInt(ceil(aSize.width)), ClampToInt(ceil(aSize.height)));
   776   *aResultOverflows = surfaceSize.width != ceil(aSize.width) ||
   777     surfaceSize.height != ceil(aSize.height);
   779   if (!gfxASurface::CheckSurfaceSize(surfaceSize)) {
   780     surfaceSize.width = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
   781                                surfaceSize.width);
   782     surfaceSize.height = std::min(NS_SVG_OFFSCREEN_MAX_DIMENSION,
   783                                 surfaceSize.height);
   784     *aResultOverflows = true;
   785   }
   787   return surfaceSize;
   788 }
   790 bool
   791 nsSVGUtils::HitTestRect(const gfx::Matrix &aMatrix,
   792                         float aRX, float aRY, float aRWidth, float aRHeight,
   793                         float aX, float aY)
   794 {
   795   gfx::Rect rect(aRX, aRY, aRWidth, aRHeight);
   796   if (rect.IsEmpty() || aMatrix.IsSingular()) {
   797     return false;
   798   }
   799   gfx::Matrix toRectSpace = aMatrix;
   800   toRectSpace.Invert();
   801   gfx::Point p = toRectSpace * gfx::Point(aX, aY);
   802   return rect.x <= p.x && p.x <= rect.XMost() &&
   803          rect.y <= p.y && p.y <= rect.YMost();
   804 }
   806 gfxRect
   807 nsSVGUtils::GetClipRectForFrame(nsIFrame *aFrame,
   808                                 float aX, float aY, float aWidth, float aHeight)
   809 {
   810   const nsStyleDisplay* disp = aFrame->StyleDisplay();
   812   if (!(disp->mClipFlags & NS_STYLE_CLIP_RECT)) {
   813     NS_ASSERTION(disp->mClipFlags == NS_STYLE_CLIP_AUTO,
   814                  "We don't know about this type of clip.");
   815     return gfxRect(aX, aY, aWidth, aHeight);
   816   }
   818   if (disp->mOverflowX == NS_STYLE_OVERFLOW_HIDDEN ||
   819       disp->mOverflowY == NS_STYLE_OVERFLOW_HIDDEN) {
   821     nsIntRect clipPxRect =
   822       disp->mClip.ToOutsidePixels(aFrame->PresContext()->AppUnitsPerDevPixel());
   823     gfxRect clipRect =
   824       gfxRect(clipPxRect.x, clipPxRect.y, clipPxRect.width, clipPxRect.height);
   826     if (NS_STYLE_CLIP_RIGHT_AUTO & disp->mClipFlags) {
   827       clipRect.width = aWidth - clipRect.X();
   828     }
   829     if (NS_STYLE_CLIP_BOTTOM_AUTO & disp->mClipFlags) {
   830       clipRect.height = aHeight - clipRect.Y();
   831     }
   833     if (disp->mOverflowX != NS_STYLE_OVERFLOW_HIDDEN) {
   834       clipRect.x = aX;
   835       clipRect.width = aWidth;
   836     }
   837     if (disp->mOverflowY != NS_STYLE_OVERFLOW_HIDDEN) {
   838       clipRect.y = aY;
   839       clipRect.height = aHeight;
   840     }
   842     return clipRect;
   843   }
   844   return gfxRect(aX, aY, aWidth, aHeight);
   845 }
   847 void
   848 nsSVGUtils::SetClipRect(gfxContext *aContext,
   849                         const gfxMatrix &aCTM,
   850                         const gfxRect &aRect)
   851 {
   852   if (aCTM.IsSingular())
   853     return;
   855   gfxContextMatrixAutoSaveRestore matrixAutoSaveRestore(aContext);
   856   aContext->Multiply(aCTM);
   857   aContext->Clip(aRect);
   858 }
   860 gfxRect
   861 nsSVGUtils::GetBBox(nsIFrame *aFrame, uint32_t aFlags)
   862 {
   863   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
   864     aFrame = aFrame->GetParent();
   865   }
   866   gfxRect bbox;
   867   nsISVGChildFrame *svg = do_QueryFrame(aFrame);
   868   if (svg || aFrame->IsSVGText()) {
   869     // It is possible to apply a gradient, pattern, clipping path, mask or
   870     // filter to text. When one of these facilities is applied to text
   871     // the bounding box is the entire text element in all
   872     // cases.
   873     if (aFrame->IsSVGText()) {
   874       nsIFrame* ancestor = GetFirstNonAAncestorFrame(aFrame);
   875       if (ancestor && ancestor->IsSVGText()) {
   876         while (ancestor->GetType() != nsGkAtoms::svgTextFrame) {
   877           ancestor = ancestor->GetParent();
   878         }
   879       }
   880       svg = do_QueryFrame(ancestor);
   881     }
   882     nsIContent* content = aFrame->GetContent();
   883     if (content->IsSVG() &&
   884         !static_cast<const nsSVGElement*>(content)->HasValidDimensions()) {
   885       return bbox;
   886     }
   887     gfxMatrix matrix;
   888     if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
   889       // The spec says getBBox "Returns the tight bounding box in *current user
   890       // space*". So we should really be doing this for all elements, but that
   891       // needs investigation to check that we won't break too much content.
   892       // NOTE: When changing this to apply to other frame types, make sure to
   893       // also update nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset.
   894       NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
   895       nsSVGElement *element = static_cast<nsSVGElement*>(content);
   896       matrix = element->PrependLocalTransformsTo(matrix,
   897                           nsSVGElement::eChildToUserSpace);
   898     }
   899     return svg->GetBBoxContribution(ToMatrix(matrix), aFlags).ToThebesRect();
   900   }
   901   return nsSVGIntegrationUtils::GetSVGBBoxForNonSVGFrame(aFrame);
   902 }
   904 gfxPoint
   905 nsSVGUtils::FrameSpaceInCSSPxToUserSpaceOffset(nsIFrame *aFrame)
   906 {
   907   if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
   908     // The user space for non-SVG frames is defined as the bounding box of the
   909     // frame's border-box rects over all continuations.
   910     return gfxPoint();
   911   }
   913   // Leaf frames apply their own offset inside their user space.
   914   if (aFrame->IsFrameOfType(nsIFrame::eSVGGeometry) ||
   915       aFrame->IsSVGText()) {
   916     return nsLayoutUtils::RectToGfxRect(aFrame->GetRect(),
   917                                          nsPresContext::AppUnitsPerCSSPixel()).TopLeft();
   918   }
   920   // For foreignObject frames, nsSVGUtils::GetBBox applies their local
   921   // transform, so we need to do the same here.
   922   if (aFrame->GetType() == nsGkAtoms::svgForeignObjectFrame) {
   923     gfxMatrix transform = static_cast<nsSVGElement*>(aFrame->GetContent())->
   924         PrependLocalTransformsTo(gfxMatrix(),
   925                                  nsSVGElement::eChildToUserSpace);
   926     NS_ASSERTION(!transform.HasNonTranslation(), "we're relying on this being an offset-only transform");
   927     return transform.GetTranslation();
   928   }
   930   return gfxPoint();
   931 }
   933 gfxRect
   934 nsSVGUtils::GetRelativeRect(uint16_t aUnits, const nsSVGLength2 *aXYWH,
   935                             const gfxRect &aBBox, nsIFrame *aFrame)
   936 {
   937   float x, y, width, height;
   938   if (aUnits == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
   939     x = aBBox.X() + ObjectSpace(aBBox, &aXYWH[0]);
   940     y = aBBox.Y() + ObjectSpace(aBBox, &aXYWH[1]);
   941     width = ObjectSpace(aBBox, &aXYWH[2]);
   942     height = ObjectSpace(aBBox, &aXYWH[3]);
   943   } else {
   944     x = UserSpace(aFrame, &aXYWH[0]);
   945     y = UserSpace(aFrame, &aXYWH[1]);
   946     width = UserSpace(aFrame, &aXYWH[2]);
   947     height = UserSpace(aFrame, &aXYWH[3]);
   948   }
   949   return gfxRect(x, y, width, height);
   950 }
   952 bool
   953 nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame)
   954 {
   955   if (!(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
   956     return false;
   957   }
   958   nsIAtom *type = aFrame->GetType();
   959   if (type != nsGkAtoms::svgImageFrame &&
   960       type != nsGkAtoms::svgPathGeometryFrame) {
   961     return false;
   962   }
   963   if (aFrame->StyleSVGReset()->HasFilters()) {
   964     return false;
   965   }
   966   // XXX The SVG WG is intending to allow fill, stroke and markers on <image>
   967   if (type == nsGkAtoms::svgImageFrame) {
   968     return true;
   969   }
   970   const nsStyleSVG *style = aFrame->StyleSVG();
   971   if (style->HasMarker()) {
   972     return false;
   973   }
   974   if (!style->HasFill() || !HasStroke(aFrame)) {
   975     return true;
   976   }
   977   return false;
   978 }
   980 gfxMatrix
   981 nsSVGUtils::AdjustMatrixForUnits(const gfxMatrix &aMatrix,
   982                                  nsSVGEnum *aUnits,
   983                                  nsIFrame *aFrame)
   984 {
   985   if (aFrame &&
   986       aUnits->GetAnimValue() == SVG_UNIT_TYPE_OBJECTBOUNDINGBOX) {
   987     gfxRect bbox = GetBBox(aFrame);
   988     return gfxMatrix().Scale(bbox.Width(), bbox.Height()) *
   989            gfxMatrix().Translate(gfxPoint(bbox.X(), bbox.Y())) *
   990            aMatrix;
   991   }
   992   return aMatrix;
   993 }
   995 nsIFrame*
   996 nsSVGUtils::GetFirstNonAAncestorFrame(nsIFrame* aStartFrame)
   997 {
   998   for (nsIFrame *ancestorFrame = aStartFrame; ancestorFrame;
   999        ancestorFrame = ancestorFrame->GetParent()) {
  1000     if (ancestorFrame->GetType() != nsGkAtoms::svgAFrame) {
  1001       return ancestorFrame;
  1004   return nullptr;
  1007 gfxMatrix
  1008 nsSVGUtils::GetStrokeTransform(nsIFrame *aFrame)
  1010   if (aFrame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
  1011     aFrame = aFrame->GetParent();
  1014   if (aFrame->StyleSVGReset()->mVectorEffect ==
  1015       NS_STYLE_VECTOR_EFFECT_NON_SCALING_STROKE) {
  1017     nsIContent *content = aFrame->GetContent();
  1018     NS_ABORT_IF_FALSE(content->IsSVG(), "bad cast");
  1020     // a non-scaling stroke is in the screen co-ordinate
  1021     // space rather so we need to invert the transform
  1022     // to the screen co-ordinate space to get there.
  1023     // See http://www.w3.org/TR/SVGTiny12/painting.html#NonScalingStroke
  1024     gfx::Matrix transform = SVGContentUtils::GetCTM(
  1025                               static_cast<nsSVGElement*>(content), true);
  1026     if (!transform.IsSingular()) {
  1027       transform.Invert();
  1028       return ThebesMatrix(transform);
  1031   return gfxMatrix();
  1034 // The logic here comes from _cairo_stroke_style_max_distance_from_path
  1035 static gfxRect
  1036 PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1037                               nsIFrame* aFrame,
  1038                               double aStyleExpansionFactor,
  1039                               const gfxMatrix& aMatrix)
  1041   double style_expansion =
  1042     aStyleExpansionFactor * nsSVGUtils::GetStrokeWidth(aFrame);
  1044   gfxMatrix matrix = aMatrix;
  1045   matrix.Multiply(nsSVGUtils::GetStrokeTransform(aFrame));
  1047   double dx = style_expansion * (fabs(matrix.xx) + fabs(matrix.xy));
  1048   double dy = style_expansion * (fabs(matrix.yy) + fabs(matrix.yx));
  1050   gfxRect strokeExtents = aPathExtents;
  1051   strokeExtents.Inflate(dx, dy);
  1052   return strokeExtents;
  1055 /*static*/ gfxRect
  1056 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1057                                           nsTextFrame* aFrame,
  1058                                           const gfxMatrix& aMatrix)
  1060   NS_ASSERTION(aFrame->IsSVGText(), "expected an nsTextFrame for SVG text");
  1061   return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix);
  1064 /*static*/ gfxRect
  1065 nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents,
  1066                                           nsSVGPathGeometryFrame* aFrame,
  1067                                           const gfxMatrix& aMatrix)
  1069   double styleExpansionFactor = 0.5;
  1071   if (static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
  1072     const nsStyleSVG* style = aFrame->StyleSVG();
  1074     if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
  1075       styleExpansionFactor = M_SQRT1_2;
  1078     if (style->mStrokeLinejoin == NS_STYLE_STROKE_LINEJOIN_MITER &&
  1079         styleExpansionFactor < style->mStrokeMiterlimit &&
  1080         aFrame->GetContent()->Tag() != nsGkAtoms::line) {
  1081       styleExpansionFactor = style->mStrokeMiterlimit;
  1085   return ::PathExtentsToMaxStrokeExtents(aPathExtents,
  1086                                          aFrame,
  1087                                          styleExpansionFactor,
  1088                                          aMatrix);
  1091 // ----------------------------------------------------------------------
  1093 /* static */ nscolor
  1094 nsSVGUtils::GetFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
  1095                                     nsStyleSVGPaint nsStyleSVG::*aFillOrStroke)
  1097   const nsStyleSVGPaint &paint = aStyleContext->StyleSVG()->*aFillOrStroke;
  1098   nsStyleContext *styleIfVisited = aStyleContext->GetStyleIfVisited();
  1099   bool isServer = paint.mType == eStyleSVGPaintType_Server ||
  1100                   paint.mType == eStyleSVGPaintType_ContextFill ||
  1101                   paint.mType == eStyleSVGPaintType_ContextStroke;
  1102   nscolor color = isServer ? paint.mFallbackColor : paint.mPaint.mColor;
  1103   if (styleIfVisited) {
  1104     const nsStyleSVGPaint &paintIfVisited =
  1105       styleIfVisited->StyleSVG()->*aFillOrStroke;
  1106     // To prevent Web content from detecting if a user has visited a URL
  1107     // (via URL loading triggered by paint servers or performance
  1108     // differences between paint servers or between a paint server and a
  1109     // color), we do not allow whether links are visited to change which
  1110     // paint server is used or switch between paint servers and simple
  1111     // colors.  A :visited style may only override a simple color with
  1112     // another simple color.
  1113     if (paintIfVisited.mType == eStyleSVGPaintType_Color &&
  1114         paint.mType == eStyleSVGPaintType_Color) {
  1115       nscolor colors[2] = { color, paintIfVisited.mPaint.mColor };
  1116       return nsStyleContext::CombineVisitedColors(
  1117                colors, aStyleContext->RelevantLinkVisited());
  1120   return color;
  1123 static void
  1124 SetupFallbackOrPaintColor(gfxContext *aContext, nsStyleContext *aStyleContext,
  1125                           nsStyleSVGPaint nsStyleSVG::*aFillOrStroke,
  1126                           float aOpacity)
  1128   nscolor color = nsSVGUtils::GetFallbackOrPaintColor(
  1129     aContext, aStyleContext, aFillOrStroke);
  1131   aContext->SetColor(gfxRGBA(NS_GET_R(color)/255.0,
  1132                              NS_GET_G(color)/255.0,
  1133                              NS_GET_B(color)/255.0,
  1134                              NS_GET_A(color)/255.0 * aOpacity));
  1137 static float
  1138 MaybeOptimizeOpacity(nsIFrame *aFrame, float aFillOrStrokeOpacity)
  1140   float opacity = aFrame->StyleDisplay()->mOpacity;
  1141   if (opacity < 1 && nsSVGUtils::CanOptimizeOpacity(aFrame)) {
  1142     return aFillOrStrokeOpacity * opacity;
  1144   return aFillOrStrokeOpacity;
  1147 /* static */ bool
  1148 nsSVGUtils::SetupContextPaint(gfxContext *aContext,
  1149                               gfxTextContextPaint *aContextPaint,
  1150                               const nsStyleSVGPaint &aPaint,
  1151                               float aOpacity)
  1153   nsRefPtr<gfxPattern> pattern;
  1155   if (!aContextPaint) {
  1156     return false;
  1159   switch (aPaint.mType) {
  1160     case eStyleSVGPaintType_ContextFill:
  1161       pattern = aContextPaint->GetFillPattern(aOpacity, aContext->CurrentMatrix());
  1162       break;
  1163     case eStyleSVGPaintType_ContextStroke:
  1164       pattern = aContextPaint->GetStrokePattern(aOpacity, aContext->CurrentMatrix());
  1165       break;
  1166     default:
  1167       return false;
  1170   if (!pattern) {
  1171     return false;
  1174   aContext->SetPattern(pattern);
  1176   return true;
  1179 bool
  1180 nsSVGUtils::SetupCairoFillPaint(nsIFrame *aFrame, gfxContext* aContext,
  1181                                 gfxTextContextPaint *aContextPaint)
  1183   const nsStyleSVG* style = aFrame->StyleSVG();
  1184   if (style->mFill.mType == eStyleSVGPaintType_None)
  1185     return false;
  1187   if (style->mFillRule == NS_STYLE_FILL_RULE_EVENODD)
  1188     aContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
  1189   else
  1190     aContext->SetFillRule(gfxContext::FILL_RULE_WINDING);
  1192   float opacity = MaybeOptimizeOpacity(aFrame,
  1193                                        GetOpacity(style->mFillOpacitySource,
  1194                                                   style->mFillOpacity,
  1195                                                   aContextPaint));
  1196   nsSVGPaintServerFrame *ps =
  1197     nsSVGEffects::GetPaintServer(aFrame, &style->mFill, nsSVGEffects::FillProperty());
  1198   if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mFill, opacity))
  1199     return true;
  1201   if (SetupContextPaint(aContext, aContextPaint, style->mFill, opacity)) {
  1202     return true;
  1205   // On failure, use the fallback colour in case we have an
  1206   // objectBoundingBox where the width or height of the object is zero.
  1207   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1208   SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
  1209                             &nsStyleSVG::mFill, opacity);
  1211   return true;
  1214 bool
  1215 nsSVGUtils::SetupCairoStrokePaint(nsIFrame *aFrame, gfxContext* aContext,
  1216                                   gfxTextContextPaint *aContextPaint)
  1218   const nsStyleSVG* style = aFrame->StyleSVG();
  1219   if (style->mStroke.mType == eStyleSVGPaintType_None)
  1220     return false;
  1222   float opacity = MaybeOptimizeOpacity(aFrame,
  1223                                        GetOpacity(style->mStrokeOpacitySource,
  1224                                                   style->mStrokeOpacity,
  1225                                                   aContextPaint));
  1227   nsSVGPaintServerFrame *ps =
  1228     nsSVGEffects::GetPaintServer(aFrame, &style->mStroke, nsSVGEffects::StrokeProperty());
  1229   if (ps && ps->SetupPaintServer(aContext, aFrame, &nsStyleSVG::mStroke, opacity))
  1230     return true;
  1232   if (SetupContextPaint(aContext, aContextPaint, style->mStroke, opacity)) {
  1233     return true;
  1236   // On failure, use the fallback colour in case we have an
  1237   // objectBoundingBox where the width or height of the object is zero.
  1238   // See http://www.w3.org/TR/SVG11/coords.html#ObjectBoundingBox
  1239   SetupFallbackOrPaintColor(aContext, aFrame->StyleContext(),
  1240                             &nsStyleSVG::mStroke, opacity);
  1242   return true;
  1245 /* static */ float
  1246 nsSVGUtils::GetOpacity(nsStyleSVGOpacitySource aOpacityType,
  1247                        const float& aOpacity,
  1248                        gfxTextContextPaint *aOuterContextPaint)
  1250   float opacity = 1.0f;
  1251   switch (aOpacityType) {
  1252   case eStyleSVGOpacitySource_Normal:
  1253     opacity = aOpacity;
  1254     break;
  1255   case eStyleSVGOpacitySource_ContextFillOpacity:
  1256     if (aOuterContextPaint) {
  1257       opacity = aOuterContextPaint->GetFillOpacity();
  1258     } else {
  1259       NS_WARNING("context-fill-opacity used outside of an SVG glyph");
  1261     break;
  1262   case eStyleSVGOpacitySource_ContextStrokeOpacity:
  1263     if (aOuterContextPaint) {
  1264       opacity = aOuterContextPaint->GetStrokeOpacity();
  1265     } else {
  1266       NS_WARNING("context-stroke-opacity used outside of an SVG glyph");
  1268     break;
  1269   default:
  1270     NS_NOTREACHED("Unknown object opacity inheritance type for SVG glyph");
  1272   return opacity;
  1275 bool
  1276 nsSVGUtils::HasStroke(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
  1278   const nsStyleSVG *style = aFrame->StyleSVG();
  1279   return style->HasStroke() && GetStrokeWidth(aFrame, aContextPaint) > 0;
  1282 float
  1283 nsSVGUtils::GetStrokeWidth(nsIFrame* aFrame, gfxTextContextPaint *aContextPaint)
  1285   const nsStyleSVG *style = aFrame->StyleSVG();
  1286   if (aContextPaint && style->mStrokeWidthFromObject) {
  1287     return aContextPaint->GetStrokeWidth();
  1290   nsIContent* content = aFrame->GetContent();
  1291   if (content->IsNodeOfType(nsINode::eTEXT)) {
  1292     content = content->GetParent();
  1295   nsSVGElement *ctx = static_cast<nsSVGElement*>(content);
  1297   return SVGContentUtils::CoordToFloat(aFrame->PresContext(), ctx,
  1298                                        style->mStrokeWidth);
  1301 void
  1302 nsSVGUtils::SetupCairoStrokeBBoxGeometry(nsIFrame* aFrame,
  1303                                          gfxContext *aContext,
  1304                                          gfxTextContextPaint *aContextPaint)
  1306   float width = GetStrokeWidth(aFrame, aContextPaint);
  1307   if (width <= 0)
  1308     return;
  1309   aContext->SetLineWidth(width);
  1311   // Apply any stroke-specific transform
  1312   gfxMatrix strokeTransform = GetStrokeTransform(aFrame);
  1313   if (!strokeTransform.IsIdentity()) {
  1314     aContext->Multiply(strokeTransform);
  1317   const nsStyleSVG* style = aFrame->StyleSVG();
  1319   switch (style->mStrokeLinecap) {
  1320   case NS_STYLE_STROKE_LINECAP_BUTT:
  1321     aContext->SetLineCap(gfxContext::LINE_CAP_BUTT);
  1322     break;
  1323   case NS_STYLE_STROKE_LINECAP_ROUND:
  1324     aContext->SetLineCap(gfxContext::LINE_CAP_ROUND);
  1325     break;
  1326   case NS_STYLE_STROKE_LINECAP_SQUARE:
  1327     aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
  1328     break;
  1331   aContext->SetMiterLimit(style->mStrokeMiterlimit);
  1333   switch (style->mStrokeLinejoin) {
  1334   case NS_STYLE_STROKE_LINEJOIN_MITER:
  1335     aContext->SetLineJoin(gfxContext::LINE_JOIN_MITER);
  1336     break;
  1337   case NS_STYLE_STROKE_LINEJOIN_ROUND:
  1338     aContext->SetLineJoin(gfxContext::LINE_JOIN_ROUND);
  1339     break;
  1340   case NS_STYLE_STROKE_LINEJOIN_BEVEL:
  1341     aContext->SetLineJoin(gfxContext::LINE_JOIN_BEVEL);
  1342     break;
  1346 static bool
  1347 GetStrokeDashData(nsIFrame* aFrame,
  1348                   FallibleTArray<gfxFloat>& aDashes,
  1349                   gfxFloat* aDashOffset,
  1350                   gfxTextContextPaint *aContextPaint)
  1352   const nsStyleSVG* style = aFrame->StyleSVG();
  1353   nsPresContext *presContext = aFrame->PresContext();
  1354   nsIContent *content = aFrame->GetContent();
  1355   nsSVGElement *ctx = static_cast<nsSVGElement*>
  1356     (content->IsNodeOfType(nsINode::eTEXT) ?
  1357      content->GetParent() : content);
  1359   gfxFloat totalLength = 0.0;
  1360   if (aContextPaint && style->mStrokeDasharrayFromObject) {
  1361     aDashes = aContextPaint->GetStrokeDashArray();
  1363     for (uint32_t i = 0; i < aDashes.Length(); i++) {
  1364       if (aDashes[i] < 0.0) {
  1365         return false;
  1367       totalLength += aDashes[i];
  1370   } else {
  1371     uint32_t count = style->mStrokeDasharrayLength;
  1372     if (!count || !aDashes.SetLength(count)) {
  1373       return false;
  1376     gfxFloat pathScale = 1.0;
  1378     if (content->Tag() == nsGkAtoms::path) {
  1379       pathScale = static_cast<SVGPathElement*>(content)->
  1380         GetPathLengthScale(SVGPathElement::eForStroking);
  1381       if (pathScale <= 0) {
  1382         return false;
  1386     const nsStyleCoord *dasharray = style->mStrokeDasharray;
  1388     for (uint32_t i = 0; i < count; i++) {
  1389       aDashes[i] = SVGContentUtils::CoordToFloat(presContext,
  1390                                                  ctx,
  1391                                                  dasharray[i]) * pathScale;
  1392       if (aDashes[i] < 0.0) {
  1393         return false;
  1395       totalLength += aDashes[i];
  1399   if (aContextPaint && style->mStrokeDashoffsetFromObject) {
  1400     *aDashOffset = aContextPaint->GetStrokeDashOffset();
  1401   } else {
  1402     *aDashOffset = SVGContentUtils::CoordToFloat(presContext,
  1403                                                  ctx,
  1404                                                  style->mStrokeDashoffset);
  1407   return (totalLength > 0.0);
  1410 void
  1411 nsSVGUtils::SetupCairoStrokeGeometry(nsIFrame* aFrame, gfxContext* aContext,
  1412                                      gfxTextContextPaint *aContextPaint)
  1414   SetupCairoStrokeBBoxGeometry(aFrame, aContext, aContextPaint);
  1416   AutoFallibleTArray<gfxFloat, 10> dashes;
  1417   gfxFloat dashOffset;
  1418   if (GetStrokeDashData(aFrame, dashes, &dashOffset, aContextPaint)) {
  1419     aContext->SetDash(dashes.Elements(), dashes.Length(), dashOffset);
  1423 uint16_t
  1424 nsSVGUtils::GetGeometryHitTestFlags(nsIFrame* aFrame)
  1426   uint16_t flags = 0;
  1428   switch(aFrame->StyleVisibility()->mPointerEvents) {
  1429   case NS_STYLE_POINTER_EVENTS_NONE:
  1430     break;
  1431   case NS_STYLE_POINTER_EVENTS_AUTO:
  1432   case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED:
  1433     if (aFrame->StyleVisibility()->IsVisible()) {
  1434       if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
  1435         flags |= SVG_HIT_TEST_FILL;
  1436       if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
  1437         flags |= SVG_HIT_TEST_STROKE;
  1438       if (aFrame->StyleSVG()->mStrokeOpacity > 0)
  1439         flags |= SVG_HIT_TEST_CHECK_MRECT;
  1441     break;
  1442   case NS_STYLE_POINTER_EVENTS_VISIBLEFILL:
  1443     if (aFrame->StyleVisibility()->IsVisible()) {
  1444       flags |= SVG_HIT_TEST_FILL;
  1446     break;
  1447   case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE:
  1448     if (aFrame->StyleVisibility()->IsVisible()) {
  1449       flags |= SVG_HIT_TEST_STROKE;
  1451     break;
  1452   case NS_STYLE_POINTER_EVENTS_VISIBLE:
  1453     if (aFrame->StyleVisibility()->IsVisible()) {
  1454       flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1456     break;
  1457   case NS_STYLE_POINTER_EVENTS_PAINTED:
  1458     if (aFrame->StyleSVG()->mFill.mType != eStyleSVGPaintType_None)
  1459       flags |= SVG_HIT_TEST_FILL;
  1460     if (aFrame->StyleSVG()->mStroke.mType != eStyleSVGPaintType_None)
  1461       flags |= SVG_HIT_TEST_STROKE;
  1462     if (aFrame->StyleSVG()->mStrokeOpacity)
  1463       flags |= SVG_HIT_TEST_CHECK_MRECT;
  1464     break;
  1465   case NS_STYLE_POINTER_EVENTS_FILL:
  1466     flags |= SVG_HIT_TEST_FILL;
  1467     break;
  1468   case NS_STYLE_POINTER_EVENTS_STROKE:
  1469     flags |= SVG_HIT_TEST_STROKE;
  1470     break;
  1471   case NS_STYLE_POINTER_EVENTS_ALL:
  1472     flags |= SVG_HIT_TEST_FILL | SVG_HIT_TEST_STROKE;
  1473     break;
  1474   default:
  1475     NS_ERROR("not reached");
  1476     break;
  1479   return flags;
  1482 bool
  1483 nsSVGUtils::SetupCairoStroke(nsIFrame* aFrame, gfxContext* aContext,
  1484                              gfxTextContextPaint *aContextPaint)
  1486   if (!HasStroke(aFrame, aContextPaint)) {
  1487     return false;
  1489   SetupCairoStrokeGeometry(aFrame, aContext, aContextPaint);
  1491   return SetupCairoStrokePaint(aFrame, aContext, aContextPaint);
  1494 bool
  1495 nsSVGUtils::PaintSVGGlyph(Element* aElement, gfxContext* aContext,
  1496                           DrawMode aDrawMode,
  1497                           gfxTextContextPaint* aContextPaint)
  1499   nsIFrame* frame = aElement->GetPrimaryFrame();
  1500   nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1501   if (!svgFrame) {
  1502     return false;
  1504   nsRefPtr<nsRenderingContext> context(new nsRenderingContext());
  1505   context->Init(frame->PresContext()->DeviceContext(), aContext);
  1506   context->AddUserData(&gfxTextContextPaint::sUserDataKey, aContextPaint,
  1507                        nullptr);
  1508   svgFrame->NotifySVGChanged(nsISVGChildFrame::TRANSFORM_CHANGED);
  1509   nsresult rv = svgFrame->PaintSVG(context, nullptr, frame);
  1510   return NS_SUCCEEDED(rv);
  1513 bool
  1514 nsSVGUtils::GetSVGGlyphExtents(Element* aElement,
  1515                                const gfxMatrix& aSVGToAppSpace,
  1516                                gfxRect* aResult)
  1518   nsIFrame* frame = aElement->GetPrimaryFrame();
  1519   nsISVGChildFrame* svgFrame = do_QueryFrame(frame);
  1520   if (!svgFrame) {
  1521     return false;
  1524   gfxMatrix transform(aSVGToAppSpace);
  1525   nsIContent* content = frame->GetContent();
  1526   if (content->IsSVG()) {
  1527     transform = static_cast<nsSVGElement*>(content)->
  1528                   PrependLocalTransformsTo(aSVGToAppSpace);
  1531   *aResult = svgFrame->GetBBoxContribution(gfx::ToMatrix(transform),
  1532     nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIncludeFillGeometry |
  1533     nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIncludeStrokeGeometry |
  1534     nsSVGUtils::eBBoxIncludeMarkers).ToThebesRect();
  1535   return true;
  1538 nsRect
  1539 nsSVGUtils::ToCanvasBounds(const gfxRect &aUserspaceRect,
  1540                            const gfxMatrix &aToCanvas,
  1541                            const nsPresContext *presContext)
  1543   return nsLayoutUtils::RoundGfxRectToAppRect(
  1544                           aToCanvas.TransformBounds(aUserspaceRect),
  1545                           presContext->AppUnitsPerDevPixel());

mercurial