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 SVGAnimatedListPropertyTearOff_h 21 #define SVGAnimatedListPropertyTearOff_h 22 23 #include "core/svg/properties/SVGAnimatedProperty.h" 24 #include "core/svg/properties/SVGListPropertyTearOff.h" 25 #include "core/svg/properties/SVGStaticListPropertyTearOff.h" 26 27 namespace WebCore { 28 29 template<typename PropertyType> 30 class SVGPropertyTearOff; 31 32 template<typename PropertyType> 33 class SVGAnimatedListPropertyTearOff : public SVGAnimatedProperty { 34 public: 35 typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; 36 typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; 37 typedef Vector<RefPtr<ListItemTearOff> > ListWrapperCache; 38 typedef SVGListProperty<PropertyType> ListProperty; 39 typedef SVGListPropertyTearOff<PropertyType> ListPropertyTearOff; 40 typedef PropertyType ContentType; 41 42 virtual ListProperty* baseVal() 43 { 44 if (!m_baseVal) 45 m_baseVal = ListPropertyTearOff::create(this, BaseValRole, m_values, m_wrappers); 46 return static_cast<ListProperty*>(m_baseVal.get()); 47 } 48 49 virtual ListProperty* animVal() 50 { 51 if (!m_animVal) 52 m_animVal = ListPropertyTearOff::create(this, AnimValRole, m_values, m_wrappers); 53 return static_cast<ListProperty*>(m_animVal.get()); 54 } 55 56 virtual bool isAnimatedListTearOff() const { return true; } 57 58 int findItem(SVGProperty* property) const 59 { 60 // This should ever be called for our baseVal, as animVal can't modify the list. 61 // It's safe to cast to ListPropertyTearOff here as all classes inheriting from us supply their own removeItemFromList() method. 62 typedef SVGPropertyTearOff<typename SVGPropertyTraits<PropertyType>::ListItemType> ListItemTearOff; 63 return static_cast<ListPropertyTearOff*>(m_baseVal.get())->findItem(static_cast<ListItemTearOff*>(property)); 64 } 65 66 void removeItemFromList(size_t itemIndex, bool shouldSynchronizeWrappers) 67 { 68 // This should ever be called for our baseVal, as animVal can't modify the list. 69 // It's safe to cast to ListPropertyTearOff here as all classes inheriting from us supply their own removeItemFromList() method. 70 static_cast<ListPropertyTearOff*>(m_baseVal.get())->removeItemFromList(itemIndex, shouldSynchronizeWrappers); 71 } 72 73 void detachListWrappers(unsigned newListSize) 74 { 75 ListProperty::detachListWrappersAndResize(&m_wrappers, newListSize); 76 } 77 78 PropertyType& currentAnimatedValue() 79 { 80 ASSERT(m_isAnimating); 81 ASSERT(m_animVal); 82 return static_cast<ListProperty*>(m_animVal.get())->values(); 83 } 84 85 const PropertyType& currentBaseValue() const 86 { 87 return m_values; 88 } 89 90 void animationStarted(PropertyType* newAnimVal, bool shouldOwnValues = false) 91 { 92 ASSERT(!m_isAnimating); 93 ASSERT(newAnimVal); 94 ASSERT(m_values.size() == m_wrappers.size()); 95 ASSERT(m_animatedWrappers.isEmpty()); 96 97 // Switch to new passed in value type & new wrappers list. The new wrappers list must be created for the new value. 98 if (!newAnimVal->isEmpty()) 99 m_animatedWrappers.fill(0, newAnimVal->size()); 100 101 ListProperty* animVal = static_cast<ListProperty*>(this->animVal()); 102 animVal->setValuesAndWrappers(newAnimVal, &m_animatedWrappers, shouldOwnValues); 103 ASSERT(animVal->values().size() == animVal->wrappers().size()); 104 ASSERT(animVal->wrappers().size() == m_animatedWrappers.size()); 105 m_isAnimating = true; 106 } 107 108 void animationEnded() 109 { 110 ASSERT(m_isAnimating); 111 ASSERT(m_animVal); 112 ASSERT(m_values.size() == m_wrappers.size()); 113 114 ListProperty* animVal = static_cast<ListProperty*>(m_animVal.get()); 115 ASSERT(animVal->values().size() == animVal->wrappers().size()); 116 ASSERT(animVal->wrappers().size() == m_animatedWrappers.size()); 117 118 animVal->setValuesAndWrappers(&m_values, &m_wrappers, false); 119 ASSERT(animVal->values().size() == animVal->wrappers().size()); 120 ASSERT(animVal->wrappers().size() == m_wrappers.size()); 121 122 m_animatedWrappers.clear(); 123 m_isAnimating = false; 124 } 125 126 void synchronizeWrappersIfNeeded() 127 { 128 // Eventually the wrapper list needs synchronization because any SVGAnimateLengthList::calculateAnimatedValue() call may 129 // mutate the length of our values() list, and thus the wrapper() cache needs synchronization, to have the same size. 130 // Also existing wrappers which point directly at elements in the existing SVGLengthList have to be detached (so a copy 131 // of them is created, so existing animVal variables in JS are kept-alive). If we'd detach them later the underlying 132 // SVGLengthList was already mutated, and our list item wrapper tear offs would point nowhere. Assertions would fire. 133 ListProperty* animVal = static_cast<ListProperty*>(m_animVal.get()); 134 animVal->detachListWrappers(animVal->values().size()); 135 136 ASSERT(animVal->values().size() == animVal->wrappers().size()); 137 ASSERT(animVal->wrappers().size() == m_animatedWrappers.size()); 138 } 139 140 void animValWillChange() 141 { 142 ASSERT(m_isAnimating); 143 ASSERT(m_animVal); 144 ASSERT(m_values.size() == m_wrappers.size()); 145 synchronizeWrappersIfNeeded(); 146 } 147 148 void animValDidChange() 149 { 150 ASSERT(m_isAnimating); 151 ASSERT(m_animVal); 152 ASSERT(m_values.size() == m_wrappers.size()); 153 synchronizeWrappersIfNeeded(); 154 } 155 156 static PassRefPtr<SVGAnimatedListPropertyTearOff<PropertyType> > create(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values) 157 { 158 ASSERT(contextElement); 159 return adoptRef(new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, animatedPropertyType, values)); 160 } 161 162 protected: 163 SVGAnimatedListPropertyTearOff(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values) 164 : SVGAnimatedProperty(contextElement, attributeName, animatedPropertyType) 165 , m_values(values) 166 { 167 if (!values.isEmpty()) 168 m_wrappers.fill(0, values.size()); 169 } 170 171 PropertyType& m_values; 172 173 ListWrapperCache m_wrappers; 174 ListWrapperCache m_animatedWrappers; 175 176 RefPtr<SVGProperty> m_baseVal; 177 RefPtr<SVGProperty> m_animVal; 178 }; 179 180 } 181 182 #endif // SVGAnimatedListPropertyTearOff_h 183