Bug 1936278 - Prevent search mode chiclet from being dismissed when clicking in page...
[gecko.git] / dom / svg / SVGPathElement.cpp
blob0e8d559167dd628bcff400b9ee9bcb703ea970d0
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 "mozilla/dom/SVGPathElement.h"
9 #include <algorithm>
11 #include "SVGGeometryProperty.h"
12 #include "gfx2DGlue.h"
13 #include "gfxPlatform.h"
14 #include "nsGkAtoms.h"
15 #include "nsIFrame.h"
16 #include "nsStyleConsts.h"
17 #include "nsStyleStruct.h"
18 #include "nsWindowSizes.h"
19 #include "mozilla/dom/SVGPathElementBinding.h"
20 #include "mozilla/gfx/2D.h"
21 #include "mozilla/RefPtr.h"
22 #include "SVGPathSegUtils.h"
23 #include "mozilla/SVGContentUtils.h"
25 NS_IMPL_NS_NEW_SVG_ELEMENT(Path)
27 using namespace mozilla::gfx;
29 namespace mozilla::dom {
31 JSObject* SVGPathElement::WrapNode(JSContext* aCx,
32 JS::Handle<JSObject*> aGivenProto) {
33 return SVGPathElement_Binding::Wrap(aCx, this, aGivenProto);
36 //----------------------------------------------------------------------
37 // Implementation
39 SVGPathElement::SVGPathElement(
40 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
41 : SVGPathElementBase(std::move(aNodeInfo)) {}
43 //----------------------------------------------------------------------
44 // memory reporting methods
46 void SVGPathElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
47 size_t* aNodeSize) const {
48 SVGPathElementBase::AddSizeOfExcludingThis(aSizes, aNodeSize);
49 *aNodeSize += mD.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
52 //----------------------------------------------------------------------
53 // nsINode methods
55 NS_IMPL_ELEMENT_CLONE_WITH_INIT(SVGPathElement)
57 //----------------------------------------------------------------------
58 // SVGElement methods
60 /* virtual */
61 bool SVGPathElement::HasValidDimensions() const {
62 bool hasPath = false;
63 auto callback = [&](const ComputedStyle* s) {
64 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
65 hasPath =
66 styleSVGReset->mD.IsPath() && !styleSVGReset->mD.AsPath()._0.IsEmpty();
69 SVGGeometryProperty::DoForComputedStyle(this, callback);
70 // If hasPath is false, we may disable the pref of d property, so we fallback
71 // to check mD.
72 return hasPath || !mD.GetAnimValue().IsEmpty();
75 //----------------------------------------------------------------------
76 // nsIContent methods
78 NS_IMETHODIMP_(bool)
79 SVGPathElement::IsAttributeMapped(const nsAtom* name) const {
80 return name == nsGkAtoms::d || SVGPathElementBase::IsAttributeMapped(name);
83 already_AddRefed<Path> SVGPathElement::GetOrBuildPathForMeasuring() {
84 RefPtr<Path> path;
85 bool success = SVGGeometryProperty::DoForComputedStyle(
86 this, [&path](const ComputedStyle* s) {
87 const auto& d = s->StyleSVGReset()->mD;
88 if (d.IsNone()) {
89 return;
91 path = SVGPathData::BuildPathForMeasuring(d.AsPath()._0.AsSpan(),
92 s->EffectiveZoom().ToFloat());
93 });
94 return success ? path.forget()
95 : mD.GetAnimValue().BuildPathForMeasuring(1.0f);
98 //----------------------------------------------------------------------
99 // SVGGeometryElement methods
101 bool SVGPathElement::AttributeDefinesGeometry(const nsAtom* aName) {
102 return aName == nsGkAtoms::d || aName == nsGkAtoms::pathLength;
105 bool SVGPathElement::IsMarkable() { return true; }
107 void SVGPathElement::GetMarkPoints(nsTArray<SVGMark>* aMarks) {
108 auto callback = [aMarks](const ComputedStyle* s) {
109 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
110 if (styleSVGReset->mD.IsPath()) {
111 Span<const StylePathCommand> path =
112 styleSVGReset->mD.AsPath()._0.AsSpan();
113 SVGPathData::GetMarkerPositioningData(path, s->EffectiveZoom().ToFloat(),
114 aMarks);
118 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
119 return;
122 mD.GetAnimValue().GetMarkerPositioningData(1.0f, aMarks);
125 void SVGPathElement::GetAsSimplePath(SimplePath* aSimplePath) {
126 aSimplePath->Reset();
127 auto callback = [&](const ComputedStyle* s) {
128 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
129 if (styleSVGReset->mD.IsPath()) {
130 auto pathData = styleSVGReset->mD.AsPath()._0.AsSpan();
131 auto maybeRect = SVGPathToAxisAlignedRect(pathData);
132 if (maybeRect.isSome()) {
133 const Rect& r = *maybeRect;
134 float zoom = s->EffectiveZoom().ToFloat();
135 aSimplePath->SetRect(r.x * zoom, r.y * zoom, r.width * zoom,
136 r.height * zoom);
141 SVGGeometryProperty::DoForComputedStyle(this, callback);
144 already_AddRefed<Path> SVGPathElement::BuildPath(PathBuilder* aBuilder) {
145 // The Moz2D PathBuilder that our SVGPathData will be using only cares about
146 // the fill rule. However, in order to fulfill the requirements of the SVG
147 // spec regarding zero length sub-paths when square line caps are in use,
148 // SVGPathData needs to know our stroke-linecap style and, if "square", then
149 // also our stroke width. See the comment for
150 // ApproximateZeroLengthSubpathSquareCaps for more info.
152 auto strokeLineCap = StyleStrokeLinecap::Butt;
153 Float strokeWidth = 0;
154 RefPtr<Path> path;
156 auto callback = [&](const ComputedStyle* s) {
157 const nsStyleSVG* styleSVG = s->StyleSVG();
158 // Note: the path that we return may be used for hit-testing, and SVG
159 // exposes hit-testing of strokes that are not actually painted. For that
160 // reason we do not check for eStyleSVGPaintType_None or check the stroke
161 // opacity here.
162 if (styleSVG->mStrokeLinecap != StyleStrokeLinecap::Butt) {
163 strokeLineCap = styleSVG->mStrokeLinecap;
164 strokeWidth = SVGContentUtils::GetStrokeWidth(this, s, nullptr);
167 const auto& d = s->StyleSVGReset()->mD;
168 if (d.IsPath()) {
169 path = SVGPathData::BuildPath(d.AsPath()._0.AsSpan(), aBuilder,
170 strokeLineCap, strokeWidth, {}, {},
171 s->EffectiveZoom().ToFloat());
175 bool success = SVGGeometryProperty::DoForComputedStyle(this, callback);
176 if (success) {
177 return path.forget();
180 // Fallback to use the d attribute if it exists.
181 return mD.GetAnimValue().BuildPath(aBuilder, strokeLineCap, strokeWidth,
182 1.0f);
185 bool SVGPathElement::GetDistancesFromOriginToEndsOfVisibleSegments(
186 FallibleTArray<double>* aOutput) {
187 bool ret = false;
188 auto callback = [&ret, aOutput](const ComputedStyle* s) {
189 const auto& d = s->StyleSVGReset()->mD;
190 ret = d.IsNone() ||
191 SVGPathData::GetDistancesFromOriginToEndsOfVisibleSegments(
192 d.AsPath()._0.AsSpan(), aOutput);
195 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
196 return ret;
199 return mD.GetAnimValue().GetDistancesFromOriginToEndsOfVisibleSegments(
200 aOutput);
203 static bool PathIsClosed(Span<const StylePathCommand> aPath) {
204 return !aPath.IsEmpty() && aPath.rbegin()->IsClose();
207 // Offset paths (including references to SVG Paths) are closed loops only if the
208 // final command in the path list is a closepath command ("z" or "Z"), otherwise
209 // they are unclosed intervals.
210 // https://drafts.fxtf.org/motion/#path-distance
211 bool SVGPathElement::IsClosedLoop() const {
212 bool isClosed = false;
214 auto callback = [&](const ComputedStyle* s) {
215 const nsStyleSVGReset* styleSVGReset = s->StyleSVGReset();
216 if (styleSVGReset->mD.IsPath()) {
217 isClosed = PathIsClosed(styleSVGReset->mD.AsPath()._0.AsSpan());
221 if (SVGGeometryProperty::DoForComputedStyle(this, callback)) {
222 return isClosed;
225 return PathIsClosed(mD.GetAnimValue().AsSpan());
228 /* static */
229 bool SVGPathElement::IsDPropertyChangedViaCSS(const ComputedStyle& aNewStyle,
230 const ComputedStyle& aOldStyle) {
231 return aNewStyle.StyleSVGReset()->mD != aOldStyle.StyleSVGReset()->mD;
234 } // namespace mozilla::dom