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 SVGListPropertyTearOff_h 21 #define SVGListPropertyTearOff_h 22 23 #include "core/svg/properties/SVGListProperty.h" 24 25 namespace WebCore { 26 27 class ExceptionState; 28 29 template<typename PropertyType> 30 class SVGListPropertyTearOff : public SVGListProperty<PropertyType> { 31 public: 32 typedef SVGListProperty<PropertyType> Base; 33 typedef SVGListPropertyTearOff<PropertyType> Self; 34 35 typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; 36 typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; 37 typedef PassRefPtr<ListItemTearOff> PassListItemTearOff; 38 typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff; 39 typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache; 40 41 using Base::m_role; 42 using Base::m_values; 43 using Base::m_wrappers; 44 45 static PassRefPtr<Self> create(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role, PropertyType& values, ListWrapperCache& wrappers) 46 { 47 ASSERT(animatedProperty); 48 return adoptRef(new Self(animatedProperty, role, values, wrappers)); 49 } 50 51 int findItem(ListItemTearOff* item) const 52 { 53 ASSERT(m_values); 54 ASSERT(m_wrappers); 55 56 unsigned size = m_wrappers->size(); 57 ASSERT(size == m_values->size()); 58 for (size_t i = 0; i < size; ++i) { 59 if (item == m_wrappers->at(i)) 60 return i; 61 } 62 63 return -1; 64 } 65 66 void removeItemFromList(size_t itemIndex, bool shouldSynchronizeWrappers) 67 { 68 ASSERT(m_values); 69 ASSERT(m_wrappers); 70 ASSERT(m_values->size() == m_wrappers->size()); 71 ASSERT_WITH_SECURITY_IMPLICATION(itemIndex < m_wrappers->size()); 72 73 RefPtr<ListItemTearOff>& item = m_wrappers->at(itemIndex); 74 item->detachWrapper(); 75 m_wrappers->remove(itemIndex); 76 m_values->remove(itemIndex); 77 78 if (shouldSynchronizeWrappers) 79 commitChange(); 80 } 81 82 // SVGList API 83 void clear(ExceptionState& exceptionState) 84 { 85 Base::clearValuesAndWrappers(exceptionState); 86 } 87 88 PassListItemTearOff initialize(PassListItemTearOff passNewItem, ExceptionState& exceptionState) 89 { 90 return Base::initializeValuesAndWrappers(passNewItem, exceptionState); 91 } 92 93 PassListItemTearOff getItem(unsigned index, ExceptionState& exceptionState) 94 { 95 ASSERT(m_animatedProperty); 96 return Base::getItemValuesAndWrappers(m_animatedProperty, index, exceptionState); 97 } 98 99 PassListItemTearOff insertItemBefore(PassListItemTearOff passNewItem, unsigned index, ExceptionState& exceptionState) 100 { 101 return Base::insertItemBeforeValuesAndWrappers(passNewItem, index, exceptionState); 102 } 103 104 PassListItemTearOff replaceItem(PassListItemTearOff passNewItem, unsigned index, ExceptionState& exceptionState) 105 { 106 return Base::replaceItemValuesAndWrappers(passNewItem, index, exceptionState); 107 } 108 109 PassListItemTearOff removeItem(unsigned index, ExceptionState& exceptionState) 110 { 111 ASSERT(m_animatedProperty); 112 return Base::removeItemValuesAndWrappers(m_animatedProperty, index, exceptionState); 113 } 114 115 PassListItemTearOff appendItem(PassListItemTearOff passNewItem, ExceptionState& exceptionState) 116 { 117 return Base::appendItemValuesAndWrappers(passNewItem, exceptionState); 118 } 119 120 SVGElement* contextElement() const 121 { 122 ASSERT(m_animatedProperty); 123 return m_animatedProperty->contextElement(); 124 } 125 126 void clearAnimatedProperty() 127 { 128 ASSERT(m_animatedProperty); 129 m_animatedProperty = 0; 130 } 131 132 protected: 133 SVGListPropertyTearOff(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role, PropertyType& values, ListWrapperCache& wrappers) 134 : SVGListProperty<PropertyType>(role, values, &wrappers) 135 , m_animatedProperty(animatedProperty) 136 { 137 ASSERT(m_animatedProperty); 138 } 139 140 virtual bool isReadOnly() const 141 { 142 if (m_role == AnimValRole) 143 return true; 144 if (m_animatedProperty && m_animatedProperty->isReadOnly()) 145 return true; 146 return false; 147 } 148 149 virtual void commitChange() 150 { 151 ASSERT(m_values); 152 ASSERT(m_wrappers); 153 ASSERT(m_animatedProperty); 154 155 // Update existing wrappers, as the index in the values list has changed. 156 unsigned size = m_wrappers->size(); 157 ASSERT(size == m_values->size()); 158 for (unsigned i = 0; i < size; ++i) { 159 ListItemTearOff* item = m_wrappers->at(i).get(); 160 if (!item) 161 continue; 162 item->setAnimatedProperty(m_animatedProperty); 163 item->setValue(m_values->at(i)); 164 } 165 166 m_animatedProperty->commitChange(); 167 } 168 169 virtual bool processIncomingListItemValue(const ListItemType&, unsigned*) 170 { 171 ASSERT_NOT_REACHED(); 172 return true; 173 } 174 175 virtual bool processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) 176 { 177 SVGAnimatedProperty* animatedPropertyOfItem = newItem->animatedProperty(); 178 179 // newItem has been created manually, it doesn't belong to any SVGElement. 180 // (for example: "textElement.x.baseVal.appendItem(svgsvgElement.createSVGLength())") 181 if (!animatedPropertyOfItem) 182 return true; 183 184 // newItem belongs to a SVGElement, but its associated SVGAnimatedProperty is not an animated list tear off. 185 // (for example: "textElement.x.baseVal.appendItem(rectElement.width.baseVal)") 186 if (!animatedPropertyOfItem->isAnimatedListTearOff()) { 187 // We have to copy the incoming newItem, as we're not allowed to insert this tear off as is into our wrapper cache. 188 // Otherwhise we'll end up having two SVGAnimatedPropertys that operate on the same SVGPropertyTearOff. Consider the example above: 189 // SVGRectElements SVGAnimatedLength 'width' property baseVal points to the same tear off object 190 // that's inserted into SVGTextElements SVGAnimatedLengthList 'x'. textElement.x.baseVal.getItem(0).value += 150 would 191 // mutate the rectElement width _and_ the textElement x list. That's obviously wrong, take care of that. 192 newItem = ListItemTearOff::create(newItem->propertyReference()); 193 return true; 194 } 195 196 // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. 197 // 'newItem' is already living in another list. If it's not our list, synchronize the other lists wrappers after the removal. 198 ASSERT(m_animatedProperty); 199 bool livesInOtherList = animatedPropertyOfItem != m_animatedProperty; 200 AnimatedListPropertyTearOff* propertyTearOff = static_cast<AnimatedListPropertyTearOff*>(animatedPropertyOfItem); 201 int indexToRemove = propertyTearOff->findItem(newItem.get()); 202 ASSERT(indexToRemove != -1); 203 204 // Do not remove newItem if already in this list at the target index. 205 if (!livesInOtherList && indexToModify && static_cast<unsigned>(indexToRemove) == *indexToModify) 206 return false; 207 208 propertyTearOff->removeItemFromList(indexToRemove, livesInOtherList); 209 210 if (!indexToModify) 211 return true; 212 213 // If the item lived in our list, adjust the insertion index. 214 if (!livesInOtherList) { 215 unsigned& index = *indexToModify; 216 // Spec: If the item is already in this list, note that the index of the item to (replace|insert before) is before the removal of the item. 217 if (static_cast<unsigned>(indexToRemove) < index) 218 --index; 219 } 220 221 return true; 222 } 223 224 // Back pointer to the animated property that created us 225 // For example (text.x.baseVal): m_animatedProperty points to the 'x' SVGAnimatedLengthList object 226 AnimatedListPropertyTearOff* m_animatedProperty; 227 }; 228 229 } 230 231 #endif // SVGListPropertyTearOff_h 232