Home | History | Annotate | Download | only in properties
      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