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_DOMSVGANIMATEDLENGTHLIST_H__
38 : #define MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
39 :
40 : #include "nsAutoPtr.h"
41 : #include "nsCOMPtr.h"
42 : #include "nsCycleCollectionParticipant.h"
43 : #include "nsIDOMSVGAnimatedLengthList.h"
44 : #include "nsSVGElement.h"
45 :
46 : namespace mozilla {
47 :
48 : class SVGAnimatedLengthList;
49 : class SVGLengthList;
50 : class DOMSVGLengthList;
51 :
52 : /**
53 : * Class DOMSVGAnimatedLengthList
54 : *
55 : * This class is used to create the DOM tearoff objects that wrap internal
56 : * SVGAnimatedLengthList objects. We have this internal-DOM split because DOM
57 : * classes are relatively heavy-weight objects with non-optimal interfaces for
58 : * internal code, and they're relatively infrequently used. Having separate
59 : * internal and DOM classes does add complexity - especially for lists where
60 : * the internal list and DOM lists (and their items) need to be kept in sync -
61 : * but it keeps the internal classes light and fast, and in 99% of cases
62 : * they're all that's used. DOM wrappers are only instantiated when script
63 : * demands it.
64 : *
65 : * Ownership model:
66 : *
67 : * The diagram below shows the ownership model between the various DOM objects
68 : * in the tree of DOM objects that correspond to an SVG length list attribute.
69 : * The angled brackets ">" and "<" denote a reference from one object to
70 : * another, where the "!" character denotes a strong reference, and the "~"
71 : * character denotes a weak reference.
72 : *
73 : * .----<!----. .----<!----. .----<!----.
74 : * | | | | | |
75 : * element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
76 : *
77 : * Rationale:
78 : *
79 : * The following three paragraphs explain the main three requirements that must
80 : * be met by any design. These are followed by an explanation of the rationale
81 : * behind our particular design.
82 : *
83 : * 1: DOMSVGAnimatedLengthList, DOMSVGLengthLists and DOMSVGLength get to their
84 : * internal counterparts via their element, and they use their element to send
85 : * out appropriate notifications when they change. Because of this, having
86 : * their element disappear out from under them would be very bad. To keep their
87 : * element alive at least as long as themselves, each of these classes must
88 : * contain a _strong_ reference (directly or indirectly) to their element.
89 : *
90 : * 2: Another central requirement of any design is the SVG specification's
91 : * requirement that script must always be given the exact same objects each
92 : * time it accesses a given object in a DOM object tree corresponding to an SVG
93 : * length list attribute. In practice "always" actually means "whenever script
94 : * has kept a references to a DOM object it previously accessed", since a
95 : * script will only be able to detect any difference in object identity if it
96 : * has a previous reference to compare against.
97 : *
98 : * 3: The wiggle room in the "same object" requirement leads us to a third
99 : * (self imposed) requirement: if script no longer has a reference to a given
100 : * DOM object from an object tree corresponding to an SVG length list
101 : * attribute, and if that object doesn't currently have any descendants, then
102 : * that object should be released to free up memory.
103 : *
104 : * To help in understanding our current design, consider this BROKEN design:
105 : *
106 : * .-------------------------------<!-------------------------.
107 : * |--------------------<!----------------. |
108 : * |----<!----. | |
109 : * | | | |
110 : * element ~> DOMSVGAnimatedLengthList !> DOMSVGLengthList !> DOMSVGLength
111 : *
112 : * Having all the objects keep a reference directly to their element like this
113 : * would reduce the number of dereferences that they need to make to get their
114 : * internal counterpart. Hovewer, this design does not meet the "same object"
115 : * requirement of the SVG specification. If script keeps a reference to a
116 : * DOMSVGLength or DOMSVGLengthList object, but not to that object's
117 : * DOMSVGAnimatedLengthList, then the DOMSVGAnimatedLengthList may be garbage
118 : * collected. We'd then have no way to return the same DOMSVGLength /
119 : * DOMSVGLengthList object that the script has a reference to if the script
120 : * went looking for it via the DOMSVGAnimatedLengthList property on the
121 : * element - we'd end up creating a fresh DOMSVGAnimatedLengthList, with no
122 : * knowlegde of the existing DOMSVGLengthList or DOMSVGLength object.
123 : *
124 : * The way we solve this problem is by making sure that parent objects cannot
125 : * die until all their children are dead by having child objects hold a strong
126 : * reference to their parent object. Note that this design means that the child
127 : * objects hold a strong reference to their element too, albeit indirectly via
128 : * the strong reference to their parent object:
129 : *
130 : * .----<!----. .----<!----. .----<!----.
131 : * | | | | | |
132 : * element ~> DOMSVGAnimatedLengthList ~> DOMSVGLengthList ~> DOMSVGLength
133 : *
134 : * One drawback of this design is that objects must look up their parent
135 : * chain to find their element, but that overhead is relatively small.
136 : */
137 : class DOMSVGAnimatedLengthList : public nsIDOMSVGAnimatedLengthList
138 : {
139 : friend class DOMSVGLengthList;
140 :
141 : public:
142 0 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
143 1464 : NS_DECL_CYCLE_COLLECTION_CLASS(DOMSVGAnimatedLengthList)
144 : NS_DECL_NSIDOMSVGANIMATEDLENGTHLIST
145 :
146 : /**
147 : * Factory method to create and return a DOMSVGAnimatedLengthList wrapper
148 : * for a given internal SVGAnimatedLengthList object. The factory takes care
149 : * of caching the object that it returns so that the same object can be
150 : * returned for the given SVGAnimatedLengthList each time it is requested.
151 : * The cached object is only removed from the cache when it is destroyed due
152 : * to there being no more references to it or to any of its descendant
153 : * objects. If that happens, any subsequent call requesting the DOM wrapper
154 : * for the SVGAnimatedLengthList will naturally result in a new
155 : * DOMSVGAnimatedLengthList being returned.
156 : */
157 : static already_AddRefed<DOMSVGAnimatedLengthList>
158 : GetDOMWrapper(SVGAnimatedLengthList *aList,
159 : nsSVGElement *aElement,
160 : PRUint8 aAttrEnum,
161 : PRUint8 aAxis);
162 :
163 : /**
164 : * This method returns the DOMSVGAnimatedLengthList wrapper for an internal
165 : * SVGAnimatedLengthList object if it currently has a wrapper. If it does
166 : * not, then nsnull is returned.
167 : */
168 : static DOMSVGAnimatedLengthList*
169 : GetDOMWrapperIfExists(SVGAnimatedLengthList *aList);
170 :
171 : /**
172 : * Called by internal code to notify us when we need to sync the length of
173 : * our baseVal DOM list with its internal list. This is called just prior to
174 : * the length of the internal baseVal list being changed so that any DOM list
175 : * items that need to be removed from the DOM list can first get their values
176 : * from their internal counterpart.
177 : *
178 : * The only time this method could fail is on OOM when trying to increase the
179 : * length of the DOM list. If that happens then this method simply clears the
180 : * list and returns. Callers just proceed as normal, and we simply accept
181 : * that the DOM list will be empty (until successfully set to a new value).
182 : */
183 : void InternalBaseValListWillChangeTo(const SVGLengthList& aNewValue);
184 : void InternalAnimValListWillChangeTo(const SVGLengthList& aNewValue);
185 :
186 : /**
187 : * Returns true if our attribute is animating (in which case our animVal is
188 : * not simply a mirror of our baseVal).
189 : */
190 : bool IsAnimating() const;
191 :
192 : private:
193 :
194 : /**
195 : * Only our static GetDOMWrapper() factory method may create objects of our
196 : * type.
197 : */
198 0 : DOMSVGAnimatedLengthList(nsSVGElement *aElement, PRUint8 aAttrEnum, PRUint8 aAxis)
199 : : mBaseVal(nsnull)
200 : , mAnimVal(nsnull)
201 : , mElement(aElement)
202 : , mAttrEnum(aAttrEnum)
203 0 : , mAxis(aAxis)
204 0 : {}
205 :
206 : ~DOMSVGAnimatedLengthList();
207 :
208 : /// Get a reference to this DOM wrapper object's internal counterpart.
209 : SVGAnimatedLengthList& InternalAList();
210 : const SVGAnimatedLengthList& InternalAList() const;
211 :
212 : // Weak refs to our DOMSVGLengthList baseVal/animVal objects. These objects
213 : // are friends and take care of clearing these pointers when they die, making
214 : // these true weak references.
215 : DOMSVGLengthList *mBaseVal;
216 : DOMSVGLengthList *mAnimVal;
217 :
218 : // Strong ref to our element to keep it alive. We hold this not only for
219 : // ourself, but also for our base/animVal and all of their items.
220 : nsRefPtr<nsSVGElement> mElement;
221 :
222 : PRUint8 mAttrEnum;
223 : PRUint8 mAxis;
224 : };
225 :
226 : } // namespace mozilla
227 :
228 : #endif // MOZILLA_DOMSVGANIMATEDLENGTHLIST_H__
|