LCOV - code coverage report
Current view: top level - dom/svg - SVGContentUtils.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 108 380 28.4 %
Date: 2017-07-14 16:53:18 Functions: 11 33 33.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : // Main header first:
       8             : // This is also necessary to ensure our definition of M_SQRT1_2 is picked up
       9             : #include "SVGContentUtils.h"
      10             : 
      11             : // Keep others in (case-insensitive) order:
      12             : #include "gfx2DGlue.h"
      13             : #include "gfxMatrix.h"
      14             : #include "gfxPlatform.h"
      15             : #include "mozilla/gfx/2D.h"
      16             : #include "mozilla/dom/SVGSVGElement.h"
      17             : #include "mozilla/RefPtr.h"
      18             : #include "mozilla/SVGContextPaint.h"
      19             : #include "nsComputedDOMStyle.h"
      20             : #include "nsFontMetrics.h"
      21             : #include "nsIFrame.h"
      22             : #include "nsIScriptError.h"
      23             : #include "nsLayoutUtils.h"
      24             : #include "nsMathUtils.h"
      25             : #include "SVGAnimationElement.h"
      26             : #include "SVGAnimatedPreserveAspectRatio.h"
      27             : #include "nsContentUtils.h"
      28             : #include "mozilla/gfx/2D.h"
      29             : #include "mozilla/gfx/Types.h"
      30             : #include "mozilla/FloatingPoint.h"
      31             : #include "nsStyleContext.h"
      32             : #include "nsSVGPathDataParser.h"
      33             : #include "SVGPathData.h"
      34             : #include "SVGPathElement.h"
      35             : 
      36             : using namespace mozilla;
      37             : using namespace mozilla::dom;
      38             : using namespace mozilla::gfx;
      39             : 
      40             : SVGSVGElement*
      41           0 : SVGContentUtils::GetOuterSVGElement(nsSVGElement *aSVGElement)
      42             : {
      43           0 :   nsIContent *element = nullptr;
      44           0 :   nsIContent *ancestor = aSVGElement->GetFlattenedTreeParent();
      45             : 
      46           0 :   while (ancestor && ancestor->IsSVGElement() &&
      47           0 :                      !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
      48           0 :     element = ancestor;
      49           0 :     ancestor = element->GetFlattenedTreeParent();
      50             :   }
      51             : 
      52           0 :   if (element && element->IsSVGElement(nsGkAtoms::svg)) {
      53           0 :     return static_cast<SVGSVGElement*>(element);
      54             :   }
      55           0 :   return nullptr;
      56             : }
      57             : 
      58             : void
      59           0 : SVGContentUtils::ActivateByHyperlink(nsIContent *aContent)
      60             : {
      61           0 :   MOZ_ASSERT(aContent->IsNodeOfType(nsINode::eANIMATION),
      62             :              "Expecting an animation element");
      63             : 
      64           0 :   static_cast<SVGAnimationElement*>(aContent)->ActivateByHyperlink();
      65           0 : }
      66             : 
      67             : enum DashState {
      68             :   eDashedStroke,
      69             :   eContinuousStroke, //< all dashes, no gaps
      70             :   eNoStroke          //< all gaps, no dashes
      71             : };
      72             : 
      73             : static DashState
      74           2 : GetStrokeDashData(SVGContentUtils::AutoStrokeOptions* aStrokeOptions,
      75             :                   nsSVGElement* aElement,
      76             :                   const nsStyleSVG* aStyleSVG,
      77             :                   SVGContextPaint* aContextPaint)
      78             : {
      79             :   size_t dashArrayLength;
      80           2 :   Float totalLengthOfDashes = 0.0, totalLengthOfGaps = 0.0;
      81           2 :   Float pathScale = 1.0;
      82             : 
      83           2 :   if (aContextPaint && aStyleSVG->StrokeDasharrayFromObject()) {
      84           0 :     const FallibleTArray<gfxFloat>& dashSrc = aContextPaint->GetStrokeDashArray();
      85           0 :     dashArrayLength = dashSrc.Length();
      86           0 :     if (dashArrayLength <= 0) {
      87           0 :       return eContinuousStroke;
      88             :     }
      89           0 :     Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
      90           0 :     if (!dashPattern) {
      91           0 :       return eContinuousStroke;
      92             :     }
      93           0 :     for (size_t i = 0; i < dashArrayLength; i++) {
      94           0 :       if (dashSrc[i] < 0.0) {
      95           0 :         return eContinuousStroke; // invalid
      96             :       }
      97           0 :       dashPattern[i] = Float(dashSrc[i]);
      98           0 :       (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashSrc[i];
      99             :     }
     100             :   } else {
     101           2 :     const nsTArray<nsStyleCoord>& dasharray = aStyleSVG->mStrokeDasharray;
     102           2 :     dashArrayLength = aStyleSVG->mStrokeDasharray.Length();
     103           2 :     if (dashArrayLength <= 0) {
     104           2 :       return eContinuousStroke;
     105             :     }
     106           0 :     if (aElement->IsSVGElement(nsGkAtoms::path)) {
     107             :       pathScale = static_cast<SVGPathElement*>(aElement)->
     108           0 :         GetPathLengthScale(SVGPathElement::eForStroking);
     109           0 :       if (pathScale <= 0) {
     110           0 :         return eContinuousStroke;
     111             :       }
     112             :     }
     113           0 :     Float* dashPattern = aStrokeOptions->InitDashPattern(dashArrayLength);
     114           0 :     if (!dashPattern) {
     115           0 :       return eContinuousStroke;
     116             :     }
     117           0 :     for (uint32_t i = 0; i < dashArrayLength; i++) {
     118             :       Float dashLength =
     119           0 :         SVGContentUtils::CoordToFloat(aElement, dasharray[i]) * pathScale;
     120           0 :       if (dashLength < 0.0) {
     121           0 :         return eContinuousStroke; // invalid
     122             :       }
     123           0 :       dashPattern[i] = dashLength;
     124           0 :       (i % 2 ? totalLengthOfGaps : totalLengthOfDashes) += dashLength;
     125             :     }
     126             :   }
     127             : 
     128             :   // Now that aStrokeOptions.mDashPattern is fully initialized (we didn't
     129             :   // return early above) we can safely set mDashLength:
     130           0 :   aStrokeOptions->mDashLength = dashArrayLength;
     131             : 
     132           0 :   if ((dashArrayLength % 2) == 1) {
     133             :     // If we have a dash pattern with an odd number of lengths the pattern
     134             :     // repeats a second time, per the SVG spec., and as implemented by Moz2D.
     135             :     // When deciding whether to return eNoStroke or eContinuousStroke below we
     136             :     // need to take into account that in the repeat pattern the dashes become
     137             :     // gaps, and the gaps become dashes.
     138           0 :     Float origTotalLengthOfDashes = totalLengthOfDashes;
     139           0 :     totalLengthOfDashes += totalLengthOfGaps;
     140           0 :     totalLengthOfGaps += origTotalLengthOfDashes;
     141             :   }
     142             : 
     143             :   // Stroking using dashes is much slower than stroking a continuous line
     144             :   // (see bug 609361 comment 40), and much, much slower than not stroking the
     145             :   // line at all. Here we check for cases when the dash pattern causes the
     146             :   // stroke to essentially be continuous or to be nonexistent in which case
     147             :   // we can avoid expensive stroking operations (the underlying platform
     148             :   // graphics libraries don't seem to optimize for this).
     149           0 :   if (totalLengthOfGaps <= 0) {
     150           0 :     return eContinuousStroke;
     151             :   }
     152             :   // We can only return eNoStroke if the value of stroke-linecap isn't
     153             :   // adding caps to zero length dashes.
     154           0 :   if (totalLengthOfDashes <= 0 &&
     155           0 :       aStyleSVG->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_BUTT) {
     156           0 :     return eNoStroke;
     157             :   }
     158             : 
     159           0 :   if (aContextPaint && aStyleSVG->StrokeDashoffsetFromObject()) {
     160           0 :     aStrokeOptions->mDashOffset = Float(aContextPaint->GetStrokeDashOffset());
     161             :   } else {
     162           0 :     aStrokeOptions->mDashOffset =
     163           0 :       SVGContentUtils::CoordToFloat(aElement, aStyleSVG->mStrokeDashoffset) *
     164             :       pathScale;
     165             :   }
     166             : 
     167           0 :   return eDashedStroke;
     168             : }
     169             : 
     170             : void
     171           6 : SVGContentUtils::GetStrokeOptions(AutoStrokeOptions* aStrokeOptions,
     172             :                                   nsSVGElement* aElement,
     173             :                                   nsStyleContext* aStyleContext,
     174             :                                   SVGContextPaint* aContextPaint,
     175             :                                   StrokeOptionFlags aFlags)
     176             : {
     177          12 :   RefPtr<nsStyleContext> styleContext;
     178           6 :   if (aStyleContext) {
     179           6 :     styleContext = aStyleContext;
     180             :   } else {
     181             :     styleContext =
     182           0 :       nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
     183             :   }
     184             : 
     185           6 :   if (!styleContext) {
     186           0 :     return;
     187             :   }
     188             : 
     189           6 :   const nsStyleSVG* styleSVG = styleContext->StyleSVG();
     190             : 
     191           6 :   bool checkedDashAndStrokeIsDashed = false;
     192           6 :   if (aFlags != eIgnoreStrokeDashing) {
     193             :     DashState dashState =
     194           2 :       GetStrokeDashData(aStrokeOptions, aElement, styleSVG, aContextPaint);
     195             : 
     196           2 :     if (dashState == eNoStroke) {
     197             :       // Hopefully this will shortcircuit any stroke operations:
     198           0 :       aStrokeOptions->mLineWidth = 0;
     199           0 :       return;
     200             :     }
     201           2 :     if (dashState == eContinuousStroke && aStrokeOptions->mDashPattern) {
     202             :       // Prevent our caller from wasting time looking at a pattern without gaps:
     203           0 :       aStrokeOptions->DiscardDashPattern();
     204             :     }
     205           2 :     checkedDashAndStrokeIsDashed = (dashState == eDashedStroke);
     206             :   }
     207             : 
     208           6 :   aStrokeOptions->mLineWidth =
     209           6 :     GetStrokeWidth(aElement, styleContext, aContextPaint);
     210             : 
     211           6 :   aStrokeOptions->mMiterLimit = Float(styleSVG->mStrokeMiterlimit);
     212             : 
     213           6 :   switch (styleSVG->mStrokeLinejoin) {
     214             :   case NS_STYLE_STROKE_LINEJOIN_MITER:
     215           6 :     aStrokeOptions->mLineJoin = JoinStyle::MITER_OR_BEVEL;
     216           6 :     break;
     217             :   case NS_STYLE_STROKE_LINEJOIN_ROUND:
     218           0 :     aStrokeOptions->mLineJoin = JoinStyle::ROUND;
     219           0 :     break;
     220             :   case NS_STYLE_STROKE_LINEJOIN_BEVEL:
     221           0 :     aStrokeOptions->mLineJoin = JoinStyle::BEVEL;
     222           0 :     break;
     223             :   }
     224             : 
     225           6 :   if (ShapeTypeHasNoCorners(aElement) && !checkedDashAndStrokeIsDashed) {
     226             :     // Note: if aFlags == eIgnoreStrokeDashing then we may be returning the
     227             :     // wrong linecap value here, since the actual linecap used on render in this
     228             :     // case depends on whether the stroke is dashed or not.
     229           0 :     aStrokeOptions->mLineCap = CapStyle::BUTT;
     230             :   } else {
     231           6 :     switch (styleSVG->mStrokeLinecap) {
     232             :       case NS_STYLE_STROKE_LINECAP_BUTT:
     233           6 :         aStrokeOptions->mLineCap = CapStyle::BUTT;
     234           6 :         break;
     235             :       case NS_STYLE_STROKE_LINECAP_ROUND:
     236           0 :         aStrokeOptions->mLineCap = CapStyle::ROUND;
     237           0 :         break;
     238             :       case NS_STYLE_STROKE_LINECAP_SQUARE:
     239           0 :         aStrokeOptions->mLineCap = CapStyle::SQUARE;
     240           0 :         break;
     241             :     }
     242             :   }
     243             : }
     244             : 
     245             : Float
     246           6 : SVGContentUtils::GetStrokeWidth(nsSVGElement* aElement,
     247             :                                 nsStyleContext* aStyleContext,
     248             :                                 SVGContextPaint* aContextPaint)
     249             : {
     250          12 :   RefPtr<nsStyleContext> styleContext;
     251           6 :   if (aStyleContext) {
     252           6 :     styleContext = aStyleContext;
     253             :   } else {
     254             :     styleContext =
     255           0 :       nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
     256             :   }
     257             : 
     258           6 :   if (!styleContext) {
     259           0 :     return 0.0f;
     260             :   }
     261             : 
     262           6 :   const nsStyleSVG* styleSVG = styleContext->StyleSVG();
     263             : 
     264           6 :   if (aContextPaint && styleSVG->StrokeWidthFromObject()) {
     265           0 :     return aContextPaint->GetStrokeWidth();
     266             :   }
     267             : 
     268           6 :   return SVGContentUtils::CoordToFloat(aElement, styleSVG->mStrokeWidth);
     269             : }
     270             : 
     271             : float
     272           0 : SVGContentUtils::GetFontSize(Element *aElement)
     273             : {
     274           0 :   if (!aElement)
     275           0 :     return 1.0f;
     276             : 
     277             :   RefPtr<nsStyleContext> styleContext =
     278           0 :     nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
     279           0 :   if (!styleContext) {
     280             :     // ReportToConsole
     281           0 :     NS_WARNING("Couldn't get style context for content in GetFontStyle");
     282           0 :     return 1.0f;
     283             :   }
     284             : 
     285           0 :   return GetFontSize(styleContext);
     286             : }
     287             : 
     288             : float
     289           0 : SVGContentUtils::GetFontSize(nsIFrame *aFrame)
     290             : {
     291           0 :   MOZ_ASSERT(aFrame, "NULL frame in GetFontSize");
     292           0 :   return GetFontSize(aFrame->StyleContext());
     293             : }
     294             : 
     295             : float
     296           0 : SVGContentUtils::GetFontSize(nsStyleContext *aStyleContext)
     297             : {
     298           0 :   MOZ_ASSERT(aStyleContext, "NULL style context in GetFontSize");
     299             : 
     300           0 :   nsPresContext *presContext = aStyleContext->PresContext();
     301           0 :   MOZ_ASSERT(presContext, "NULL pres context in GetFontSize");
     302             : 
     303           0 :   nscoord fontSize = aStyleContext->StyleFont()->mSize;
     304           0 :   return nsPresContext::AppUnitsToFloatCSSPixels(fontSize) /
     305           0 :          presContext->EffectiveTextZoom();
     306             : }
     307             : 
     308             : float
     309           0 : SVGContentUtils::GetFontXHeight(Element *aElement)
     310             : {
     311           0 :   if (!aElement)
     312           0 :     return 1.0f;
     313             : 
     314             :   RefPtr<nsStyleContext> styleContext =
     315           0 :     nsComputedDOMStyle::GetStyleContextNoFlush(aElement, nullptr, nullptr);
     316           0 :   if (!styleContext) {
     317             :     // ReportToConsole
     318           0 :     NS_WARNING("Couldn't get style context for content in GetFontStyle");
     319           0 :     return 1.0f;
     320             :   }
     321             : 
     322           0 :   return GetFontXHeight(styleContext);
     323             : }
     324             : 
     325             : float
     326           0 : SVGContentUtils::GetFontXHeight(nsIFrame *aFrame)
     327             : {
     328           0 :   MOZ_ASSERT(aFrame, "NULL frame in GetFontXHeight");
     329           0 :   return GetFontXHeight(aFrame->StyleContext());
     330             : }
     331             : 
     332             : float
     333           0 : SVGContentUtils::GetFontXHeight(nsStyleContext *aStyleContext)
     334             : {
     335           0 :   MOZ_ASSERT(aStyleContext, "NULL style context in GetFontXHeight");
     336             : 
     337           0 :   nsPresContext *presContext = aStyleContext->PresContext();
     338           0 :   MOZ_ASSERT(presContext, "NULL pres context in GetFontXHeight");
     339             : 
     340             :   RefPtr<nsFontMetrics> fontMetrics =
     341           0 :     nsLayoutUtils::GetFontMetricsForStyleContext(aStyleContext);
     342             : 
     343           0 :   if (!fontMetrics) {
     344             :     // ReportToConsole
     345           0 :     NS_WARNING("no FontMetrics in GetFontXHeight()");
     346           0 :     return 1.0f;
     347             :   }
     348             : 
     349           0 :   nscoord xHeight = fontMetrics->XHeight();
     350           0 :   return nsPresContext::AppUnitsToFloatCSSPixels(xHeight) /
     351           0 :          presContext->EffectiveTextZoom();
     352             : }
     353             : nsresult
     354           0 : SVGContentUtils::ReportToConsole(nsIDocument* doc,
     355             :                                  const char* aWarning,
     356             :                                  const char16_t **aParams,
     357             :                                  uint32_t aParamsLength)
     358             : {
     359           0 :   return nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
     360           0 :                                          NS_LITERAL_CSTRING("SVG"), doc,
     361             :                                          nsContentUtils::eSVG_PROPERTIES,
     362             :                                          aWarning,
     363           0 :                                          aParams, aParamsLength);
     364             : }
     365             : 
     366             : bool
     367           0 : SVGContentUtils::EstablishesViewport(nsIContent *aContent)
     368             : {
     369             :   // Although SVG 1.1 states that <image> is an element that establishes a
     370             :   // viewport, this is really only for the document it references, not
     371             :   // for any child content, which is what this function is used for.
     372           0 :   return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::svg,
     373             :                                                   nsGkAtoms::foreignObject,
     374           0 :                                                   nsGkAtoms::symbol);
     375             : }
     376             : 
     377             : nsSVGElement*
     378           0 : SVGContentUtils::GetNearestViewportElement(nsIContent *aContent)
     379             : {
     380           0 :   nsIContent *element = aContent->GetFlattenedTreeParent();
     381             : 
     382           0 :   while (element && element->IsSVGElement()) {
     383           0 :     if (EstablishesViewport(element)) {
     384           0 :       if (element->IsSVGElement(nsGkAtoms::foreignObject)) {
     385           0 :         return nullptr;
     386             :       }
     387           0 :       return static_cast<nsSVGElement*>(element);
     388             :     }
     389           0 :     element = element->GetFlattenedTreeParent();
     390             :   }
     391           0 :   return nullptr;
     392             : }
     393             : 
     394             : static gfx::Matrix
     395           0 : GetCTMInternal(nsSVGElement *aElement, bool aScreenCTM, bool aHaveRecursed)
     396             : {
     397           0 :   gfxMatrix matrix = aElement->PrependLocalTransformsTo(gfxMatrix(),
     398           0 :     aHaveRecursed ? eAllTransforms : eUserSpaceToParent);
     399           0 :   nsSVGElement *element = aElement;
     400           0 :   nsIContent *ancestor = aElement->GetFlattenedTreeParent();
     401             : 
     402           0 :   while (ancestor && ancestor->IsSVGElement() &&
     403           0 :                      !ancestor->IsSVGElement(nsGkAtoms::foreignObject)) {
     404           0 :     element = static_cast<nsSVGElement*>(ancestor);
     405           0 :     matrix *= element->PrependLocalTransformsTo(gfxMatrix()); // i.e. *A*ppend
     406           0 :     if (!aScreenCTM && SVGContentUtils::EstablishesViewport(element)) {
     407           0 :       if (!element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG) &&
     408           0 :           !element->NodeInfo()->Equals(nsGkAtoms::symbol, kNameSpaceID_SVG)) {
     409           0 :         NS_ERROR("New (SVG > 1.1) SVG viewport establishing element?");
     410           0 :         return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
     411             :       }
     412             :       // XXX spec seems to say x,y translation should be undone for IsInnerSVG
     413           0 :       return gfx::ToMatrix(matrix);
     414             :     }
     415           0 :     ancestor = ancestor->GetFlattenedTreeParent();
     416             :   }
     417           0 :   if (!aScreenCTM) {
     418             :     // didn't find a nearestViewportElement
     419           0 :     return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
     420             :   }
     421           0 :   if (!element->IsSVGElement(nsGkAtoms::svg)) {
     422             :     // Not a valid SVG fragment
     423           0 :     return gfx::Matrix(0.0, 0.0, 0.0, 0.0, 0.0, 0.0); // singular
     424             :   }
     425           0 :   if (element == aElement && !aHaveRecursed) {
     426             :     // We get here when getScreenCTM() is called on an outer-<svg>.
     427             :     // Consistency with other elements would have us include only the
     428             :     // eFromUserSpace transforms, but we include the eAllTransforms
     429             :     // transforms in this case since that's what we've been doing for
     430             :     // a while, and it keeps us consistent with WebKit and Opera (if not
     431             :     // really with the ambiguous spec).
     432           0 :     matrix = aElement->PrependLocalTransformsTo(gfxMatrix());
     433             :   }
     434           0 :   if (!ancestor || !ancestor->IsElement()) {
     435           0 :     return gfx::ToMatrix(matrix);
     436             :   }
     437           0 :   if (ancestor->IsSVGElement()) {
     438             :     return
     439           0 :       gfx::ToMatrix(matrix) * GetCTMInternal(static_cast<nsSVGElement*>(ancestor), true, true);
     440             :   }
     441             : 
     442             :   // XXX this does not take into account CSS transform, or that the non-SVG
     443             :   // content that we've hit may itself be inside an SVG foreignObject higher up
     444           0 :   nsIDocument* currentDoc = aElement->GetComposedDoc();
     445           0 :   float x = 0.0f, y = 0.0f;
     446           0 :   if (currentDoc && element->NodeInfo()->Equals(nsGkAtoms::svg, kNameSpaceID_SVG)) {
     447           0 :     nsIPresShell *presShell = currentDoc->GetShell();
     448           0 :     if (presShell) {
     449           0 :       nsIFrame* frame = element->GetPrimaryFrame();
     450           0 :       nsIFrame* ancestorFrame = presShell->GetRootFrame();
     451           0 :       if (frame && ancestorFrame) {
     452           0 :         nsPoint point = frame->GetOffsetTo(ancestorFrame);
     453           0 :         x = nsPresContext::AppUnitsToFloatCSSPixels(point.x);
     454           0 :         y = nsPresContext::AppUnitsToFloatCSSPixels(point.y);
     455             :       }
     456             :     }
     457             :   }
     458           0 :   return ToMatrix(matrix).PostTranslate(x, y);
     459             : }
     460             : 
     461             : gfx::Matrix
     462           0 : SVGContentUtils::GetCTM(nsSVGElement *aElement, bool aScreenCTM)
     463             : {
     464           0 :   return GetCTMInternal(aElement, aScreenCTM, false);
     465             : }
     466             : 
     467             : void
     468           0 : SVGContentUtils::RectilinearGetStrokeBounds(const Rect& aRect,
     469             :                                             const Matrix& aToBoundsSpace,
     470             :                                             const Matrix& aToNonScalingStrokeSpace,
     471             :                                             float aStrokeWidth,
     472             :                                             Rect* aBounds)
     473             : {
     474           0 :   MOZ_ASSERT(aToBoundsSpace.IsRectilinear(),
     475             :              "aToBoundsSpace must be rectilinear");
     476           0 :   MOZ_ASSERT(aToNonScalingStrokeSpace.IsRectilinear(),
     477             :              "aToNonScalingStrokeSpace must be rectilinear");
     478             : 
     479           0 :   Matrix nonScalingToSource = aToNonScalingStrokeSpace.Inverse();
     480           0 :   Matrix nonScalingToBounds = nonScalingToSource * aToBoundsSpace;
     481             : 
     482           0 :   *aBounds = aToBoundsSpace.TransformBounds(aRect);
     483             : 
     484             :   // Compute the amounts dx and dy that nonScalingToBounds scales a half-width
     485             :   // stroke in the x and y directions, and then inflate aBounds by those amounts
     486             :   // so that when aBounds is transformed back to non-scaling-stroke space
     487             :   // it will map onto the correct stroked bounds.
     488             : 
     489           0 :   Float dx = 0.0f;
     490           0 :   Float dy = 0.0f;
     491             :   // nonScalingToBounds is rectilinear, so either _12 and _21 are zero or _11
     492             :   // and _22 are zero, and in each case the non-zero entries (from among _11,
     493             :   // _12, _21, _22) simply scale the stroke width in the x and y directions.
     494           0 :   if (FuzzyEqual(nonScalingToBounds._12, 0) &&
     495           0 :       FuzzyEqual(nonScalingToBounds._21, 0)) {
     496           0 :     dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._11);
     497           0 :     dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._22);
     498             :   } else {
     499           0 :     dx = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._21);
     500           0 :     dy = (aStrokeWidth / 2.0f) * std::abs(nonScalingToBounds._12);
     501             :   }
     502             : 
     503           0 :   aBounds->Inflate(dx, dy);
     504           0 : }
     505             : 
     506             : double
     507           0 : SVGContentUtils::ComputeNormalizedHypotenuse(double aWidth, double aHeight)
     508             : {
     509           0 :   return NS_hypot(aWidth, aHeight) / M_SQRT2;
     510             : }
     511             : 
     512             : float
     513           0 : SVGContentUtils::AngleBisect(float a1, float a2)
     514             : {
     515           0 :   float delta = fmod(a2 - a1, static_cast<float>(2*M_PI));
     516           0 :   if (delta < 0) {
     517           0 :     delta += static_cast<float>(2*M_PI);
     518             :   }
     519             :   /* delta is now the angle from a1 around to a2, in the range [0, 2*M_PI) */
     520           0 :   float r = a1 + delta/2;
     521           0 :   if (delta >= M_PI) {
     522             :     /* the arc from a2 to a1 is smaller, so use the ray on that side */
     523           0 :     r += static_cast<float>(M_PI);
     524             :   }
     525           0 :   return r;
     526             : }
     527             : 
     528             : gfx::Matrix
     529           0 : SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
     530             :                                      float aViewboxX, float aViewboxY,
     531             :                                      float aViewboxWidth, float aViewboxHeight,
     532             :                                      const SVGAnimatedPreserveAspectRatio &aPreserveAspectRatio)
     533             : {
     534             :   return GetViewBoxTransform(aViewportWidth, aViewportHeight,
     535             :                              aViewboxX, aViewboxY,
     536             :                              aViewboxWidth, aViewboxHeight,
     537           0 :                              aPreserveAspectRatio.GetAnimValue());
     538             : }
     539             : 
     540             : gfx::Matrix
     541         539 : SVGContentUtils::GetViewBoxTransform(float aViewportWidth, float aViewportHeight,
     542             :                                      float aViewboxX, float aViewboxY,
     543             :                                      float aViewboxWidth, float aViewboxHeight,
     544             :                                      const SVGPreserveAspectRatio &aPreserveAspectRatio)
     545             : {
     546         539 :   NS_ASSERTION(aViewportWidth  >= 0, "viewport width must be nonnegative!");
     547         539 :   NS_ASSERTION(aViewportHeight >= 0, "viewport height must be nonnegative!");
     548         539 :   NS_ASSERTION(aViewboxWidth  > 0, "viewBox width must be greater than zero!");
     549         539 :   NS_ASSERTION(aViewboxHeight > 0, "viewBox height must be greater than zero!");
     550             : 
     551         539 :   SVGAlign align = aPreserveAspectRatio.GetAlign();
     552         539 :   SVGMeetOrSlice meetOrSlice = aPreserveAspectRatio.GetMeetOrSlice();
     553             : 
     554             :   // default to the defaults
     555         539 :   if (align == SVG_PRESERVEASPECTRATIO_UNKNOWN)
     556           0 :     align = SVG_PRESERVEASPECTRATIO_XMIDYMID;
     557         539 :   if (meetOrSlice == SVG_MEETORSLICE_UNKNOWN)
     558           0 :     meetOrSlice = SVG_MEETORSLICE_MEET;
     559             : 
     560             :   float a, d, e, f;
     561         539 :   a = aViewportWidth / aViewboxWidth;
     562         539 :   d = aViewportHeight / aViewboxHeight;
     563         539 :   e = 0.0f;
     564         539 :   f = 0.0f;
     565             : 
     566         539 :   if (align != SVG_PRESERVEASPECTRATIO_NONE &&
     567             :       a != d) {
     568           0 :     if ((meetOrSlice == SVG_MEETORSLICE_MEET && a < d) ||
     569           0 :         (meetOrSlice == SVG_MEETORSLICE_SLICE && d < a)) {
     570           0 :       d = a;
     571           0 :       switch (align) {
     572             :       case SVG_PRESERVEASPECTRATIO_XMINYMIN:
     573             :       case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
     574             :       case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
     575           0 :         break;
     576             :       case SVG_PRESERVEASPECTRATIO_XMINYMID:
     577             :       case SVG_PRESERVEASPECTRATIO_XMIDYMID:
     578             :       case SVG_PRESERVEASPECTRATIO_XMAXYMID:
     579           0 :         f = (aViewportHeight - a * aViewboxHeight) / 2.0f;
     580           0 :         break;
     581             :       case SVG_PRESERVEASPECTRATIO_XMINYMAX:
     582             :       case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
     583             :       case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
     584           0 :         f = aViewportHeight - a * aViewboxHeight;
     585           0 :         break;
     586             :       default:
     587           0 :         NS_NOTREACHED("Unknown value for align");
     588             :       }
     589             :     }
     590           0 :     else if (
     591           0 :       (meetOrSlice == SVG_MEETORSLICE_MEET &&
     592           0 :       d < a) ||
     593           0 :       (meetOrSlice == SVG_MEETORSLICE_SLICE &&
     594             :       a < d)) {
     595           0 :       a = d;
     596           0 :       switch (align) {
     597             :       case SVG_PRESERVEASPECTRATIO_XMINYMIN:
     598             :       case SVG_PRESERVEASPECTRATIO_XMINYMID:
     599             :       case SVG_PRESERVEASPECTRATIO_XMINYMAX:
     600           0 :         break;
     601             :       case SVG_PRESERVEASPECTRATIO_XMIDYMIN:
     602             :       case SVG_PRESERVEASPECTRATIO_XMIDYMID:
     603             :       case SVG_PRESERVEASPECTRATIO_XMIDYMAX:
     604           0 :         e = (aViewportWidth - a * aViewboxWidth) / 2.0f;
     605           0 :         break;
     606             :       case SVG_PRESERVEASPECTRATIO_XMAXYMIN:
     607             :       case SVG_PRESERVEASPECTRATIO_XMAXYMID:
     608             :       case SVG_PRESERVEASPECTRATIO_XMAXYMAX:
     609           0 :         e = aViewportWidth - a * aViewboxWidth;
     610           0 :         break;
     611             :       default:
     612           0 :         NS_NOTREACHED("Unknown value for align");
     613             :       }
     614             :     }
     615           0 :     else NS_NOTREACHED("Unknown value for meetOrSlice");
     616             :   }
     617             : 
     618         539 :   if (aViewboxX) e += -a * aViewboxX;
     619         539 :   if (aViewboxY) f += -d * aViewboxY;
     620             : 
     621         539 :   return gfx::Matrix(a, 0.0f, 0.0f, d, e, f);
     622             : }
     623             : 
     624             : static bool
     625        3483 : ParseNumber(RangedPtr<const char16_t>& aIter,
     626             :             const RangedPtr<const char16_t>& aEnd,
     627             :             double& aValue)
     628             : {
     629             :   int32_t sign;
     630        3483 :   if (!SVGContentUtils::ParseOptionalSign(aIter, aEnd, sign)) {
     631           0 :     return false;
     632             :   }
     633             : 
     634             :   // Absolute value of the integer part of the mantissa.
     635        3483 :   double intPart = 0.0;
     636             : 
     637        3483 :   bool gotDot = *aIter == '.';
     638             : 
     639        3483 :   if (!gotDot) {
     640        2496 :     if (!SVGContentUtils::IsDigit(*aIter)) {
     641           0 :       return false;
     642             :     }
     643        3645 :     do {
     644        3645 :       intPart = 10.0 * intPart + SVGContentUtils::DecimalDigitValue(*aIter);
     645        3645 :       ++aIter;
     646        3645 :     } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
     647             : 
     648        2496 :     if (aIter != aEnd) {
     649        2171 :       gotDot = *aIter == '.';
     650             :     }
     651             :   }
     652             : 
     653             :   // Fractional part of the mantissa.
     654        3483 :   double fracPart = 0.0;
     655             : 
     656        3483 :   if (gotDot) {
     657        2355 :     ++aIter;
     658        2355 :     if (aIter == aEnd || !SVGContentUtils::IsDigit(*aIter)) {
     659           0 :       return false;
     660             :     }
     661             : 
     662             :     // Power of ten by which we need to divide the fraction
     663        2355 :     double divisor = 1.0;
     664             : 
     665        5152 :     do {
     666        5152 :       fracPart = 10.0 * fracPart + SVGContentUtils::DecimalDigitValue(*aIter);
     667        5152 :       divisor *= 10.0;
     668        5152 :       ++aIter;
     669        5152 :     } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
     670             : 
     671        2355 :     fracPart /= divisor;
     672             :   }
     673             : 
     674        3483 :   bool gotE = false;
     675        3483 :   int32_t exponent = 0;
     676             :   int32_t expSign;
     677             : 
     678        3483 :   if (aIter != aEnd && (*aIter == 'e' || *aIter == 'E')) {
     679             : 
     680           0 :     RangedPtr<const char16_t> expIter(aIter);
     681             : 
     682           0 :     ++expIter;
     683           0 :     if (expIter != aEnd) {
     684           0 :       expSign = *expIter == '-' ? -1 : 1;
     685           0 :       if (*expIter == '-' || *expIter == '+') {
     686           0 :         ++expIter;
     687             :       }
     688           0 :       if (expIter != aEnd && SVGContentUtils::IsDigit(*expIter)) {
     689             :         // At this point we're sure this is an exponent
     690             :         // and not the start of a unit such as em or ex.
     691           0 :         gotE = true;
     692             :       }
     693             :     }
     694             : 
     695           0 :     if (gotE) {
     696           0 :       aIter = expIter;
     697           0 :       do {
     698           0 :         exponent = 10.0 * exponent + SVGContentUtils::DecimalDigitValue(*aIter);
     699           0 :         ++aIter;
     700           0 :       } while (aIter != aEnd && SVGContentUtils::IsDigit(*aIter));
     701             :     }
     702             :   }
     703             : 
     704             :   // Assemble the number
     705        3483 :   aValue = sign * (intPart + fracPart);
     706        3483 :   if (gotE) {
     707           0 :     aValue *= pow(10.0, expSign * exponent);
     708             :   }
     709        3483 :   return true;
     710             : }
     711             : 
     712             : template<class floatType>
     713             : bool
     714        3483 : SVGContentUtils::ParseNumber(RangedPtr<const char16_t>& aIter,
     715             :                              const RangedPtr<const char16_t>& aEnd,
     716             :                              floatType& aValue)
     717             : {
     718        3483 :   RangedPtr<const char16_t> iter(aIter);
     719             : 
     720             :   double value;
     721        3483 :   if (!::ParseNumber(iter, aEnd, value)) {
     722           0 :     return false;
     723             :   }
     724        3483 :   floatType floatValue = floatType(value);
     725        3483 :   if (!IsFinite(floatValue)) {
     726           0 :     return false;
     727             :   }
     728        3483 :   aValue = floatValue;
     729        3483 :   aIter = iter;
     730        3483 :   return true;
     731             : }
     732             : 
     733             : template bool
     734             : SVGContentUtils::ParseNumber<float>(RangedPtr<const char16_t>& aIter,
     735             :                                     const RangedPtr<const char16_t>& aEnd,
     736             :                                     float& aValue);
     737             : 
     738             : template bool
     739             : SVGContentUtils::ParseNumber<double>(RangedPtr<const char16_t>& aIter,
     740             :                                      const RangedPtr<const char16_t>& aEnd,
     741             :                                      double& aValue);
     742             : 
     743             : RangedPtr<const char16_t>
     744         547 : SVGContentUtils::GetStartRangedPtr(const nsAString& aString)
     745             : {
     746         547 :   return RangedPtr<const char16_t>(aString.Data(), aString.Length());
     747             : }
     748             : 
     749             : RangedPtr<const char16_t>
     750         547 : SVGContentUtils::GetEndRangedPtr(const nsAString& aString)
     751             : {
     752         547 :   return RangedPtr<const char16_t>(aString.Data() + aString.Length(),
     753        1094 :                                     aString.Data(), aString.Length());
     754             : }
     755             : 
     756             : template<class floatType>
     757             : bool
     758         227 : SVGContentUtils::ParseNumber(const nsAString& aString,
     759             :                              floatType& aValue)
     760             : {
     761         227 :   RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
     762         227 :   const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
     763             : 
     764         227 :   return ParseNumber(iter, end, aValue) && iter == end;
     765             : }
     766             : 
     767             : template bool
     768             : SVGContentUtils::ParseNumber<float>(const nsAString& aString,
     769             :                                     float& aValue);
     770             : template bool
     771             : SVGContentUtils::ParseNumber<double>(const nsAString& aString,
     772             :                                      double& aValue);
     773             : 
     774             : /* static */
     775             : bool
     776           0 : SVGContentUtils::ParseInteger(RangedPtr<const char16_t>& aIter,
     777             :                               const RangedPtr<const char16_t>& aEnd,
     778             :                               int32_t& aValue)
     779             : {
     780           0 :   RangedPtr<const char16_t> iter(aIter);
     781             : 
     782             :   int32_t sign;
     783           0 :   if (!ParseOptionalSign(iter, aEnd, sign)) {
     784           0 :     return false;
     785             :   }
     786             : 
     787           0 :   if (!IsDigit(*iter)) {
     788           0 :     return false;
     789             :   }
     790             : 
     791           0 :   int64_t value = 0;
     792             : 
     793           0 :   do {
     794           0 :     if (value <= std::numeric_limits<int32_t>::max()) {
     795           0 :       value = 10 * value + DecimalDigitValue(*iter);
     796             :     }
     797           0 :     ++iter;
     798           0 :   } while (iter != aEnd && IsDigit(*iter));
     799             : 
     800           0 :   aIter = iter;
     801           0 :   aValue = int32_t(clamped(sign * value,
     802           0 :                            int64_t(std::numeric_limits<int32_t>::min()),
     803           0 :                            int64_t(std::numeric_limits<int32_t>::max())));
     804           0 :   return true;
     805             : }
     806             : 
     807             : /* static */
     808             : bool
     809           0 : SVGContentUtils::ParseInteger(const nsAString& aString,
     810             :                               int32_t& aValue)
     811             : {
     812           0 :   RangedPtr<const char16_t> iter = GetStartRangedPtr(aString);
     813           0 :   const RangedPtr<const char16_t> end = GetEndRangedPtr(aString);
     814             : 
     815           0 :   return ParseInteger(iter, end, aValue) && iter == end;
     816             : }
     817             : 
     818             : float
     819          72 : SVGContentUtils::CoordToFloat(nsSVGElement *aContent,
     820             :                               const nsStyleCoord &aCoord)
     821             : {
     822          72 :   switch (aCoord.GetUnit()) {
     823             :   case eStyleUnit_Factor:
     824             :     // user units
     825           8 :     return aCoord.GetFactorValue();
     826             : 
     827             :   case eStyleUnit_Coord:
     828          64 :     return nsPresContext::AppUnitsToFloatCSSPixels(aCoord.GetCoordValue());
     829             : 
     830             :   case eStyleUnit_Percent: {
     831           0 :     SVGSVGElement* ctx = aContent->GetCtx();
     832           0 :     return ctx ? aCoord.GetPercentValue() * ctx->GetLength(SVGContentUtils::XY) : 0.0f;
     833             :   }
     834             :   default:
     835           0 :     return 0.0f;
     836             :   }
     837             : }
     838             : 
     839             : already_AddRefed<gfx::Path>
     840           0 : SVGContentUtils::GetPath(const nsAString& aPathString)
     841             : {
     842           0 :   SVGPathData pathData;
     843           0 :   nsSVGPathDataParser parser(aPathString, &pathData);
     844           0 :   if (!parser.Parse()) {
     845           0 :     return NULL;
     846             :   }
     847             : 
     848             :   RefPtr<DrawTarget> drawTarget =
     849           0 :     gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
     850             :   RefPtr<PathBuilder> builder =
     851           0 :     drawTarget->CreatePathBuilder(FillRule::FILL_WINDING);
     852             : 
     853           0 :   return pathData.BuildPath(builder, NS_STYLE_STROKE_LINECAP_BUTT, 1);
     854             : }
     855             : 
     856             : bool
     857           8 : SVGContentUtils::ShapeTypeHasNoCorners(const nsIContent* aContent) {
     858           8 :   return aContent && aContent->IsAnyOfSVGElements(nsGkAtoms::circle,
     859           8 :                                                   nsGkAtoms::ellipse);
     860             : }

Generated by: LCOV version 1.13