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_
12 #include "nsIContent.h"
14 #include "nsIWeakReferenceUtils.h"
15 #include "SVGElement.h"
17 #include "SVGLength.h"
18 #include "mozilla/dom/SVGLengthBinding.h"
24 class DOMSVGLengthList
;
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
34 * The DOM wrapper class for this class is DOMSVGLengthList.
37 friend class dom::DOMSVGLength
;
38 friend class dom::DOMSVGLengthList
;
39 friend class SVGAnimatedLengthList
;
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
);
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.
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
&);
89 void SwapWith(SVGLengthList
& aOther
) {
90 mLengths
.SwapElements(aOther
.mLengths
);
93 SVGLength
& operator[](uint32_t aIndex
) { return mLengths
[aIndex
]; }
96 * This may fail (return false) on OOM if the internal capacity is being
97 * increased, in which case the list will be left unmodified.
99 bool SetLength(uint32_t aNumberOfItems
) {
100 return mLengths
.SetLength(aNumberOfItems
, fallible
);
104 // Marking the following private only serves to show which methods are only
105 // used by our friend classes (as opposed to our subclasses) - it doesn't
106 // really provide additional safety.
108 nsresult
SetValueFromString(const nsAString
& aValue
);
110 void Clear() { mLengths
.Clear(); }
112 bool InsertItem(uint32_t aIndex
, const SVGLength
& aLength
) {
113 if (aIndex
>= mLengths
.Length()) aIndex
= mLengths
.Length();
114 return !!mLengths
.InsertElementAt(aIndex
, aLength
, fallible
);
117 void ReplaceItem(uint32_t aIndex
, const SVGLength
& aLength
) {
118 MOZ_ASSERT(aIndex
< mLengths
.Length(),
119 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
120 mLengths
[aIndex
] = aLength
;
123 void RemoveItem(uint32_t aIndex
) {
124 MOZ_ASSERT(aIndex
< mLengths
.Length(),
125 "DOM wrapper caller should have raised INDEX_SIZE_ERR");
126 mLengths
.RemoveElementAt(aIndex
);
129 bool AppendItem(SVGLength aLength
) {
130 return !!mLengths
.AppendElement(aLength
, fallible
);
134 /* Rationale for using nsTArray<SVGLength> and not nsTArray<SVGLength, 1>:
136 * It might seem like we should use AutoTArray<SVGLength, 1> instead of
137 * nsTArray<SVGLength>. That would preallocate space for one SVGLength and
138 * avoid an extra memory allocation call in the common case of the 'x'
139 * and 'y' attributes each containing a single length (and the 'dx' and 'dy'
140 * attributes being empty). However, consider this:
142 * An empty nsTArray uses sizeof(Header*). An AutoTArray<class E,
143 * uint32_t N> on the other hand uses sizeof(Header*) +
144 * (2 * sizeof(uint32_t)) + (N * sizeof(E)), which for one SVGLength is
145 * sizeof(Header*) + 16 bytes.
147 * Now consider that for text elements we have four length list attributes
148 * (x, y, dx, dy), each of which can have a baseVal and an animVal list. If
149 * we were to go the AutoTArray<SVGLength, 1> route for each of these, we'd
150 * end up using at least 160 bytes for these four attributes alone, even
151 * though we only need storage for two SVGLengths (16 bytes) in the common
154 * A compromise might be to template SVGLengthList to allow
155 * SVGAnimatedLengthList to preallocate space for an SVGLength for the
156 * baseVal lists only, and that would cut the space used by the four
157 * attributes to 96 bytes. Taking that even further and templating
158 * SVGAnimatedLengthList too in order to only use nsTArray for 'dx' and 'dy'
159 * would reduce the storage further to 64 bytes. Having different types makes
160 * things more complicated for code that needs to look at the lists though.
161 * In fact it also makes things more complicated when it comes to storing the
164 * It may be worth considering using nsAttrValue for length lists instead of
165 * storing them directly on the element.
167 FallibleTArray
<SVGLength
> mLengths
;
171 * This SVGLengthList subclass is for SVGLengthListSMILType which needs to know
172 * which element and attribute a length list belongs to so that it can convert
173 * between unit types if necessary.
175 class SVGLengthListAndInfo
: public SVGLengthList
{
177 SVGLengthListAndInfo()
178 : mElement(nullptr), mAxis(0), mCanZeroPadList(false) {}
180 SVGLengthListAndInfo(dom::SVGElement
* aElement
, uint8_t aAxis
,
181 bool aCanZeroPadList
)
182 : mElement(do_GetWeakReference(static_cast<nsINode
*>(aElement
))),
184 mCanZeroPadList(aCanZeroPadList
) {}
186 void SetInfo(dom::SVGElement
* aElement
, uint8_t aAxis
, bool aCanZeroPadList
) {
187 mElement
= do_GetWeakReference(static_cast<nsINode
*>(aElement
));
189 mCanZeroPadList
= aCanZeroPadList
;
192 dom::SVGElement
* Element() const {
193 nsCOMPtr
<nsIContent
> e
= do_QueryReferent(mElement
);
194 return static_cast<dom::SVGElement
*>(e
.get());
198 * Returns true if this object is an "identity" value, from the perspective
199 * of SMIL. In other words, returns true until the initial value set up in
200 * SVGLengthListSMILType::Init() has been changed with a SetInfo() call.
202 bool IsIdentity() const {
204 MOZ_ASSERT(IsEmpty(), "target element propagation failure");
210 uint8_t Axis() const {
211 MOZ_ASSERT(mElement
, "Axis() isn't valid");
216 * The value returned by this function depends on which attribute this object
217 * is for. If appending a list of zeros to the attribute's list would have no
218 * effect on rendering (e.g. the attributes 'dx' and 'dy' on <text>), then
219 * this method will return true. If appending a list of zeros to the
220 * attribute's list could *change* rendering (e.g. the attributes 'x' and 'y'
221 * on <text>), then this method will return false.
223 * The reason that this method exists is because the SMIL code needs to know
224 * what to do when it's asked to animate between lists of different length.
225 * If this method returns true, then it can zero pad the short list before
226 * carrying out its operations. However, in the case of the 'x' and 'y'
227 * attributes on <text>, zero would mean "zero in the current coordinate
228 * system", whereas we would want to pad shorter lists with the coordinates
229 * at which glyphs would otherwise lie, which is almost certainly not zero!
230 * Animating from/to zeros in this case would produce terrible results.
232 * Currently SVGLengthListSMILType simply disallows (drops) animation between
233 * lists of different length if it can't zero pad a list. This is to avoid
234 * having some authors create content that depends on undesirable behaviour
235 * (which would make it difficult for us to fix the behavior in future). At
236 * some point it would be nice to implement a callback to allow this code to
237 * determine padding values for lists that can't be zero padded. See
238 * https://bugzilla.mozilla.org/show_bug.cgi?id=573431
240 bool CanZeroPadList() const {
241 // NS_ASSERTION(mElement, "CanZeroPadList() isn't valid");
242 return mCanZeroPadList
;
245 // For the SMIL code. See comment in SVGLengthListSMILType::Add().
246 void SetCanZeroPadList(bool aCanZeroPadList
) {
247 mCanZeroPadList
= aCanZeroPadList
;
250 nsresult
CopyFrom(const SVGLengthListAndInfo
& rhs
) {
251 mElement
= rhs
.mElement
;
253 mCanZeroPadList
= rhs
.mCanZeroPadList
;
254 return SVGLengthList::CopyFrom(rhs
);
257 // Instances of this special subclass do not have DOM wrappers that we need
258 // to worry about keeping in sync, so it's safe to expose any hidden base
259 // class methods required by the SMIL code, as we do below.
262 * Exposed so that SVGLengthList baseVals can be copied to
263 * SVGLengthListAndInfo objects. Note that callers should also call
264 * SetInfo() when using this method!
266 nsresult
CopyFrom(const SVGLengthList
& rhs
) {
267 return SVGLengthList::CopyFrom(rhs
);
269 const SVGLength
& operator[](uint32_t aIndex
) const {
270 return SVGLengthList::operator[](aIndex
);
272 SVGLength
& operator[](uint32_t aIndex
) {
273 return SVGLengthList::operator[](aIndex
);
275 bool SetLength(uint32_t aNumberOfItems
) {
276 return SVGLengthList::SetLength(aNumberOfItems
);
280 // We must keep a weak reference to our element because we may belong to a
281 // cached baseVal SMILValue. See the comments starting at:
282 // https://bugzilla.mozilla.org/show_bug.cgi?id=515116#c15
283 // See also https://bugzilla.mozilla.org/show_bug.cgi?id=653497
286 bool mCanZeroPadList
;
290 * This class wraps SVGLengthList objects to allow frame consumers to process
291 * SVGLengthList objects as if they were simply a list of float values in user
292 * units. When consumers request the value at a given index, this class
293 * dynamically converts the corresponding SVGLength from its actual unit and
294 * returns its value in user units.
296 * Consumers should check that the user unit values returned are finite. Even
297 * if the consumer can guarantee the list's element has a valid viewport
298 * ancestor to resolve percentage units against, and a valid presContext and
299 * ComputedStyle to resolve absolute and em/ex units against, unit conversions
300 * could still overflow. In that case the value returned will be
301 * numeric_limits<float>::quiet_NaN().
303 class MOZ_STACK_CLASS SVGUserUnitList
{
305 SVGUserUnitList() : mList(nullptr), mElement(nullptr), mAxis(0) {}
307 void Init(const SVGLengthList
* aList
, const dom::SVGElement
* aElement
,
314 void Clear() { mList
= nullptr; }
316 bool IsEmpty() const { return !mList
|| mList
->IsEmpty(); }
318 uint32_t Length() const { return mList
? mList
->Length() : 0; }
320 /// This may return a non-finite value
321 float operator[](uint32_t aIndex
) const {
322 return (*mList
)[aIndex
].GetValueInPixels(mElement
, mAxis
);
325 bool HasPercentageValueAt(uint32_t aIndex
) const {
326 const SVGLength
& length
= (*mList
)[aIndex
];
327 return length
.GetUnit() ==
328 dom::SVGLength_Binding::SVG_LENGTHTYPE_PERCENTAGE
;
332 const SVGLengthList
* mList
;
333 const dom::SVGElement
* mElement
;
337 } // namespace mozilla
339 #endif // DOM_SVG_SVGLENGTHLIST_H_