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