• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public License
15  * along with this library; see the file COPYING.LIB.  If not, write to
16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifndef SVGListProperty_h
21 #define SVGListProperty_h
22 
23 #if ENABLE(SVG)
24 #include "SVGAnimatedProperty.h"
25 #include "SVGException.h"
26 #include "SVGPropertyTearOff.h"
27 #include "SVGPropertyTraits.h"
28 
29 namespace WebCore {
30 
31 template<typename PropertyType>
32 class SVGAnimatedListPropertyTearOff;
33 
34 template<typename PropertyType>
35 class SVGListProperty : public SVGProperty {
36 public:
37     typedef SVGListProperty<PropertyType> Self;
38 
39     typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType;
40     typedef SVGPropertyTearOff<ListItemType> ListItemTearOff;
41     typedef PassRefPtr<ListItemTearOff> PassListItemTearOff;
42     typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff;
43     typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache;
44 
canAlterList(ExceptionCode & ec)45     bool canAlterList(ExceptionCode& ec) const
46     {
47         if (m_role == AnimValRole) {
48             ec = NO_MODIFICATION_ALLOWED_ERR;
49             return false;
50         }
51 
52         return true;
53     }
54 
55     // SVGList::clear()
clearValues(PropertyType & values,ExceptionCode & ec)56     void clearValues(PropertyType& values, ExceptionCode& ec)
57     {
58         if (!canAlterList(ec))
59             return;
60 
61         values.clear();
62         commitChange();
63     }
64 
clearValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,ExceptionCode & ec)65     void clearValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, ExceptionCode& ec)
66     {
67         ASSERT(animatedList);
68         if (!canAlterList(ec))
69             return;
70 
71         animatedList->detachListWrappers(0);
72         animatedList->values().clear();
73         commitChange();
74     }
75 
76     // SVGList::numberOfItems()
numberOfItemsValues(PropertyType & values)77     unsigned numberOfItemsValues(PropertyType& values) const
78     {
79         return values.size();
80     }
81 
numberOfItemsValuesAndWrappers(AnimatedListPropertyTearOff * animatedList)82     unsigned numberOfItemsValuesAndWrappers(AnimatedListPropertyTearOff* animatedList) const
83     {
84         ASSERT(animatedList);
85         return animatedList->values().size();
86     }
87 
88     // SVGList::initialize()
initializeValues(PropertyType & values,const ListItemType & newItem,ExceptionCode & ec)89     ListItemType initializeValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec)
90     {
91         if (!canAlterList(ec))
92             return ListItemType();
93 
94         // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
95         processIncomingListItemValue(newItem, 0);
96 
97         // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
98         values.clear();
99         values.append(newItem);
100 
101         commitChange();
102         return newItem;
103     }
104 
initializeValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,PassListItemTearOff passNewItem,ExceptionCode & ec)105     PassListItemTearOff initializeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec)
106     {
107         ASSERT(animatedList);
108         if (!canAlterList(ec))
109             return 0;
110 
111         // Not specified, but FF/Opera do it this way, and it's just sane.
112         if (!passNewItem) {
113             ec = SVGException::SVG_WRONG_TYPE_ERR;
114             return 0;
115         }
116 
117         PropertyType& values = animatedList->values();
118         ListWrapperCache& wrappers = animatedList->wrappers();
119 
120         RefPtr<ListItemTearOff> newItem = passNewItem;
121         ASSERT(values.size() == wrappers.size());
122 
123         // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list.
124         processIncomingListItemWrapper(newItem, 0);
125 
126         // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter.
127         animatedList->detachListWrappers(0);
128         values.clear();
129 
130         values.append(newItem->propertyReference());
131         wrappers.append(newItem);
132 
133         commitChange();
134         return newItem.release();
135     }
136 
137     // SVGList::getItem()
canGetItem(PropertyType & values,unsigned index,ExceptionCode & ec)138     bool canGetItem(PropertyType& values, unsigned index, ExceptionCode& ec)
139     {
140         if (index >= values.size()) {
141             ec = INDEX_SIZE_ERR;
142             return false;
143         }
144 
145         return true;
146     }
147 
getItemValues(PropertyType & values,unsigned index,ExceptionCode & ec)148     ListItemType getItemValues(PropertyType& values, unsigned index, ExceptionCode& ec)
149     {
150         if (!canGetItem(values, index, ec))
151             return ListItemType();
152 
153         // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy.
154         return values.at(index);
155     }
156 
getItemValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,unsigned index,ExceptionCode & ec)157     PassListItemTearOff getItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
158     {
159         ASSERT(animatedList);
160         PropertyType& values = animatedList->values();
161         if (!canGetItem(values, index, ec))
162             return 0;
163 
164         ListWrapperCache& wrappers = animatedList->wrappers();
165 
166         // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy.
167         // Any changes made to the item are immediately reflected in the list.
168         ASSERT(values.size() == wrappers.size());
169         RefPtr<ListItemTearOff> wrapper = wrappers.at(index);
170         if (!wrapper) {
171             // Create new wrapper, which is allowed to directly modify the item in the list, w/o copying and cache the wrapper in our map.
172             // It is also associated with our animated property, so it can notify the SVG Element which holds the SVGAnimated*List
173             // that it has been modified (and thus can call svgAttributeChanged(associatedAttributeName)).
174             wrapper = ListItemTearOff::create(animatedList, UndefinedRole, values.at(index));
175             wrappers.at(index) = wrapper;
176         }
177 
178         return wrapper.release();
179     }
180 
181     // SVGList::insertItemBefore()
insertItemBeforeValues(PropertyType & values,const ListItemType & newItem,unsigned index,ExceptionCode & ec)182     ListItemType insertItemBeforeValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec)
183     {
184         if (!canAlterList(ec))
185             return ListItemType();
186 
187         // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
188         if (index > values.size())
189             index = values.size();
190 
191         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
192         processIncomingListItemValue(newItem, &index);
193 
194         // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be
195         // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list.
196         values.insert(index, newItem);
197 
198         commitChange();
199         return newItem;
200     }
201 
insertItemBeforeValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,PassListItemTearOff passNewItem,unsigned index,ExceptionCode & ec)202     PassListItemTearOff insertItemBeforeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
203     {
204         ASSERT(animatedList);
205         if (!canAlterList(ec))
206             return 0;
207 
208         // Not specified, but FF/Opera do it this way, and it's just sane.
209         if (!passNewItem) {
210             ec = SVGException::SVG_WRONG_TYPE_ERR;
211             return 0;
212         }
213 
214         PropertyType& values = animatedList->values();
215         ListWrapperCache& wrappers = animatedList->wrappers();
216 
217         // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list.
218         if (index > values.size())
219              index = values.size();
220 
221         RefPtr<ListItemTearOff> newItem = passNewItem;
222         ASSERT(values.size() == wrappers.size());
223 
224         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
225         processIncomingListItemWrapper(newItem, &index);
226 
227         // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be
228         // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list.
229         values.insert(index, newItem->propertyReference());
230 
231         // Store new wrapper at position 'index', change its underlying value, so mutations of newItem, directly affect the item in the list.
232         wrappers.insert(index, newItem);
233 
234         commitChange();
235         return newItem.release();
236     }
237 
238     // SVGList::replaceItem()
canReplaceItem(PropertyType & values,unsigned index,ExceptionCode & ec)239     bool canReplaceItem(PropertyType& values, unsigned index, ExceptionCode& ec)
240     {
241         if (!canAlterList(ec))
242             return false;
243 
244         if (index >= values.size()) {
245             ec = INDEX_SIZE_ERR;
246             return false;
247         }
248 
249         return true;
250     }
251 
replaceItemValues(PropertyType & values,const ListItemType & newItem,unsigned index,ExceptionCode & ec)252     ListItemType replaceItemValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec)
253     {
254         if (!canReplaceItem(values, index, ec))
255             return ListItemType();
256 
257         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
258         // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item.
259         processIncomingListItemValue(newItem, &index);
260 
261         if (values.isEmpty()) {
262             // 'newItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
263             ec = INDEX_SIZE_ERR;
264             return ListItemType();
265         }
266 
267         // Update the value at the desired position 'index'.
268         values.at(index) = newItem;
269 
270         commitChange();
271         return newItem;
272     }
273 
replaceItemValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,PassListItemTearOff passNewItem,unsigned index,ExceptionCode & ec)274     PassListItemTearOff replaceItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec)
275     {
276         ASSERT(animatedList);
277         PropertyType& values = animatedList->values();
278         if (!canReplaceItem(values, index, ec))
279             return 0;
280 
281         // Not specified, but FF/Opera do it this way, and it's just sane.
282         if (!passNewItem) {
283             ec = SVGException::SVG_WRONG_TYPE_ERR;
284             return 0;
285         }
286 
287         ListWrapperCache& wrappers = animatedList->wrappers();
288         ASSERT(values.size() == wrappers.size());
289         RefPtr<ListItemTearOff> newItem = passNewItem;
290 
291         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
292         // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item.
293         processIncomingListItemWrapper(newItem, &index);
294 
295         if (values.isEmpty()) {
296             ASSERT(wrappers.isEmpty());
297             // 'passNewItem' already lived in our list, we removed it, and now we're empty, which means there's nothing to replace.
298             ec = INDEX_SIZE_ERR;
299             return 0;
300         }
301 
302         // Detach the existing wrapper.
303         RefPtr<ListItemTearOff> oldItem = wrappers.at(index);
304         if (oldItem)
305             oldItem->detachWrapper();
306 
307         // Update the value and the wrapper at the desired position 'index'.
308         values.at(index) = newItem->propertyReference();
309         wrappers.at(index) = newItem;
310 
311         commitChange();
312         return newItem.release();
313     }
314 
315     // SVGList::removeItem()
canRemoveItem(PropertyType & values,unsigned index,ExceptionCode & ec)316     bool canRemoveItem(PropertyType& values, unsigned index, ExceptionCode& ec)
317     {
318         if (!canAlterList(ec))
319             return false;
320 
321         if (index >= values.size()) {
322             ec = INDEX_SIZE_ERR;
323             return false;
324         }
325 
326         return true;
327     }
328 
removeItemValues(PropertyType & values,unsigned index,ExceptionCode & ec)329     ListItemType removeItemValues(PropertyType& values, unsigned index, ExceptionCode& ec)
330     {
331         if (!canRemoveItem(values, index, ec))
332             return ListItemType();
333 
334         ListItemType oldItem = values.at(index);
335         values.remove(index);
336 
337         commitChange();
338         return oldItem;
339     }
340 
removeItemValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,unsigned index,ExceptionCode & ec)341     PassListItemTearOff removeItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec)
342     {
343         ASSERT(animatedList);
344         PropertyType& values = animatedList->values();
345         if (!canRemoveItem(values, index, ec))
346             return 0;
347 
348         ListWrapperCache& wrappers = animatedList->wrappers();
349         ASSERT(values.size() == wrappers.size());
350 
351         // Detach the existing wrapper.
352         RefPtr<ListItemTearOff> oldItem = wrappers.at(index);
353         if (!oldItem)
354             oldItem = ListItemTearOff::create(animatedList, UndefinedRole, values.at(index));
355 
356         oldItem->detachWrapper();
357         wrappers.remove(index);
358         values.remove(index);
359 
360         commitChange();
361         return oldItem.release();
362     }
363 
364     // SVGList::appendItem()
appendItemValues(PropertyType & values,const ListItemType & newItem,ExceptionCode & ec)365     ListItemType appendItemValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec)
366     {
367         if (!canAlterList(ec))
368             return ListItemType();
369 
370         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
371         processIncomingListItemValue(newItem, 0);
372 
373         // Append the value at the end of the list.
374         values.append(newItem);
375 
376         commitChange();
377         return newItem;
378     }
379 
appendItemValuesAndWrappers(AnimatedListPropertyTearOff * animatedList,PassListItemTearOff passNewItem,ExceptionCode & ec)380     PassListItemTearOff appendItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec)
381     {
382         ASSERT(animatedList);
383         if (!canAlterList(ec))
384             return 0;
385 
386         // Not specified, but FF/Opera do it this way, and it's just sane.
387         if (!passNewItem) {
388             ec = SVGException::SVG_WRONG_TYPE_ERR;
389             return 0;
390         }
391 
392         PropertyType& values = animatedList->values();
393         ListWrapperCache& wrappers = animatedList->wrappers();
394 
395         RefPtr<ListItemTearOff> newItem = passNewItem;
396         ASSERT(values.size() == wrappers.size());
397 
398         // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list.
399         processIncomingListItemWrapper(newItem, 0);
400 
401         // Append the value and wrapper at the end of the list.
402         values.append(newItem->propertyReference());
403         wrappers.append(newItem);
404 
405         commitChange();
406         return newItem.release();
407     }
408 
role()409     virtual SVGPropertyRole role() const { return m_role; }
410 
411 protected:
SVGListProperty(SVGPropertyRole role)412     SVGListProperty(SVGPropertyRole role)
413         : m_role(role)
414     {
415     }
416 
417     virtual void commitChange() = 0;
418     virtual void processIncomingListItemValue(const ListItemType& newItem, unsigned* indexToModify) = 0;
419     virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) = 0;
420 
421 private:
422     SVGPropertyRole m_role;
423 };
424 
425 }
426 
427 #endif // ENABLE(SVG)
428 #endif // SVGListProperty_h
429