Bug 1691109 [wpt PR 27513] - Increase timeout duration for wpt/fetch/api/basic/keepal...
[gecko.git] / dom / svg / SVGGeometryElement.cpp
blob0630bea590f243f2e8d287d9c71bef6e36bd925a
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/. */
7 #include "SVGGeometryElement.h"
9 #include "DOMSVGPoint.h"
10 #include "gfxPlatform.h"
11 #include "nsCOMPtr.h"
12 #include "SVGAnimatedLength.h"
13 #include "SVGCircleElement.h"
14 #include "SVGEllipseElement.h"
15 #include "SVGGeometryProperty.h"
16 #include "SVGRectElement.h"
17 #include "mozilla/dom/DOMPointBinding.h"
18 #include "mozilla/dom/SVGLengthBinding.h"
19 #include "mozilla/gfx/2D.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/SVGContentUtils.h"
23 using namespace mozilla::gfx;
25 namespace mozilla {
26 namespace dom {
28 SVGElement::NumberInfo SVGGeometryElement::sNumberInfo = {nsGkAtoms::pathLength,
29 0, false};
31 //----------------------------------------------------------------------
32 // Implementation
34 SVGGeometryElement::SVGGeometryElement(
35 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
36 : SVGGeometryElementBase(std::move(aNodeInfo)) {}
38 SVGElement::NumberAttributesInfo SVGGeometryElement::GetNumberInfo() {
39 return NumberAttributesInfo(&mPathLength, &sNumberInfo, 1);
42 nsresult SVGGeometryElement::AfterSetAttr(int32_t aNamespaceID, nsAtom* aName,
43 const nsAttrValue* aValue,
44 const nsAttrValue* aOldValue,
45 nsIPrincipal* aSubjectPrincipal,
46 bool aNotify) {
47 if (mCachedPath && aNamespaceID == kNameSpaceID_None &&
48 AttributeDefinesGeometry(aName)) {
49 mCachedPath = nullptr;
51 return SVGGeometryElementBase::AfterSetAttr(
52 aNamespaceID, aName, aValue, aOldValue, aSubjectPrincipal, aNotify);
55 bool SVGGeometryElement::IsNodeOfType(uint32_t aFlags) const {
56 return !(aFlags & ~(eSHAPE | eUSE_TARGET));
59 bool SVGGeometryElement::AttributeDefinesGeometry(const nsAtom* aName) {
60 if (aName == nsGkAtoms::pathLength) {
61 return true;
64 // Check for SVGAnimatedLength attribute
65 LengthAttributesInfo info = GetLengthInfo();
66 for (uint32_t i = 0; i < info.mLengthCount; i++) {
67 if (aName == info.mLengthInfo[i].mName) {
68 return true;
72 return false;
75 bool SVGGeometryElement::GeometryDependsOnCoordCtx() {
76 // Check the SVGAnimatedLength attribute
77 LengthAttributesInfo info =
78 const_cast<SVGGeometryElement*>(this)->GetLengthInfo();
79 for (uint32_t i = 0; i < info.mLengthCount; i++) {
80 if (info.mLengths[i].GetSpecifiedUnitType() ==
81 SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE) {
82 return true;
85 return false;
88 bool SVGGeometryElement::IsMarkable() { return false; }
90 void SVGGeometryElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {}
92 already_AddRefed<Path> SVGGeometryElement::GetOrBuildPath(
93 const DrawTarget* aDrawTarget, FillRule aFillRule) {
94 // We only cache the path if it matches the backend used for screen painting,
95 // and it's not a capturing drawtarget. A capturing DT might start using the
96 // the Path object on a different thread (OMTP), and we might have a data race
97 // if we keep a handle to it.
98 bool cacheable = (aDrawTarget->GetBackendType() ==
99 gfxPlatform::GetPlatform()->GetDefaultContentBackend()) &&
100 !aDrawTarget->IsCaptureDT();
102 if (cacheable && mCachedPath && mCachedPath->GetFillRule() == aFillRule &&
103 aDrawTarget->GetBackendType() == mCachedPath->GetBackendType()) {
104 RefPtr<Path> path(mCachedPath);
105 return path.forget();
107 RefPtr<PathBuilder> builder = aDrawTarget->CreatePathBuilder(aFillRule);
108 RefPtr<Path> path = BuildPath(builder);
109 if (cacheable) {
110 mCachedPath = path;
112 return path.forget();
115 already_AddRefed<Path> SVGGeometryElement::GetOrBuildPathForMeasuring() {
116 RefPtr<DrawTarget> drawTarget =
117 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
118 FillRule fillRule = mCachedPath ? mCachedPath->GetFillRule() : GetFillRule();
119 return GetOrBuildPath(drawTarget, fillRule);
122 // This helper is currently identical to GetOrBuildPathForMeasuring.
123 // We keep it a separate method because for length measuring purpose,
124 // fillRule isn't really needed. Derived class (e.g. SVGPathElement)
125 // may override GetOrBuildPathForMeasuring() to ignore fillRule. And
126 // GetOrBuildPathForMeasuring() itself may be modified in the future.
127 already_AddRefed<Path> SVGGeometryElement::GetOrBuildPathForHitTest() {
128 RefPtr<DrawTarget> drawTarget =
129 gfxPlatform::GetPlatform()->ScreenReferenceDrawTarget();
130 FillRule fillRule = mCachedPath ? mCachedPath->GetFillRule() : GetFillRule();
131 return GetOrBuildPath(drawTarget, fillRule);
134 bool SVGGeometryElement::IsGeometryChangedViaCSS(
135 ComputedStyle const& aNewStyle, ComputedStyle const& aOldStyle) const {
136 nsAtom* name = NodeInfo()->NameAtom();
137 if (name == nsGkAtoms::rect) {
138 return SVGRectElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
140 if (name == nsGkAtoms::circle) {
141 return SVGCircleElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
143 if (name == nsGkAtoms::ellipse) {
144 return SVGEllipseElement::IsLengthChangedViaCSS(aNewStyle, aOldStyle);
146 return false;
149 FillRule SVGGeometryElement::GetFillRule() {
150 FillRule fillRule =
151 FillRule::FILL_WINDING; // Equivalent to StyleFillRule::Nonzero
153 bool res = SVGGeometryProperty::DoForComputedStyle(
154 this, [&](const ComputedStyle* s) {
155 const auto* styleSVG = s->StyleSVG();
157 MOZ_ASSERT(styleSVG->mFillRule == StyleFillRule::Nonzero ||
158 styleSVG->mFillRule == StyleFillRule::Evenodd);
160 if (styleSVG->mFillRule == StyleFillRule::Evenodd) {
161 fillRule = FillRule::FILL_EVEN_ODD;
165 if (!res) {
166 NS_WARNING("Couldn't get ComputedStyle for content in GetFillRule");
169 return fillRule;
172 static Point GetPointFrom(const DOMPointInit& aPoint) {
173 return Point(aPoint.mX, aPoint.mY);
176 bool SVGGeometryElement::IsPointInFill(const DOMPointInit& aPoint) {
177 auto point = GetPointFrom(aPoint);
179 RefPtr<Path> path = GetOrBuildPathForHitTest();
180 if (!path) {
181 return false;
184 return path->ContainsPoint(point, {});
187 bool SVGGeometryElement::IsPointInStroke(const DOMPointInit& aPoint) {
188 auto point = GetPointFrom(aPoint);
190 RefPtr<Path> path = GetOrBuildPathForHitTest();
191 if (!path) {
192 return false;
194 if (nsCOMPtr<Document> doc = GetComposedDoc()) {
195 doc->FlushPendingNotifications(FlushType::Layout);
198 bool res = false;
199 SVGGeometryProperty::DoForComputedStyle(this, [&](const ComputedStyle* s) {
200 // Per spec, we should take vector-effect into account.
201 if (s->StyleSVGReset()->HasNonScalingStroke()) {
202 auto mat = SVGContentUtils::GetCTM(this, true);
203 if (mat.HasNonTranslation()) {
204 // We have non-scaling-stroke as well as a non-translation transform.
205 // We should transform the path first then apply the stroke on the
206 // transformed path to preserve the stroke-width.
207 RefPtr<PathBuilder> builder = path->TransformedCopyToBuilder(mat);
209 path = builder->Finish();
210 point = mat.TransformPoint(point);
214 SVGContentUtils::AutoStrokeOptions strokeOptions;
215 SVGContentUtils::GetStrokeOptions(&strokeOptions, this, s, nullptr);
217 res = path->StrokeContainsPoint(strokeOptions, point, {});
220 return res;
223 float SVGGeometryElement::GetTotalLength() {
224 RefPtr<Path> flat = GetOrBuildPathForMeasuring();
225 return flat ? flat->ComputeLength() : 0.f;
228 already_AddRefed<DOMSVGPoint> SVGGeometryElement::GetPointAtLength(
229 float distance, ErrorResult& rv) {
230 RefPtr<Path> path = GetOrBuildPathForMeasuring();
231 if (!path) {
232 rv.ThrowInvalidStateError("No path available for measuring");
233 return nullptr;
236 RefPtr<DOMSVGPoint> point = new DOMSVGPoint(path->ComputePointAtLength(
237 clamped(distance, 0.f, path->ComputeLength())));
238 return point.forget();
241 float SVGGeometryElement::GetPathLengthScale(PathLengthScaleForType aFor) {
242 MOZ_ASSERT(aFor == eForTextPath || aFor == eForStroking, "Unknown enum");
243 if (mPathLength.IsExplicitlySet()) {
244 float authorsPathLengthEstimate = mPathLength.GetAnimValue();
245 if (authorsPathLengthEstimate > 0) {
246 RefPtr<Path> path = GetOrBuildPathForMeasuring();
247 if (!path) {
248 // The path is empty or invalid so its length must be zero and
249 // we know that 0 / authorsPathLengthEstimate = 0.
250 return 0.0;
252 if (aFor == eForTextPath) {
253 // For textPath, a transform on the referenced path affects the
254 // textPath layout, so when calculating the actual path length
255 // we need to take that into account.
256 gfxMatrix matrix = PrependLocalTransformsTo(gfxMatrix());
257 if (!matrix.IsIdentity()) {
258 RefPtr<PathBuilder> builder =
259 path->TransformedCopyToBuilder(ToMatrix(matrix));
260 path = builder->Finish();
263 return path->ComputeLength() / authorsPathLengthEstimate;
266 return 1.0;
269 already_AddRefed<DOMSVGAnimatedNumber> SVGGeometryElement::PathLength() {
270 return mPathLength.ToDOMAnimatedNumber(this);
273 } // namespace dom
274 } // namespace mozilla