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