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 #include "nsSVGPathGeometryElement.h"
8 
9 #include "gfxPlatform.h"
10 #include "mozilla/gfx/2D.h"
11 #include "nsComputedDOMStyle.h"
12 #include "nsSVGUtils.h"
13 #include "nsSVGLength2.h"
14 #include "SVGContentUtils.h"
15 
16 using namespace mozilla;
17 using namespace mozilla::gfx;
18 
19 //----------------------------------------------------------------------
20 // Implementation
21 
nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo> & aNodeInfo)22 nsSVGPathGeometryElement::nsSVGPathGeometryElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
23   : nsSVGPathGeometryElementBase(aNodeInfo)
24 {
25 }
26 
27 nsresult
AfterSetAttr(int32_t aNamespaceID,nsIAtom * aName,const nsAttrValue * aValue,bool aNotify)28 nsSVGPathGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName,
29                                        const nsAttrValue* aValue, bool aNotify)
30 {
31   if (mCachedPath &&
32       aNamespaceID == kNameSpaceID_None &&
33       AttributeDefinesGeometry(aName)) {
34     mCachedPath = nullptr;
35   }
36   return nsSVGPathGeometryElementBase::AfterSetAttr(aNamespaceID, aName,
37                                                     aValue, aNotify);
38 }
39 
40 bool
AttributeDefinesGeometry(const nsIAtom * aName)41 nsSVGPathGeometryElement::AttributeDefinesGeometry(const nsIAtom *aName)
42 {
43   // Check for nsSVGLength2 attribute
44   LengthAttributesInfo info = GetLengthInfo();
45   for (uint32_t i = 0; i < info.mLengthCount; i++) {
46     if (aName == *info.mLengthInfo[i].mName) {
47       return true;
48     }
49   }
50 
51   return false;
52 }
53 
54 bool
GeometryDependsOnCoordCtx()55 nsSVGPathGeometryElement::GeometryDependsOnCoordCtx()
56 {
57   // Check the nsSVGLength2 attribute
58   LengthAttributesInfo info = const_cast<nsSVGPathGeometryElement*>(this)->GetLengthInfo();
59   for (uint32_t i = 0; i < info.mLengthCount; i++) {
60     if (info.mLengths[i].GetSpecifiedUnitType() == nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE) {
61       return true;
62     }
63   }
64   return false;
65 }
66 
67 bool
IsMarkable()68 nsSVGPathGeometryElement::IsMarkable()
69 {
70   return false;
71 }
72 
73 void
GetMarkPoints(nsTArray<nsSVGMark> * aMarks)74 nsSVGPathGeometryElement::GetMarkPoints(nsTArray<nsSVGMark> *aMarks)
75 {
76 }
77 
78 already_AddRefed<Path>
GetOrBuildPath(const DrawTarget & aDrawTarget,FillRule aFillRule)79 nsSVGPathGeometryElement::GetOrBuildPath(const DrawTarget& aDrawTarget,
80                                          FillRule aFillRule)
81 {
82   // We only cache the path if it matches the backend used for screen painting:
83   bool cacheable  = aDrawTarget.GetBackendType() ==
84                     gfxPlatform::GetPlatform()->GetDefaultContentBackend();
85 
86   // Checking for and returning mCachedPath before checking the pref means
87   // that the pref is only live on page reload (or app restart for SVG in
88   // chrome). The benefit is that we avoid causing a CPU memory cache miss by
89   // looking at the global variable that the pref's stored in.
90   if (cacheable && mCachedPath) {
91     if (aDrawTarget.GetBackendType() == mCachedPath->GetBackendType()) {
92       RefPtr<Path> path(mCachedPath);
93       return path.forget();
94     }
95   }
96   RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder(aFillRule);
97   RefPtr<Path> path = BuildPath(builder);
98   if (cacheable && NS_SVGPathCachingEnabled()) {
99     mCachedPath = path;
100   }
101   return path.forget();
102 }
103 
104 already_AddRefed<Path>
GetOrBuildPathForMeasuring()105 nsSVGPathGeometryElement::GetOrBuildPathForMeasuring()
106 {
107   return nullptr;
108 }
109 
110 FillRule
GetFillRule()111 nsSVGPathGeometryElement::GetFillRule()
112 {
113   FillRule fillRule = FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero
114 
115   RefPtr<nsStyleContext> styleContext =
116     nsComputedDOMStyle::GetStyleContextForElementNoFlush(this, nullptr,
117                                                          nullptr);
118 
119   if (styleContext) {
120     MOZ_ASSERT(styleContext->StyleSVG()->mFillRule == StyleFillRule::Nonzero ||
121                styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd);
122 
123     if (styleContext->StyleSVG()->mFillRule == StyleFillRule::Evenodd) {
124       fillRule = FillRule::FILL_EVEN_ODD;
125     }
126   } else {
127     // ReportToConsole
128     NS_WARNING("Couldn't get style context for content in GetFillRule");
129   }
130 
131   return fillRule;
132 }
133