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