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