/src/mozilla-central/dom/svg/SVGLengthList.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_SVGLENGTHLIST_H__ |
8 | | #define MOZILLA_SVGLENGTHLIST_H__ |
9 | | |
10 | | #include "nsCOMPtr.h" |
11 | | #include "nsDebug.h" |
12 | | #include "nsIContent.h" |
13 | | #include "nsINode.h" |
14 | | #include "nsIWeakReferenceUtils.h" |
15 | | #include "nsSVGElement.h" |
16 | | #include "nsTArray.h" |
17 | | #include "SVGLength.h" |
18 | | #include "mozilla/dom/SVGLengthBinding.h" |
19 | | |
20 | | namespace mozilla { |
21 | | |
22 | | /** |
23 | | * ATTENTION! WARNING! WATCH OUT!! |
24 | | * |
25 | | * Consumers that modify objects of this type absolutely MUST keep the DOM |
26 | | * wrappers for those lists (if any) in sync!! That's why this class is so |
27 | | * locked down. |
28 | | * |
29 | | * The DOM wrapper class for this class is DOMSVGLengthList. |
30 | | */ |
31 | | class SVGLengthList |
32 | | { |
33 | | friend class SVGAnimatedLengthList; |
34 | | friend class DOMSVGLengthList; |
35 | | friend class DOMSVGLength; |
36 | | |
37 | | public: |
38 | | |
39 | 0 | SVGLengthList(){} |
40 | 0 | ~SVGLengthList(){} |
41 | | |
42 | | // Only methods that don't make/permit modification to this list are public. |
43 | | // Only our friend classes can access methods that may change us. |
44 | | |
45 | | /// This may return an incomplete string on OOM, but that's acceptable. |
46 | | void GetValueAsString(nsAString& aValue) const; |
47 | | |
48 | 0 | bool IsEmpty() const { |
49 | 0 | return mLengths.IsEmpty(); |
50 | 0 | } |
51 | | |
52 | 0 | uint32_t Length() const { |
53 | 0 | return mLengths.Length(); |
54 | 0 | } |
55 | | |
56 | 0 | const SVGLength& operator[](uint32_t aIndex) const { |
57 | 0 | return mLengths[aIndex]; |
58 | 0 | } |
59 | | |
60 | | bool operator==(const SVGLengthList& rhs) const; |
61 | | |
62 | 0 | bool SetCapacity(uint32_t size) { |
63 | 0 | return mLengths.SetCapacity(size, fallible); |
64 | 0 | } |
65 | | |
66 | 0 | void Compact() { |
67 | 0 | mLengths.Compact(); |
68 | 0 | } |
69 | | |
70 | | // Access to methods that can modify objects of this type is deliberately |
71 | | // limited. This is to reduce the chances of someone modifying objects of |
72 | | // this type without taking the necessary steps to keep DOM wrappers in sync. |
73 | | // If you need wider access to these methods, consider adding a method to |
74 | | // SVGAnimatedLengthList and having that class act as an intermediary so it |
75 | | // can take care of keeping DOM wrappers in sync. |
76 | | |
77 | | protected: |
78 | | |
79 | | /** |
80 | | * This may fail on OOM if the internal capacity needs to be increased, in |
81 | | * which case the list will be left unmodified. |
82 | | */ |
83 | | nsresult CopyFrom(const SVGLengthList& rhs); |
84 | | |
85 | 0 | SVGLength& operator[](uint32_t aIndex) { |
86 | 0 | return mLengths[aIndex]; |
87 | 0 | } |
88 | | |
89 | | /** |
90 | | * This may fail (return false) on OOM if the internal capacity is being |
91 | | * increased, in which case the list will be left unmodified. |
92 | | */ |
93 | 0 | bool SetLength(uint32_t aNumberOfItems) { |
94 | 0 | return mLengths.SetLength(aNumberOfItems, fallible); |
95 | 0 | } |
96 | | |
97 | | private: |
98 | | |
99 | | // Marking the following private only serves to show which methods are only |
100 | | // used by our friend classes (as opposed to our subclasses) - it doesn't |
101 | | // really provide additional safety. |
102 | | |
103 | | nsresult SetValueFromString(const nsAString& aValue); |
104 | | |
105 | 0 | void Clear() { |
106 | 0 | mLengths.Clear(); |
107 | 0 | } |
108 | | |
109 | 0 | bool InsertItem(uint32_t aIndex, const SVGLength &aLength) { |
110 | 0 | if (aIndex >= mLengths.Length()) aIndex = mLengths.Length(); |
111 | 0 | return !!mLengths.InsertElementAt(aIndex, aLength, fallible); |
112 | 0 | } |
113 | | |
114 | 0 | void ReplaceItem(uint32_t aIndex, const SVGLength &aLength) { |
115 | 0 | MOZ_ASSERT(aIndex < mLengths.Length(), |
116 | 0 | "DOM wrapper caller should have raised INDEX_SIZE_ERR"); |
117 | 0 | mLengths[aIndex] = aLength; |
118 | 0 | } |
119 | | |
120 | 0 | void RemoveItem(uint32_t aIndex) { |
121 | 0 | MOZ_ASSERT(aIndex < mLengths.Length(), |
122 | 0 | "DOM wrapper caller should have raised INDEX_SIZE_ERR"); |
123 | 0 | mLengths.RemoveElementAt(aIndex); |
124 | 0 | } |
125 | | |
126 | 0 | bool AppendItem(SVGLength aLength) { |
127 | 0 | return !!mLengths.AppendElement(aLength, fallible); |
128 | 0 | } |
129 | | |
130 | | protected: |
131 | | |
132 | | /* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>: |
133 | | * |
134 | | * It might seem like we should use AutoTArray<SVGLength, 1> instead of |
135 | | * nsTArray<SVGLength>. That would preallocate space for one SVGLength and |
136 | | * avoid an extra memory allocation call in the common case of the 'x' |
137 | | * and 'y' attributes each containing a single length (and the 'dx' and 'dy' |
138 | | * attributes being empty). However, consider this: |
139 | | * |
140 | | * An empty nsTArray uses sizeof(Header*). An AutoTArray<class E, |
141 | | * uint32_t N> on the other hand uses sizeof(Header*) + |
142 | | * (2 * sizeof(uint32_t)) + (N * sizeof(E)), which for one SVGLength is |
143 | | * sizeof(Header*) + 16 bytes. |
144 | | * |
145 | | * Now consider that for text elements we have four length list attributes |
146 | | * (x, y, dx, dy), each of which can have a baseVal and an animVal list. If |
147 | | * we were to go the AutoTArray<SVGLength, 1> route for each of these, we'd |
148 | | * end up using at least 160 bytes for these four attributes alone, even |
149 | | * though we only need storage for two SVGLengths (16 bytes) in the common |
150 | | * case!! |
151 | | * |
152 | | * A compromise might be to template SVGLengthList to allow |
153 | | * SVGAnimatedLengthList to preallocate space for an SVGLength for the |
154 | | * baseVal lists only, and that would cut the space used by the four |
155 | | * attributes to 96 bytes. Taking that even further and templating |
156 | | * SVGAnimatedLengthList too in order to only use nsTArray for 'dx' and 'dy' |
157 | | * would reduce the storage further to 64 bytes. Having different types makes |
158 | | * things more complicated for code that needs to look at the lists though. |
159 | | * In fact it also makes things more complicated when it comes to storing the |
160 | | * lists. |
161 | | * |
162 | | * It may be worth considering using nsAttrValue for length lists instead of |
163 | | * storing them directly on the element. |
164 | | */ |
165 | | FallibleTArray<SVGLength> mLengths; |
166 | | }; |
167 | | |
168 | | |
169 | | /** |
170 | | * This SVGLengthList subclass is for SVGLengthListSMILType which needs to know |
171 | | * which element and attribute a length list belongs to so that it can convert |
172 | | * between unit types if necessary. |
173 | | */ |
174 | | class SVGLengthListAndInfo : public SVGLengthList |
175 | | { |
176 | | public: |
177 | | |
178 | | SVGLengthListAndInfo() |
179 | | : mElement(nullptr) |
180 | | , mAxis(0) |
181 | | , mCanZeroPadList(false) |
182 | 0 | {} |
183 | | |
184 | | SVGLengthListAndInfo(nsSVGElement *aElement, uint8_t aAxis, bool aCanZeroPadList) |
185 | | : mElement(do_GetWeakReference(static_cast<nsINode*>(aElement))) |
186 | | , mAxis(aAxis) |
187 | | , mCanZeroPadList(aCanZeroPadList) |
188 | 0 | {} |
189 | | |
190 | 0 | void SetInfo(nsSVGElement *aElement, uint8_t aAxis, bool aCanZeroPadList) { |
191 | 0 | mElement = do_GetWeakReference(static_cast<nsINode*>(aElement)); |
192 | 0 | mAxis = aAxis; |
193 | 0 | mCanZeroPadList = aCanZeroPadList; |
194 | 0 | } |
195 | | |
196 | 0 | nsSVGElement* Element() const { |
197 | 0 | nsCOMPtr<nsIContent> e = do_QueryReferent(mElement); |
198 | 0 | return static_cast<nsSVGElement*>(e.get()); |
199 | 0 | } |
200 | | |
201 | | /** |
202 | | * Returns true if this object is an "identity" value, from the perspective |
203 | | * of SMIL. In other words, returns true until the initial value set up in |
204 | | * SVGLengthListSMILType::Init() has been changed with a SetInfo() call. |
205 | | */ |
206 | 0 | bool IsIdentity() const { |
207 | 0 | if (!mElement) { |
208 | 0 | MOZ_ASSERT(IsEmpty(), "target element propagation failure"); |
209 | 0 | return true; |
210 | 0 | } |
211 | 0 | return false; |
212 | 0 | } |
213 | | |
214 | 0 | uint8_t Axis() const { |
215 | 0 | MOZ_ASSERT(mElement, "Axis() isn't valid"); |
216 | 0 | return mAxis; |
217 | 0 | } |
218 | | |
219 | | /** |
220 | | * The value returned by this function depends on which attribute this object |
221 | | * is for. If appending a list of zeros to the attribute's list would have no |
222 | | * effect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then |
223 | | * this method will return true. If appending a list of zeros to the |
224 | | * attribute's list could *change* rendering (e.g. the attributes 'x' and 'y' |
225 | | * on <text>), then this method will return false. |
226 | | * |
227 | | * The reason that this method exists is because the SMIL code needs to know |
228 | | * what to do when it's asked to animate between lists of different length. |
229 | | * If this method returns true, then it can zero pad the short list before |
230 | | * carrying out its operations. However, in the case of the 'x' and 'y' |
231 | | * attributes on <text>, zero would mean "zero in the current coordinate |
232 | | * system", whereas we would want to pad shorter lists with the coordinates |
233 | | * at which glyphs would otherwise lie, which is almost certainly not zero! |
234 | | * Animating from/to zeros in this case would produce terrible results. |
235 | | * |
236 | | * Currently SVGLengthListSMILType simply disallows (drops) animation between |
237 | | * lists of different length if it can't zero pad a list. This is to avoid |
238 | | * having some authors create content that depends on undesirable behaviour |
239 | | * (which would make it difficult for us to fix the behavior in future). At |
240 | | * some point it would be nice to implement a callback to allow this code to |
241 | | * determine padding values for lists that can't be zero padded. See |
242 | | * https://bugzilla.mozilla.org/show_bug.cgi?id=573431 |
243 | | */ |
244 | 0 | bool CanZeroPadList() const { |
245 | 0 | //NS_ASSERTION(mElement, "CanZeroPadList() isn't valid"); |
246 | 0 | return mCanZeroPadList; |
247 | 0 | } |
248 | | |
249 | | // For the SMIL code. See comment in SVGLengthListSMILType::Add(). |
250 | 0 | void SetCanZeroPadList(bool aCanZeroPadList) { |
251 | 0 | mCanZeroPadList = aCanZeroPadList; |
252 | 0 | } |
253 | | |
254 | 0 | nsresult CopyFrom(const SVGLengthListAndInfo& rhs) { |
255 | 0 | mElement = rhs.mElement; |
256 | 0 | mAxis = rhs.mAxis; |
257 | 0 | mCanZeroPadList = rhs.mCanZeroPadList; |
258 | 0 | return SVGLengthList::CopyFrom(rhs); |
259 | 0 | } |
260 | | |
261 | | // Instances of this special subclass do not have DOM wrappers that we need |
262 | | // to worry about keeping in sync, so it's safe to expose any hidden base |
263 | | // class methods required by the SMIL code, as we do below. |
264 | | |
265 | | /** |
266 | | * Exposed so that SVGLengthList baseVals can be copied to |
267 | | * SVGLengthListAndInfo objects. Note that callers should also call |
268 | | * SetInfo() when using this method! |
269 | | */ |
270 | 0 | nsresult CopyFrom(const SVGLengthList& rhs) { |
271 | 0 | return SVGLengthList::CopyFrom(rhs); |
272 | 0 | } |
273 | 0 | const SVGLength& operator[](uint32_t aIndex) const { |
274 | 0 | return SVGLengthList::operator[](aIndex); |
275 | 0 | } |
276 | 0 | SVGLength& operator[](uint32_t aIndex) { |
277 | 0 | return SVGLengthList::operator[](aIndex); |
278 | 0 | } |
279 | 0 | bool SetLength(uint32_t aNumberOfItems) { |
280 | 0 | return SVGLengthList::SetLength(aNumberOfItems); |
281 | 0 | } |
282 | | |
283 | | private: |
284 | | // We must keep a weak reference to our element because we may belong to a |
285 | | // cached baseVal nsSMILValue. See the comments starting at: |
286 | | // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15 |
287 | | // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497 |
288 | | nsWeakPtr mElement; |
289 | | uint8_t mAxis; |
290 | | bool mCanZeroPadList; |
291 | | }; |
292 | | |
293 | | |
294 | | /** |
295 | | * This class wraps SVGLengthList objects to allow frame consumers to process |
296 | | * SVGLengthList objects as if they were simply a list of float values in user |
297 | | * units. When consumers request the value at a given index, this class |
298 | | * dynamically converts the corresponding SVGLength from its actual unit and |
299 | | * returns its value in user units. |
300 | | * |
301 | | * Consumers should check that the user unit values returned are finite. Even |
302 | | * if the consumer can guarantee the list's element has a valid viewport |
303 | | * ancestor to resolve percentage units against, and a valid presContext and |
304 | | * ComputedStyle to resolve absolute and em/ex units against, unit conversions |
305 | | * could still overflow. In that case the value returned will be |
306 | | * numeric_limits<float>::quiet_NaN(). |
307 | | */ |
308 | | class MOZ_STACK_CLASS SVGUserUnitList |
309 | | { |
310 | | public: |
311 | | |
312 | | SVGUserUnitList() |
313 | | : mList(nullptr) |
314 | | , mElement(nullptr) |
315 | | , mAxis(0) |
316 | 0 | {} |
317 | | |
318 | 0 | void Init(const SVGLengthList *aList, nsSVGElement *aElement, uint8_t aAxis) { |
319 | 0 | mList = aList; |
320 | 0 | mElement = aElement; |
321 | 0 | mAxis = aAxis; |
322 | 0 | } |
323 | | |
324 | 0 | void Clear() { |
325 | 0 | mList = nullptr; |
326 | 0 | } |
327 | | |
328 | 0 | bool IsEmpty() const { |
329 | 0 | return !mList || mList->IsEmpty(); |
330 | 0 | } |
331 | | |
332 | 0 | uint32_t Length() const { |
333 | 0 | return mList ? mList->Length() : 0; |
334 | 0 | } |
335 | | |
336 | | /// This may return a non-finite value |
337 | 0 | float operator[](uint32_t aIndex) const { |
338 | 0 | return (*mList)[aIndex].GetValueInUserUnits(mElement, mAxis); |
339 | 0 | } |
340 | | |
341 | 0 | bool HasPercentageValueAt(uint32_t aIndex) const { |
342 | 0 | const SVGLength& length = (*mList)[aIndex]; |
343 | 0 | return length.GetUnit() == dom::SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE; |
344 | 0 | } |
345 | | |
346 | | private: |
347 | | const SVGLengthList *mList; |
348 | | nsSVGElement *mElement; |
349 | | uint8_t mAxis; |
350 | | }; |
351 | | |
352 | | } // namespace mozilla |
353 | | |
354 | | #endif // MOZILLA_SVGLENGTHLIST_H__ |