1 /* 2 * Copyright (C) 2013 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #ifndef SVGListPropertyTearOffHelper_h 32 #define SVGListPropertyTearOffHelper_h 33 34 #include "bindings/core/v8/ExceptionState.h" 35 #include "core/svg/properties/SVGPropertyTearOff.h" 36 #include "wtf/HashMap.h" 37 #include "wtf/TypeTraits.h" 38 39 namespace blink { 40 41 template<typename ItemProperty> 42 class ListItemPropertyTraits { 43 public: 44 typedef ItemProperty ItemPropertyType; 45 typedef typename ItemPropertyType::TearOffType ItemTearOffType; 46 47 static PassRefPtr<ItemPropertyType> getValueForInsertionFromTearOff(PassRefPtr<ItemTearOffType> passNewItem, SVGElement* contextElement, const QualifiedName& attributeName) 48 { 49 RefPtr<ItemTearOffType> newItem = passNewItem; 50 51 // |newItem| is immutable, OR 52 // |newItem| belongs to a SVGElement, but it does not belong to an animated list 53 // (for example: "textElement.x.baseVal.appendItem(rectElement.width.baseVal)") 54 if (newItem->isImmutable() 55 || (newItem->contextElement() && !newItem->target()->ownerList())) { 56 // We have to copy the incoming |newItem|, 57 // Otherwise we'll end up having two tearoffs that operate on the same SVGProperty. Consider the example below: 58 // SVGRectElements SVGAnimatedLength 'width' property baseVal points to the same tear off object 59 // that's inserted into SVGTextElements SVGAnimatedLengthList 'x'. textElement.x.baseVal.getItem(0).value += 150 would 60 // mutate the rectElement width _and_ the textElement x list. That's obviously wrong, take care of that. 61 return newItem->target()->clone(); 62 } 63 64 newItem->attachToSVGElementAttribute(contextElement, attributeName); 65 return newItem->target(); 66 } 67 68 static PassRefPtr<ItemTearOffType> createTearOff(PassRefPtr<ItemPropertyType> value, SVGElement* contextElement, PropertyIsAnimValType propertyIsAnimVal, const QualifiedName& attributeName) 69 { 70 return ItemTearOffType::create(value, contextElement, propertyIsAnimVal, attributeName); 71 } 72 }; 73 74 template<typename Derived, typename ListProperty> 75 class SVGListPropertyTearOffHelper : public SVGPropertyTearOff<ListProperty> { 76 public: 77 typedef ListProperty ListPropertyType; 78 typedef typename ListPropertyType::ItemPropertyType ItemPropertyType; 79 typedef typename ItemPropertyType::TearOffType ItemTearOffType; 80 typedef ListItemPropertyTraits<ItemPropertyType> ItemTraits; 81 82 // SVG*List DOM interface: 83 84 // WebIDL requires "unsigned long" type instead of size_t. 85 unsigned long length() 86 { 87 return toDerived()->target()->length(); 88 } 89 90 void clear(ExceptionState& exceptionState) 91 { 92 if (toDerived()->isImmutable()) { 93 exceptionState.throwDOMException(NoModificationAllowedError, "The object is read-only."); 94 return; 95 } 96 97 toDerived()->target()->clear(); 98 } 99 100 PassRefPtr<ItemTearOffType> initialize(PassRefPtr<ItemTearOffType> passItem, ExceptionState& exceptionState) 101 { 102 RefPtr<ItemTearOffType> item = passItem; 103 104 if (toDerived()->isImmutable()) { 105 exceptionState.throwDOMException(NoModificationAllowedError, "The object is read-only."); 106 return nullptr; 107 } 108 109 if (!item) { 110 exceptionState.throwTypeError("Lists must be initialized with a valid item."); 111 return nullptr; 112 } 113 114 RefPtr<ItemPropertyType> value = toDerived()->target()->initialize(getValueForInsertionFromTearOff(item)); 115 toDerived()->commitChange(); 116 117 return createItemTearOff(value.release()); 118 } 119 120 PassRefPtr<ItemTearOffType> getItem(unsigned long index, ExceptionState& exceptionState) 121 { 122 RefPtr<ItemPropertyType> value = toDerived()->target()->getItem(index, exceptionState); 123 return createItemTearOff(value.release()); 124 } 125 126 PassRefPtr<ItemTearOffType> insertItemBefore(PassRefPtr<ItemTearOffType> passItem, unsigned long index, ExceptionState& exceptionState) 127 { 128 RefPtr<ItemTearOffType> item = passItem; 129 130 if (toDerived()->isImmutable()) { 131 exceptionState.throwDOMException(NoModificationAllowedError, "The object is read-only."); 132 return nullptr; 133 } 134 135 if (!item) { 136 exceptionState.throwTypeError("An invalid item cannot be inserted to a list."); 137 return nullptr; 138 } 139 140 RefPtr<ItemPropertyType> value = toDerived()->target()->insertItemBefore(getValueForInsertionFromTearOff(item), index); 141 toDerived()->commitChange(); 142 143 return createItemTearOff(value.release()); 144 } 145 146 PassRefPtr<ItemTearOffType> replaceItem(PassRefPtr<ItemTearOffType> passItem, unsigned long index, ExceptionState& exceptionState) 147 { 148 RefPtr<ItemTearOffType> item = passItem; 149 150 if (toDerived()->isImmutable()) { 151 exceptionState.throwDOMException(NoModificationAllowedError, "The object is read-only."); 152 return nullptr; 153 } 154 155 if (!item) { 156 exceptionState.throwTypeError("An invalid item cannot be replaced with an existing list item."); 157 return nullptr; 158 } 159 160 RefPtr<ItemPropertyType> value = toDerived()->target()->replaceItem(getValueForInsertionFromTearOff(item), index, exceptionState); 161 toDerived()->commitChange(); 162 163 return createItemTearOff(value.release()); 164 } 165 166 bool anonymousIndexedSetter(unsigned index, PassRefPtr<ItemTearOffType> passItem, ExceptionState& exceptionState) 167 { 168 replaceItem(passItem, index, exceptionState); 169 return true; 170 } 171 172 PassRefPtr<ItemTearOffType> removeItem(unsigned long index, ExceptionState& exceptionState) 173 { 174 RefPtr<ItemPropertyType> value = toDerived()->target()->removeItem(index, exceptionState); 175 toDerived()->commitChange(); 176 177 return createItemTearOff(value.release()); 178 } 179 180 PassRefPtr<ItemTearOffType> appendItem(PassRefPtr<ItemTearOffType> passItem, ExceptionState& exceptionState) 181 { 182 RefPtr<ItemTearOffType> item = passItem; 183 184 if (toDerived()->isImmutable()) { 185 exceptionState.throwDOMException(NoModificationAllowedError, "The object is read-only."); 186 return nullptr; 187 } 188 189 if (!item) { 190 exceptionState.throwTypeError("An invalid item cannot be appended to a list."); 191 return nullptr; 192 } 193 194 RefPtr<ItemPropertyType> value = toDerived()->target()->appendItem(getValueForInsertionFromTearOff(item)); 195 toDerived()->commitChange(); 196 197 return createItemTearOff(value.release()); 198 } 199 200 protected: 201 SVGListPropertyTearOffHelper(PassRefPtr<ListPropertyType> target, SVGElement* contextElement, PropertyIsAnimValType propertyIsAnimVal, const QualifiedName& attributeName = QualifiedName::null()) 202 : SVGPropertyTearOff<ListPropertyType>(target, contextElement, propertyIsAnimVal, attributeName) 203 { 204 } 205 206 PassRefPtr<ItemPropertyType> getValueForInsertionFromTearOff(PassRefPtr<ItemTearOffType> passNewItem) 207 { 208 return ItemTraits::getValueForInsertionFromTearOff(passNewItem, toDerived()->contextElement(), toDerived()->attributeName()); 209 } 210 211 PassRefPtr<ItemTearOffType> createItemTearOff(PassRefPtr<ItemPropertyType> value) 212 { 213 if (!value) 214 return nullptr; 215 216 if (value->ownerList() == toDerived()->target()) 217 return ItemTraits::createTearOff(value, toDerived()->contextElement(), toDerived()->propertyIsAnimVal(), toDerived()->attributeName()); 218 219 return ItemTraits::createTearOff(value, 0, PropertyIsNotAnimVal, QualifiedName::null()); 220 } 221 222 private: 223 Derived* toDerived() { return static_cast<Derived*>(this); } 224 }; 225 226 } 227 228 #endif // SVGListPropertyTearOffHelper_h 229