/work/obj-fuzz/dist/include/mozilla/dom/SVGPathData.h
Line | Count | Source (jump to first uncovered line) |
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 | | #ifndef MOZILLA_SVGPATHDATA_H__ |
8 | | #define MOZILLA_SVGPATHDATA_H__ |
9 | | |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsDebug.h" |
12 | | #include "nsIContent.h" |
13 | | #include "nsINode.h" |
14 | | #include "nsIWeakReferenceUtils.h" |
15 | | #include "mozilla/gfx/2D.h" |
16 | | #include "mozilla/gfx/Types.h" |
17 | | #include "mozilla/MemoryReporting.h" |
18 | | #include "mozilla/RefPtr.h" |
19 | | #include "nsSVGElement.h" |
20 | | #include "nsTArray.h" |
21 | | |
22 | | #include <string.h> |
23 | | |
24 | | class nsSVGPathDataParser; // IWYU pragma: keep |
25 | | |
26 | | struct nsSVGMark; |
27 | | |
28 | | namespace mozilla { |
29 | | |
30 | | /** |
31 | | * ATTENTION! WARNING! WATCH OUT!! |
32 | | * |
33 | | * Consumers that modify objects of this type absolutely MUST keep the DOM |
34 | | * wrappers for those lists (if any) in sync!! That's why this class is so |
35 | | * locked down. |
36 | | * |
37 | | * The DOM wrapper class for this class is DOMSVGPathSegList. |
38 | | * |
39 | | * This class is not called |class SVGPathSegList| for one very good reason; |
40 | | * this class does not provide a list of "SVGPathSeg" items, it provides an |
41 | | * array of floats into which path segments are encoded. See the paragraphs |
42 | | * that follow for why. Note that the Length() method returns the number of |
43 | | * floats in our array, not the number of encoded segments, and the index |
44 | | * operator indexes floats in the array, not segments. If this class were |
45 | | * called SVGPathSegList the names of these methods would be very misleading. |
46 | | * |
47 | | * The reason this class is designed in this way is because there are many |
48 | | * different types of path segment, each taking a different numbers of |
49 | | * arguments. We want to store the segments in an nsTArray to avoid individual |
50 | | * allocations for each item, but the different size of segments means we can't |
51 | | * have one single segment type for the nsTArray (not without using a space |
52 | | * wasteful union or something similar). Since the internal code does not need |
53 | | * to index into the list (the DOM wrapper does, but it handles that itself) |
54 | | * the obvious solution is to have the items in this class take up variable |
55 | | * width and have the internal code iterate over these lists rather than index |
56 | | * into them. |
57 | | * |
58 | | * Implementing indexing to segments with O(1) performance would require us to |
59 | | * allocate and maintain a separate segment index table (keeping that table in |
60 | | * sync when items are inserted or removed from the list). So long as the |
61 | | * internal code doesn't require indexing to segments, we can avoid that |
62 | | * overhead and additional complexity. |
63 | | * |
64 | | * Segment encoding: the first float in the encoding of a segment contains the |
65 | | * segment's type. The segment's type is encoded to/decoded from this float |
66 | | * using the static methods SVGPathSegUtils::EncodeType(uint32_t)/ |
67 | | * SVGPathSegUtils::DecodeType(float). If the path segment type in question |
68 | | * takes any arguments then these follow the first float, and are in the same |
69 | | * order as they are given in a <path> element's 'd' attribute (NOT in the |
70 | | * order of the createSVGPathSegXxx() methods' arguments from the SVG DOM |
71 | | * interface SVGPathElement, which are different...grr). Consumers can use |
72 | | * SVGPathSegUtils::ArgCountForType(type) to determine how many arguments |
73 | | * there are (if any), and thus where the current encoded segment ends, and |
74 | | * where the next segment (if any) begins. |
75 | | */ |
76 | | class SVGPathData |
77 | | { |
78 | | friend class SVGAnimatedPathSegList; |
79 | | friend class DOMSVGPathSegList; |
80 | | friend class DOMSVGPathSeg; |
81 | | friend class ::nsSVGPathDataParser; |
82 | | // nsSVGPathDataParser will not keep wrappers in sync, so consumers |
83 | | // are responsible for that! |
84 | | |
85 | | typedef gfx::DrawTarget DrawTarget; |
86 | | typedef gfx::Path Path; |
87 | | typedef gfx::PathBuilder PathBuilder; |
88 | | typedef gfx::FillRule FillRule; |
89 | | typedef gfx::Float Float; |
90 | | typedef gfx::CapStyle CapStyle; |
91 | | |
92 | | public: |
93 | | typedef const float* const_iterator; |
94 | | |
95 | | SVGPathData(){} |
96 | | ~SVGPathData(){} |
97 | | |
98 | | // Only methods that don't make/permit modification to this list are public. |
99 | | // Only our friend classes can access methods that may change us. |
100 | | |
101 | | /// This may return an incomplete string on OOM, but that's acceptable. |
102 | | void GetValueAsString(nsAString& aValue) const; |
103 | | |
104 | | bool IsEmpty() const { |
105 | | return mData.IsEmpty(); |
106 | | } |
107 | | |
108 | | #ifdef DEBUG |
109 | | /** |
110 | | * This method iterates over the encoded segment data and counts the number |
111 | | * of segments we currently have. |
112 | | */ |
113 | | uint32_t CountItems() const; |
114 | | #endif |
115 | | |
116 | | /** |
117 | | * Returns the number of *floats* in the encoding array, and NOT the number |
118 | | * of segments encoded in this object. (For that, see CountItems() above.) |
119 | | */ |
120 | | uint32_t Length() const { |
121 | | return mData.Length(); |
122 | | } |
123 | | |
124 | 0 | const float& operator[](uint32_t aIndex) const { |
125 | 0 | return mData[aIndex]; |
126 | 0 | } |
127 | | |
128 | | // Used by nsSMILCompositor to check if the cached base val is out of date |
129 | | bool operator==(const SVGPathData& rhs) const { |
130 | | // We use memcmp so that we don't need to worry that the data encoded in |
131 | | // the first float may have the same bit pattern as a NaN. |
132 | | return mData.Length() == rhs.mData.Length() && |
133 | | memcmp(mData.Elements(), rhs.mData.Elements(), |
134 | | mData.Length() * sizeof(float)) == 0; |
135 | | } |
136 | | |
137 | 0 | bool SetCapacity(uint32_t aSize) { |
138 | 0 | return mData.SetCapacity(aSize, fallible); |
139 | 0 | } |
140 | | |
141 | 0 | void Compact() { |
142 | 0 | mData.Compact(); |
143 | 0 | } |
144 | | |
145 | | |
146 | | float GetPathLength() const; |
147 | | |
148 | | uint32_t GetPathSegAtLength(float aLength) const; |
149 | | |
150 | | void GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const; |
151 | | |
152 | | /** |
153 | | * Returns true, except on OOM, in which case returns false. |
154 | | */ |
155 | | bool GetSegmentLengths(nsTArray<double> *aLengths) const; |
156 | | |
157 | | /** |
158 | | * Returns true, except on OOM, in which case returns false. |
159 | | */ |
160 | | bool GetDistancesFromOriginToEndsOfVisibleSegments(FallibleTArray<double> *aArray) const; |
161 | | |
162 | | /** |
163 | | * This returns a path without the extra little line segments that |
164 | | * ApproximateZeroLengthSubpathSquareCaps can insert if we have square-caps. |
165 | | * See the comment for that function for more info on that. |
166 | | */ |
167 | | already_AddRefed<Path> BuildPathForMeasuring() const; |
168 | | |
169 | | already_AddRefed<Path> BuildPath(PathBuilder* aBuilder, |
170 | | uint8_t aCapStyle, |
171 | | Float aStrokeWidth) const; |
172 | | /** |
173 | | * This function tries to build the path by an array of StylePathCommand, |
174 | | * which is generated by cbindgen from Rust (see ServoStyleConsts.h). |
175 | | * Basically, this is a variant of the above BuildPath() functions. |
176 | | */ |
177 | | static already_AddRefed<Path> |
178 | | BuildPath(const nsTArray<StylePathCommand>& aPath, |
179 | | PathBuilder* aBuilder, |
180 | | uint8_t aCapStyle, |
181 | | Float aStrokeWidth, |
182 | | float aZoomFactor = 1.0); |
183 | | |
184 | | const_iterator begin() const { return mData.Elements(); } |
185 | | const_iterator end() const { return mData.Elements() + mData.Length(); } |
186 | | |
187 | | // memory reporting methods |
188 | | size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
189 | | size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; |
190 | | |
191 | | // Access to methods that can modify objects of this type is deliberately |
192 | | // limited. This is to reduce the chances of someone modifying objects of |
193 | | // this type without taking the necessary steps to keep DOM wrappers in sync. |
194 | | // If you need wider access to these methods, consider adding a method to |
195 | | // SVGAnimatedPathSegList and having that class act as an intermediary so it |
196 | | // can take care of keeping DOM wrappers in sync. |
197 | | |
198 | | protected: |
199 | | typedef float* iterator; |
200 | | |
201 | | /** |
202 | | * This may fail on OOM if the internal capacity needs to be increased, in |
203 | | * which case the list will be left unmodified. |
204 | | */ |
205 | | nsresult CopyFrom(const SVGPathData& rhs); |
206 | | |
207 | 0 | float& operator[](uint32_t aIndex) { |
208 | 0 | return mData[aIndex]; |
209 | 0 | } |
210 | | |
211 | | /** |
212 | | * This may fail (return false) on OOM if the internal capacity is being |
213 | | * increased, in which case the list will be left unmodified. |
214 | | */ |
215 | | bool SetLength(uint32_t aLength) { |
216 | | return mData.SetLength(aLength, fallible); |
217 | | } |
218 | | |
219 | | nsresult SetValueFromString(const nsAString& aValue); |
220 | | |
221 | | void Clear() { |
222 | | mData.Clear(); |
223 | | } |
224 | | |
225 | | // Our DOM wrappers have direct access to our mData, so they directly |
226 | | // manipulate it rather than us implementing: |
227 | | // |
228 | | // * InsertItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs); |
229 | | // * ReplaceItem(uint32_t aDataIndex, uint32_t aType, const float *aArgs); |
230 | | // * RemoveItem(uint32_t aDataIndex); |
231 | | // * bool AppendItem(uint32_t aType, const float *aArgs); |
232 | | |
233 | | nsresult AppendSeg(uint32_t aType, ...); // variable number of float args |
234 | | |
235 | | iterator begin() { return mData.Elements(); } |
236 | | iterator end() { return mData.Elements() + mData.Length(); } |
237 | | |
238 | | FallibleTArray<float> mData; |
239 | | }; |
240 | | |
241 | | |
242 | | /** |
243 | | * This SVGPathData subclass is for SVGPathSegListSMILType which needs to |
244 | | * have write access to the lists it works with. |
245 | | * |
246 | | * Instances of this class do not have DOM wrappers that need to be kept in |
247 | | * sync, so we can safely expose any protected base class methods required by |
248 | | * the SMIL code. |
249 | | */ |
250 | | class SVGPathDataAndInfo final : public SVGPathData |
251 | | { |
252 | | public: |
253 | | explicit SVGPathDataAndInfo(nsSVGElement *aElement = nullptr) |
254 | | : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) |
255 | | {} |
256 | | |
257 | | void SetElement(nsSVGElement *aElement) { |
258 | | mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); |
259 | | } |
260 | | |
261 | | nsSVGElement* Element() const { |
262 | | nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); |
263 | | return static_cast<nsSVGElement*>(e.get()); |
264 | | } |
265 | | |
266 | | nsresult CopyFrom(const SVGPathDataAndInfo& rhs) { |
267 | | mElement = rhs.mElement; |
268 | | return SVGPathData::CopyFrom(rhs); |
269 | | } |
270 | | |
271 | | /** |
272 | | * Returns true if this object is an "identity" value, from the perspective |
273 | | * of SMIL. In other words, returns true until the initial value set up in |
274 | | * SVGPathSegListSMILType::Init() has been changed with a SetElement() call. |
275 | | */ |
276 | | bool IsIdentity() const { |
277 | | if (!mElement) { |
278 | | MOZ_ASSERT(IsEmpty(), "target element propagation failure"); |
279 | | return true; |
280 | | } |
281 | | return false; |
282 | | } |
283 | | |
284 | | /** |
285 | | * Exposed so that SVGPathData baseVals can be copied to |
286 | | * SVGPathDataAndInfo objects. Note that callers should also call |
287 | | * SetElement() when using this method! |
288 | | */ |
289 | | using SVGPathData::CopyFrom; |
290 | | |
291 | | // Exposed since SVGPathData objects can be modified. |
292 | | using SVGPathData::iterator; |
293 | | using SVGPathData::operator[]; |
294 | | using SVGPathData::SetLength; |
295 | | using SVGPathData::begin; |
296 | | using SVGPathData::end; |
297 | | |
298 | | private: |
299 | | // We must keep a weak reference to our element because we may belong to a |
300 | | // cached baseVal nsSMILValue. See the comments starting at: |
301 | | // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15 |
302 | | // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 |
303 | | nsWeakPtr mElement; |
304 | | }; |
305 | | |
306 | | } // namespace mozilla |
307 | | |
308 | | #endif // MOZILLA_SVGPATHDATA_H__ |