diff options
Diffstat (limited to 'WebCore/svg/properties')
-rw-r--r-- | WebCore/svg/properties/SVGAnimatedListPropertyTearOff.h | 52 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGAnimatedPropertyMacros.h | 36 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h | 36 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGListProperty.h | 412 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGListPropertyTearOff.h | 309 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGPropertyTearOff.h | 11 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGPropertyTraits.h | 22 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGStaticListPropertyTearOff.h | 114 | ||||
-rw-r--r-- | WebCore/svg/properties/SVGStaticPropertyTearOff.h | 67 |
9 files changed, 752 insertions, 307 deletions
diff --git a/WebCore/svg/properties/SVGAnimatedListPropertyTearOff.h b/WebCore/svg/properties/SVGAnimatedListPropertyTearOff.h index c002d09..0777b6a 100644 --- a/WebCore/svg/properties/SVGAnimatedListPropertyTearOff.h +++ b/WebCore/svg/properties/SVGAnimatedListPropertyTearOff.h @@ -23,6 +23,7 @@ #if ENABLE(SVG) #include "SVGAnimatedProperty.h" #include "SVGListPropertyTearOff.h" +#include "SVGStaticListPropertyTearOff.h" namespace WebCore { @@ -32,17 +33,21 @@ class SVGPropertyTearOff; template<typename PropertyType> class SVGAnimatedListPropertyTearOff : public SVGAnimatedProperty { public: + typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; + typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; + typedef Vector<RefPtr<ListItemTearOff> > ListWrapperCache; + SVGProperty* baseVal() { if (!m_baseVal) - m_baseVal = SVGListPropertyTearOff<PropertyType>::create(this, BaseValRole, m_property); + m_baseVal = SVGListPropertyTearOff<PropertyType>::create(this, BaseValRole); return m_baseVal.get(); } SVGProperty* animVal() { if (!m_animVal) - m_animVal = SVGListPropertyTearOff<PropertyType>::create(this, AnimValRole, m_property); + m_animVal = SVGListPropertyTearOff<PropertyType>::create(this, AnimValRole); return m_animVal.get(); } @@ -50,39 +55,56 @@ public: int removeItemFromList(SVGProperty* property, bool shouldSynchronizeWrappers) { - // FIXME: No animVal support. - if (!m_baseVal) - return -1; - + // This should ever be called for our baseVal, as animVal can't modify the list. typedef SVGPropertyTearOff<typename SVGPropertyTraits<PropertyType>::ListItemType> ListItemTearOff; return static_pointer_cast<SVGListPropertyTearOff<PropertyType> >(m_baseVal)->removeItemFromList(static_cast<ListItemTearOff*>(property), shouldSynchronizeWrappers); } void detachListWrappers(unsigned newListSize) { - if (m_baseVal) - static_pointer_cast<SVGListPropertyTearOff<PropertyType> >(m_baseVal)->detachListWrappers(newListSize); - if (m_animVal) - static_pointer_cast<SVGListPropertyTearOff<PropertyType> >(m_animVal)->detachListWrappers(newListSize); + // See SVGPropertyTearOff::detachWrapper() for an explaination what's happening here. + unsigned size = m_wrappers.size(); + ASSERT(size == m_values.size()); + for (unsigned i = 0; i < size; ++i) { + RefPtr<ListItemTearOff>& item = m_wrappers.at(i); + if (!item) + continue; + item->detachWrapper(); + } + + // Reinitialize the wrapper cache to be equal to the new values size, after the XML DOM changed the list. + if (newListSize) + m_wrappers.fill(0, newListSize); + else + m_wrappers.clear(); } + PropertyType& values() { return m_values; } + ListWrapperCache& wrappers() { return m_wrappers; } + private: friend class SVGAnimatedProperty; - static PassRefPtr<SVGAnimatedListPropertyTearOff<PropertyType> > create(SVGElement* contextElement, const QualifiedName& attributeName, PropertyType& property) + static PassRefPtr<SVGAnimatedListPropertyTearOff<PropertyType> > create(SVGElement* contextElement, const QualifiedName& attributeName, PropertyType& values) { ASSERT(contextElement); - return adoptRef(new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, property)); + return adoptRef(new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, values)); } - SVGAnimatedListPropertyTearOff(SVGElement* contextElement, const QualifiedName& attributeName, PropertyType& property) + SVGAnimatedListPropertyTearOff(SVGElement* contextElement, const QualifiedName& attributeName, PropertyType& values) : SVGAnimatedProperty(contextElement, attributeName) - , m_property(property) + , m_values(values) { + if (!values.isEmpty()) + m_wrappers.fill(0, values.size()); } private: - PropertyType& m_property; + PropertyType& m_values; + + // FIXME: The list wrapper cache is shared between baseVal/animVal. If we implement animVal, + // we need two seperated wrapper caches if the attribute gets animated. + ListWrapperCache m_wrappers; RefPtr<SVGProperty> m_baseVal; RefPtr<SVGProperty> m_animVal; diff --git a/WebCore/svg/properties/SVGAnimatedPropertyMacros.h b/WebCore/svg/properties/SVGAnimatedPropertyMacros.h index 12d0565..e1c75b9 100644 --- a/WebCore/svg/properties/SVGAnimatedPropertyMacros.h +++ b/WebCore/svg/properties/SVGAnimatedPropertyMacros.h @@ -32,6 +32,42 @@ namespace WebCore { +class SVGElement; + +// GetOwnerElementForType implementation +template<typename OwnerType, bool isDerivedFromSVGElement> +struct GetOwnerElementForType; + +template<typename OwnerType> +struct GetOwnerElementForType<OwnerType, true> { + static SVGElement* ownerElement(OwnerType* type) + { + return type; + } +}; + +template<typename OwnerType> +struct GetOwnerElementForType<OwnerType, false> { + static SVGElement* ownerElement(OwnerType* type) + { + SVGElement* context = type->contextElement(); + ASSERT(context); + return context; + } +}; + +// IsDerivedFromSVGElement implementation +template<typename OwnerType> +struct IsDerivedFromSVGElement { + static const bool value = true; +}; + +class SVGViewSpec; +template<> +struct IsDerivedFromSVGElement<SVGViewSpec> { + static const bool value = false; +}; + template<typename PropertyType> struct SVGSynchronizableAnimatedProperty { SVGSynchronizableAnimatedProperty() diff --git a/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h b/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h index 2b816ab..b97073d 100644 --- a/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h +++ b/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h @@ -23,42 +23,6 @@ #if ENABLE(SVG) namespace WebCore { -class SVGElement; - -// GetOwnerElementForType implementation -template<typename OwnerType, bool isDerivedFromSVGElement> -struct GetOwnerElementForType; - -template<typename OwnerType> -struct GetOwnerElementForType<OwnerType, true> : public Noncopyable { - static SVGElement* ownerElement(OwnerType* type) - { - return type; - } -}; - -template<typename OwnerType> -struct GetOwnerElementForType<OwnerType, false> : public Noncopyable { - static SVGElement* ownerElement(OwnerType* type) - { - SVGElement* context = type->contextElement(); - ASSERT(context); - return context; - } -}; - -// IsDerivedFromSVGElement implementation -template<typename OwnerType> -struct IsDerivedFromSVGElement : public Noncopyable { - static const bool value = true; -}; - -class SVGViewSpec; -template<> -struct IsDerivedFromSVGElement<SVGViewSpec> : public Noncopyable { - static const bool value = false; -}; - // Helper template used for synchronizing SVG <-> XML properties template<bool isDerivedFromSVGElement> struct SVGAnimatedPropertySynchronizer { diff --git a/WebCore/svg/properties/SVGListProperty.h b/WebCore/svg/properties/SVGListProperty.h new file mode 100644 index 0000000..7edc0f1 --- /dev/null +++ b/WebCore/svg/properties/SVGListProperty.h @@ -0,0 +1,412 @@ +/* + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SVGListProperty_h +#define SVGListProperty_h + +#if ENABLE(SVG) +#include "ExceptionCode.h" +#include "SVGAnimatedProperty.h" +#include "SVGPropertyTearOff.h" +#include "SVGPropertyTraits.h" + +namespace WebCore { + +template<typename PropertyType> +class SVGAnimatedListPropertyTearOff; + +template<typename PropertyType> +class SVGListProperty : public SVGProperty { +public: + typedef SVGListProperty<PropertyType> Self; + + typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; + typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; + typedef PassRefPtr<ListItemTearOff> PassListItemTearOff; + typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff; + typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache; + + bool canAlterList(ExceptionCode& ec) const + { + if (m_role == AnimValRole) { + ec = NO_MODIFICATION_ALLOWED_ERR; + return false; + } + + return true; + } + + // SVGList::clear() + void clearValues(PropertyType& values, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return; + + values.clear(); + commitChange(); + } + + void clearValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, ExceptionCode& ec) + { + ASSERT(animatedList); + if (!canAlterList(ec)) + return; + + animatedList->detachListWrappers(0); + animatedList->values().clear(); + commitChange(); + } + + // SVGList::numberOfItems() + unsigned numberOfItemsValues(PropertyType& values) const + { + return values.size(); + } + + unsigned numberOfItemsValuesAndWrappers(AnimatedListPropertyTearOff* animatedList) const + { + ASSERT(animatedList); + return animatedList->values().size(); + } + + // SVGList::initialize() + ListItemType initializeValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return ListItemType(); + + // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemValue(newItem, 0); + + // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter. + values.clear(); + values.append(newItem); + + commitChange(); + return newItem; + } + + PassListItemTearOff initializeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec) + { + ASSERT(animatedList); + if (!canAlterList(ec)) + return 0; + + // Not specified, but FF/Opera do it this way, and it's just sane. + if (!passNewItem) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + + PropertyType& values = animatedList->values(); + ListWrapperCache& wrappers = animatedList->wrappers(); + + RefPtr<ListItemTearOff> newItem = passNewItem; + ASSERT(values.size() == wrappers.size()); + + // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemWrapper(newItem, 0); + + // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter. + animatedList->detachListWrappers(0); + values.clear(); + + values.append(newItem->propertyReference()); + wrappers.append(newItem); + + commitChange(); + return newItem.release(); + } + + // SVGList::getItem() + bool canGetItem(PropertyType& values, unsigned index, ExceptionCode& ec) + { + if (index >= values.size()) { + ec = INDEX_SIZE_ERR; + return false; + } + + return true; + } + + ListItemType getItemValues(PropertyType& values, unsigned index, ExceptionCode& ec) + { + if (!canGetItem(values, index, ec)) + return ListItemType(); + + // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy. + return values.at(index); + } + + PassListItemTearOff getItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec) + { + ASSERT(animatedList); + PropertyType& values = animatedList->values(); + if (!canGetItem(values, index, ec)) + return 0; + + ListWrapperCache& wrappers = animatedList->wrappers(); + + // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy. + // Any changes made to the item are immediately reflected in the list. + ASSERT(values.size() == wrappers.size()); + RefPtr<ListItemTearOff> wrapper = wrappers.at(index); + if (!wrapper) { + // Create new wrapper, which is allowed to directly modify the item in the list, w/o copying and cache the wrapper in our map. + // It is also associated with our animated property, so it can notify the SVG Element which holds the SVGAnimated*List + // that it has been modified (and thus can call svgAttributeChanged(associatedAttributeName)). + wrapper = ListItemTearOff::create(animatedList, UndefinedRole, values.at(index)); + wrappers.at(index) = wrapper; + } + + return wrapper.release(); + } + + // SVGList::insertItemBefore() + ListItemType insertItemBeforeValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return ListItemType(); + + // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. + if (index > values.size()) + index = values.size(); + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemValue(newItem, &index); + + // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be + // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list. + values.insert(index, newItem); + + commitChange(); + return newItem; + } + + PassListItemTearOff insertItemBeforeValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec) + { + ASSERT(animatedList); + if (!canAlterList(ec)) + return 0; + + // Not specified, but FF/Opera do it this way, and it's just sane. + if (!passNewItem) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + + PropertyType& values = animatedList->values(); + ListWrapperCache& wrappers = animatedList->wrappers(); + + // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. + if (index > values.size()) + index = values.size(); + + RefPtr<ListItemTearOff> newItem = passNewItem; + ASSERT(values.size() == wrappers.size()); + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemWrapper(newItem, &index); + + // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be + // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list. + values.insert(index, newItem->propertyReference()); + + // Store new wrapper at position 'index', change its underlying value, so mutations of newItem, directly affect the item in the list. + wrappers.insert(index, newItem); + + commitChange(); + return newItem.release(); + } + + // SVGList::replaceItem() + bool canReplaceItem(PropertyType& values, unsigned index, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return false; + + if (index >= values.size()) { + ec = INDEX_SIZE_ERR; + return false; + } + + return true; + } + + ListItemType replaceItemValues(PropertyType& values, const ListItemType& newItem, unsigned index, ExceptionCode& ec) + { + if (!canReplaceItem(values, index, ec)) + return ListItemType(); + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item. + processIncomingListItemValue(newItem, &index); + + // Update the value at the desired position 'index'. + values.at(index) = newItem; + + commitChange(); + return newItem; + } + + PassListItemTearOff replaceItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec) + { + ASSERT(animatedList); + PropertyType& values = animatedList->values(); + if (!canReplaceItem(values, index, ec)) + return 0; + + // Not specified, but FF/Opera do it this way, and it's just sane. + if (!passNewItem) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + + ListWrapperCache& wrappers = animatedList->wrappers(); + ASSERT(values.size() == wrappers.size()); + RefPtr<ListItemTearOff> newItem = passNewItem; + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item. + processIncomingListItemWrapper(newItem, &index); + + // Detach the existing wrapper. + RefPtr<ListItemTearOff>& oldItem = wrappers.at(index); + if (oldItem) + oldItem->detachWrapper(); + + // Update the value and the wrapper at the desired position 'index'. + values.at(index) = newItem->propertyReference(); + wrappers.at(index) = newItem; + + commitChange(); + return newItem.release(); + } + + // SVGList::removeItem() + bool canRemoveItem(PropertyType& values, unsigned index, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return false; + + if (index >= values.size()) { + ec = INDEX_SIZE_ERR; + return false; + } + + return true; + } + + ListItemType removeItemValues(PropertyType& values, unsigned index, ExceptionCode& ec) + { + if (!canRemoveItem(values, index, ec)) + return ListItemType(); + + ListItemType oldItem = values.at(index); + values.remove(index); + + commitChange(); + return oldItem; + } + + PassListItemTearOff removeItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, unsigned index, ExceptionCode& ec) + { + ASSERT(animatedList); + PropertyType& values = animatedList->values(); + if (!canRemoveItem(values, index, ec)) + return 0; + + ListWrapperCache& wrappers = animatedList->wrappers(); + ASSERT(values.size() == wrappers.size()); + + // Detach the existing wrapper. + RefPtr<ListItemTearOff>& oldItem = wrappers.at(index); + if (oldItem) + oldItem->detachWrapper(); + + wrappers.remove(index); + values.remove(index); + + commitChange(); + return oldItem.release(); + } + + // SVGList::appendItem() + ListItemType appendItemValues(PropertyType& values, const ListItemType& newItem, ExceptionCode& ec) + { + if (!canAlterList(ec)) + return ListItemType(); + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemValue(newItem, 0); + + // Append the value at the end of the list. + values.append(newItem); + + commitChange(); + return newItem; + } + + PassListItemTearOff appendItemValuesAndWrappers(AnimatedListPropertyTearOff* animatedList, PassListItemTearOff passNewItem, ExceptionCode& ec) + { + ASSERT(animatedList); + if (!canAlterList(ec)) + return 0; + + // Not specified, but FF/Opera do it this way, and it's just sane. + if (!passNewItem) { + ec = TYPE_MISMATCH_ERR; + return 0; + } + + PropertyType& values = animatedList->values(); + ListWrapperCache& wrappers = animatedList->wrappers(); + + RefPtr<ListItemTearOff> newItem = passNewItem; + ASSERT(values.size() == wrappers.size()); + + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. + processIncomingListItemWrapper(newItem, 0); + + // Append the value and wrapper at the end of the list. + values.append(newItem->propertyReference()); + wrappers.append(newItem); + + commitChange(); + return newItem.release(); + } + +protected: + SVGListProperty(SVGPropertyRole role) + : m_role(role) + { + } + + virtual void commitChange() = 0; + virtual void processIncomingListItemValue(const ListItemType& newItem, unsigned* indexToModify) = 0; + virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) = 0; + +private: + SVGPropertyRole m_role; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGListProperty_h diff --git a/WebCore/svg/properties/SVGListPropertyTearOff.h b/WebCore/svg/properties/SVGListPropertyTearOff.h index 2801168..56d626f 100644 --- a/WebCore/svg/properties/SVGListPropertyTearOff.h +++ b/WebCore/svg/properties/SVGListPropertyTearOff.h @@ -21,52 +21,43 @@ #define SVGListPropertyTearOff_h #if ENABLE(SVG) -#include "ExceptionCode.h" -#include "SVGAnimatedProperty.h" -#include "SVGPropertyTearOff.h" -#include "SVGPropertyTraits.h" +#include "SVGListProperty.h" namespace WebCore { template<typename PropertyType> -class SVGAnimatedListPropertyTearOff; - -template<typename PropertyType> -class SVGListPropertyTearOff : public SVGProperty { +class SVGListPropertyTearOff : public SVGListProperty<PropertyType> { public: - typedef SVGListPropertyTearOff<PropertyType> Self; + typedef SVGListProperty<PropertyType> Base; typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; typedef PassRefPtr<ListItemTearOff> PassListItemTearOff; - typedef Vector<RefPtr<ListItemTearOff> > ListWrapperCache; + typedef SVGAnimatedListPropertyTearOff<PropertyType> AnimatedListPropertyTearOff; + typedef typename SVGAnimatedListPropertyTearOff<PropertyType>::ListWrapperCache ListWrapperCache; - // Used for [SVGAnimatedProperty] types (for example: SVGAnimatedLengthList::baseVal()) - static PassRefPtr<Self> create(SVGAnimatedProperty* animatedProperty, SVGPropertyRole role, PropertyType& values) + static PassRefPtr<SVGListPropertyTearOff<PropertyType> > create(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role) { ASSERT(animatedProperty); - return adoptRef(new Self(animatedProperty, role, values)); - } - - // Used for non-animated POD types (for example: SVGStringList). - static PassRefPtr<Self> create(const PropertyType& initialValue) - { - return adoptRef(new Self(initialValue)); + return adoptRef(new SVGListPropertyTearOff<PropertyType>(animatedProperty, role)); } int removeItemFromList(ListItemTearOff* removeItem, bool shouldSynchronizeWrappers) { + PropertyType& values = m_animatedProperty->values(); + ListWrapperCache& wrappers = m_animatedProperty->wrappers(); + // Lookup item in cache and remove its corresponding wrapper. - unsigned size = m_wrappers.size(); - ASSERT(size == m_values->size()); + unsigned size = wrappers.size(); + ASSERT(size == values.size()); for (unsigned i = 0; i < size; ++i) { - RefPtr<ListItemTearOff>& item = m_wrappers.at(i); + RefPtr<ListItemTearOff>& item = wrappers.at(i); if (item != removeItem) continue; item->detachWrapper(); - m_wrappers.remove(i); - m_values->remove(i); + wrappers.remove(i); + values.remove(i); if (shouldSynchronizeWrappers) commitChange(); @@ -77,275 +68,103 @@ public: return -1; } - void detachListWrappers(unsigned newListSize) - { - // See SVGPropertyTearOff::detachWrapper() for an explaination what's happening here. - unsigned size = m_wrappers.size(); - ASSERT(size == m_values->size()); - for (unsigned i = 0; i < size; ++i) { - RefPtr<ListItemTearOff>& item = m_wrappers.at(i); - if (!item) - continue; - item->detachWrapper(); - } - - // Reinitialize the wrapper cache to be equal to the new values size, after the XML DOM changed the list. - if (newListSize) - m_wrappers.fill(0, newListSize); - else - m_wrappers.clear(); - } - // SVGList API void clear(ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return; - } - - detachListWrappers(0); - m_values->clear(); + Base::clearValuesAndWrappers(m_animatedProperty.get(), ec); } unsigned numberOfItems() const { - return m_values->size(); + return Base::numberOfItemsValuesAndWrappers(m_animatedProperty.get()); } PassListItemTearOff initialize(PassListItemTearOff passNewItem, ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return 0; - } - - // Not specified, but FF/Opera do it this way, and it's just sane. - if (!passNewItem) { - ec = TYPE_MISMATCH_ERR; - return 0; - } - - RefPtr<ListItemTearOff> newItem = passNewItem; - ASSERT(m_values->size() == m_wrappers.size()); - - // Spec: If the inserted item is already in a list, it is removed from its previous list before it is inserted into this list. - removeItemFromListIfNeeded(newItem.get(), 0); - - // Spec: Clears all existing current items from the list and re-initializes the list to hold the single item specified by the parameter. - detachListWrappers(0); - m_values->clear(); - - m_values->append(newItem->propertyReference()); - m_wrappers.append(newItem); - - commitChange(); - return newItem.release(); + return Base::initializeValuesAndWrappers(m_animatedProperty.get(), passNewItem, ec); } PassListItemTearOff getItem(unsigned index, ExceptionCode& ec) { - if (index >= m_values->size()) { - ec = INDEX_SIZE_ERR; - return 0; - } - - // Spec: Returns the specified item from the list. The returned item is the item itself and not a copy. - // Any changes made to the item are immediately reflected in the list. - ASSERT(m_values->size() == m_wrappers.size()); - RefPtr<ListItemTearOff> wrapper = m_wrappers.at(index); - if (!wrapper) { - // Create new wrapper, which is allowed to directly modify the item in the list, w/o copying and cache the wrapper in our map. - // It is also associated with our animated property, so it can notify the SVG Element which holds the SVGAnimated*List - // that it has been modified (and thus can call svgAttributeChanged(associatedAttributeName)). - wrapper = ListItemTearOff::create(m_animatedProperty.get(), UndefinedRole, m_values->at(index)); - m_wrappers.at(index) = wrapper; - } - - return wrapper.release(); + return Base::getItemValuesAndWrappers(m_animatedProperty.get(), index, ec); } PassListItemTearOff insertItemBefore(PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return 0; - } - - // Not specified, but FF/Opera do it this way, and it's just sane. - if (!passNewItem) { - ec = TYPE_MISMATCH_ERR; - return 0; - } - - // Spec: If the index is greater than or equal to numberOfItems, then the new item is appended to the end of the list. - if (index > m_values->size()) - index = m_values->size(); - - RefPtr<ListItemTearOff> newItem = passNewItem; - ASSERT(m_values->size() == m_wrappers.size()); - - // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. - removeItemFromListIfNeeded(newItem.get(), &index); - - // Spec: Inserts a new item into the list at the specified position. The index of the item before which the new item is to be - // inserted. The first item is number 0. If the index is equal to 0, then the new item is inserted at the front of the list. - m_values->insert(index, newItem->propertyReference()); - - // Store new wrapper at position 'index', change its underlying value, so mutations of newItem, directly affect the item in the list. - m_wrappers.insert(index, newItem); - - commitChange(); - return newItem.release(); + return Base::insertItemBeforeValuesAndWrappers(m_animatedProperty.get(), passNewItem, index, ec); } PassListItemTearOff replaceItem(PassListItemTearOff passNewItem, unsigned index, ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return 0; - } - - if (index >= m_values->size()) { - ec = INDEX_SIZE_ERR; - return 0; - } - - // Not specified, but FF/Opera do it this way, and it's just sane. - if (!passNewItem) { - ec = TYPE_MISMATCH_ERR; - return 0; - } - - RefPtr<ListItemTearOff> newItem = passNewItem; - ASSERT(m_values->size() == m_wrappers.size()); - - // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. - // Spec: If the item is already in this list, note that the index of the item to replace is before the removal of the item. - removeItemFromListIfNeeded(newItem.get(), &index); - - // Detach the existing wrapper. - RefPtr<ListItemTearOff>& oldItem = m_wrappers.at(index); - if (oldItem) - oldItem->detachWrapper(); - - // Update the value and the wrapper at the desired position 'index'. - m_values->at(index) = newItem->propertyReference(); - m_wrappers.at(index) = newItem; - - commitChange(); - return newItem.release(); + return Base::replaceItemValuesAndWrappers(m_animatedProperty.get(), passNewItem, index, ec); } PassListItemTearOff removeItem(unsigned index, ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return 0; - } - - if (index >= m_values->size()) { - ec = INDEX_SIZE_ERR; - return 0; - } - - ASSERT(m_values->size() == m_wrappers.size()); - - // Detach the existing wrapper. - RefPtr<ListItemTearOff>& oldItem = m_wrappers.at(index); - if (oldItem) { - oldItem->detachWrapper(); - m_wrappers.remove(index); - } - - m_values->remove(index); - - commitChange(); - return oldItem.release(); + return Base::removeItemValuesAndWrappers(m_animatedProperty.get(), index, ec); } PassListItemTearOff appendItem(PassListItemTearOff passNewItem, ExceptionCode& ec) { - if (m_role == AnimValRole) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return 0; - } - - // Not specified, but FF/Opera do it this way, and it's just sane. - if (!passNewItem) { - ec = TYPE_MISMATCH_ERR; - return 0; - } - - RefPtr<ListItemTearOff> newItem = passNewItem; - ASSERT(m_values->size() == m_wrappers.size()); - - // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. - removeItemFromListIfNeeded(newItem.get(), 0); - - // Append the value and wrapper at the end of the list. - m_values->append(newItem->propertyReference()); - m_wrappers.append(newItem); - - commitChange(); - return newItem.release(); + return Base::appendItemValuesAndWrappers(m_animatedProperty.get(), passNewItem, ec); } private: - SVGListPropertyTearOff(SVGAnimatedProperty* animatedProperty, SVGPropertyRole role, PropertyType& values) - : m_animatedProperty(animatedProperty) - , m_role(role) - , m_values(&values) - , m_valuesIsCopy(false) - { - // Using operator & is completly fine, as SVGAnimatedProperty owns this reference, - // and we're guaranteed to live as long as SVGAnimatedProperty does. - if (!values.isEmpty()) - m_wrappers.fill(0, values.size()); - } - - SVGListPropertyTearOff(const PropertyType& initialValue) - : m_animatedProperty(0) - , m_role(UndefinedRole) - , m_values(new PropertyType(initialValue)) - , m_valuesIsCopy(true) + SVGListPropertyTearOff(AnimatedListPropertyTearOff* animatedProperty, SVGPropertyRole role) + : SVGListProperty<PropertyType>(role) + , m_animatedProperty(animatedProperty) { } - virtual ~SVGListPropertyTearOff() + virtual void commitChange() { - if (m_valuesIsCopy) - delete m_values; - } + PropertyType& values = m_animatedProperty->values(); + ListWrapperCache& wrappers = m_animatedProperty->wrappers(); - void commitChange() - { - // Update existing wrappers, as the index in the m_values list has changed. - unsigned size = m_wrappers.size(); - ASSERT(size == m_values->size()); + // Update existing wrappers, as the index in the values list has changed. + unsigned size = wrappers.size(); + ASSERT(size == values.size()); for (unsigned i = 0; i < size; ++i) { - RefPtr<ListItemTearOff>& item = m_wrappers.at(i); + RefPtr<ListItemTearOff>& item = wrappers.at(i); if (!item) continue; item->setAnimatedProperty(m_animatedProperty.get()); - item->setValue(m_values->at(i)); + item->setValue(values.at(i)); } - ASSERT(!m_valuesIsCopy); - ASSERT(m_animatedProperty); m_animatedProperty->commitChange(); } - void removeItemFromListIfNeeded(ListItemTearOff* newItem, unsigned* indexToModify) + virtual void processIncomingListItemValue(const ListItemType&, unsigned*) + { + ASSERT_NOT_REACHED(); + } + + virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>& newItem, unsigned* indexToModify) { - // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. SVGAnimatedProperty* animatedPropertyOfItem = newItem->animatedProperty(); - if (!animatedPropertyOfItem || !animatedPropertyOfItem->isAnimatedListTearOff()) + + // newItem has been created manually, it doesn't belong to any SVGElement. + // (for example: "textElement.x.baseVal.appendItem(svgsvgElement.createSVGLength())") + if (!animatedPropertyOfItem) + return; + + // newItem belongs to a SVGElement, but its associated SVGAnimatedProperty is not an animated list tear off. + // (for example: "textElement.x.baseVal.appendItem(rectElement.width.baseVal)") + if (!animatedPropertyOfItem->isAnimatedListTearOff()) { + // We have to copy the incoming newItem, as we're not allowed to insert this tear off as is into our wrapper cache. + // Otherwhise we'll end up having two SVGAnimatedPropertys that operate on the same SVGPropertyTearOff. Consider the example above: + // SVGRectElements SVGAnimatedLength 'width' property baseVal points to the same tear off object + // that's inserted into SVGTextElements SVGAnimatedLengthList 'x'. textElement.x.baseVal.getItem(0).value += 150 would + // mutate the rectElement width _and_ the textElement x list. That's obviously wrong, take care of that. + newItem = ListItemTearOff::create(newItem->propertyReference()); return; + } + // Spec: If newItem is already in a list, it is removed from its previous list before it is inserted into this list. // 'newItem' is already living in another list. If it's not our list, synchronize the other lists wrappers after the removal. bool livesInOtherList = animatedPropertyOfItem != m_animatedProperty; - int removedIndex = static_cast<SVGAnimatedListPropertyTearOff<PropertyType>*>(animatedPropertyOfItem)->removeItemFromList(newItem, livesInOtherList); + int removedIndex = static_cast<AnimatedListPropertyTearOff*>(animatedPropertyOfItem)->removeItemFromList(newItem.get(), livesInOtherList); ASSERT(removedIndex != -1); if (!indexToModify) @@ -363,17 +182,7 @@ private: private: // Back pointer to the animated property that created us // For example (text.x.baseVal): m_animatedProperty points to the 'x' SVGAnimatedLengthList object - RefPtr<SVGAnimatedProperty> m_animatedProperty; - - // The role of this property (baseVal or animVal) - SVGPropertyRole m_role; - - // For the example above (text.x.baseVal): A reference to the SVGLengthList& stored in the SVGTextElement, which we can directly modify - PropertyType* m_values; - bool m_valuesIsCopy : 1; - - // A list of wrappers, which is always in sync between m_values. - ListWrapperCache m_wrappers; + RefPtr<AnimatedListPropertyTearOff> m_animatedProperty; }; } diff --git a/WebCore/svg/properties/SVGPropertyTearOff.h b/WebCore/svg/properties/SVGPropertyTearOff.h index 17588b4..2ffaede 100644 --- a/WebCore/svg/properties/SVGPropertyTearOff.h +++ b/WebCore/svg/properties/SVGPropertyTearOff.h @@ -32,7 +32,7 @@ class SVGPropertyTearOff : public SVGProperty { public: typedef SVGPropertyTearOff<PropertyType> Self; - // Used for [SVGAnimatedProperty] types (for example: SVGAnimatedLength::baseVal()). + // Used for child types (baseVal/animVal) of a SVGAnimated* property (for example: SVGAnimatedLength::baseVal()). // Also used for list tear offs (for example: text.x.baseVal.getItem(0)). static PassRefPtr<Self> create(SVGAnimatedProperty* animatedProperty, SVGPropertyRole, PropertyType& value) { @@ -40,7 +40,7 @@ public: return adoptRef(new Self(animatedProperty, value)); } - // Used for non-animated POD types (for example: SVGLength). + // Used for non-animated POD types (for example: SVGSVGElement::createSVGLength()). static PassRefPtr<Self> create(const PropertyType& initialValue) { return adoptRef(new Self(initialValue)); @@ -49,8 +49,6 @@ public: PropertyType& propertyReference() { return *m_value; } SVGAnimatedProperty* animatedProperty() const { return m_animatedProperty.get(); } - virtual int removeItemFromList(SVGAnimatedProperty*) { return -1; } - // Used only by the list tear offs! void setValue(PropertyType& value) { @@ -81,16 +79,17 @@ public: ASSERT(!m_valueIsCopy); m_value = new PropertyType(*m_value); m_valueIsCopy = true; + m_animatedProperty = 0; } - void commitChange() + virtual void commitChange() { if (!m_animatedProperty || m_valueIsCopy) return; m_animatedProperty->commitChange(); } -private: +protected: SVGPropertyTearOff(SVGAnimatedProperty* animatedProperty, PropertyType& value) : m_animatedProperty(animatedProperty) , m_value(&value) diff --git a/WebCore/svg/properties/SVGPropertyTraits.h b/WebCore/svg/properties/SVGPropertyTraits.h index 8d82a61..85bca7e 100644 --- a/WebCore/svg/properties/SVGPropertyTraits.h +++ b/WebCore/svg/properties/SVGPropertyTraits.h @@ -26,7 +26,10 @@ #include "SVGAngle.h" #include "SVGLength.h" #include "SVGLengthList.h" +#include "SVGNumberList.h" +#include "SVGPointList.h" #include "SVGPreserveAspectRatio.h" +#include "SVGStringList.h" #include <wtf/text/StringBuilder.h> namespace WebCore { @@ -79,6 +82,14 @@ struct SVGPropertyTraits<float> { }; template<> +struct SVGPropertyTraits<SVGNumberList> { + typedef float ListItemType; + + static SVGNumberList initialValue() { return SVGNumberList(); } + static String toString(const SVGNumberList& type) { return type.valueAsString(); } +}; + +template<> struct SVGPropertyTraits<SVGPreserveAspectRatio> { static SVGPreserveAspectRatio initialValue() { return SVGPreserveAspectRatio(); } static String toString(const SVGPreserveAspectRatio& type) { return type.valueAsString(); } @@ -108,6 +119,17 @@ struct SVGPropertyTraits<String> { static String toString(const String& type) { return type; } }; +template<> +struct SVGPropertyTraits<SVGStringList> { + typedef String ListItemType; +}; + +template<> +struct SVGPropertyTraits<SVGPointList> { + static SVGPointList initialValue() { return SVGPointList(); } + typedef FloatPoint ListItemType; +}; + } #endif diff --git a/WebCore/svg/properties/SVGStaticListPropertyTearOff.h b/WebCore/svg/properties/SVGStaticListPropertyTearOff.h new file mode 100644 index 0000000..a6f0f28 --- /dev/null +++ b/WebCore/svg/properties/SVGStaticListPropertyTearOff.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SVGStaticListPropertyTearOff_h +#define SVGStaticListPropertyTearOff_h + +#if ENABLE(SVG) +#include "SVGListProperty.h" + +namespace WebCore { + +template<typename PropertyType> +class SVGStaticListPropertyTearOff : public SVGListProperty<PropertyType> { +public: + typedef SVGListProperty<PropertyType> Base; + + typedef typename SVGPropertyTraits<PropertyType>::ListItemType ListItemType; + typedef SVGPropertyTearOff<ListItemType> ListItemTearOff; + + static PassRefPtr<SVGStaticListPropertyTearOff<PropertyType> > create(SVGElement* contextElement, PropertyType& values) + { + ASSERT(contextElement); + return adoptRef(new SVGStaticListPropertyTearOff<PropertyType>(contextElement, values)); + } + + // SVGList API + void clear(ExceptionCode& ec) + { + Base::clearValues(m_values, ec); + } + + unsigned numberOfItems() const + { + return Base::numberOfItemsValues(m_values); + } + + ListItemType initialize(const ListItemType& newItem, ExceptionCode& ec) + { + return Base::initializeValues(m_values, newItem, ec); + } + + ListItemType getItem(unsigned index, ExceptionCode& ec) + { + return Base::getItemValues(m_values, index, ec); + } + + ListItemType insertItemBefore(const ListItemType& newItem, unsigned index, ExceptionCode& ec) + { + return Base::insertItemBeforeValues(m_values, newItem, index, ec); + } + + ListItemType replaceItem(const ListItemType& newItem, unsigned index, ExceptionCode& ec) + { + return Base::replaceItemValues(m_values, newItem, index, ec); + } + + ListItemType removeItem(unsigned index, ExceptionCode& ec) + { + return Base::removeItemValues(m_values, index, ec); + } + + ListItemType appendItem(const ListItemType& newItem, ExceptionCode& ec) + { + return Base::appendItemValues(m_values, newItem, ec); + } + +private: + SVGStaticListPropertyTearOff(SVGElement* contextElement, PropertyType& values) + : SVGListProperty<PropertyType>(UndefinedRole) + , m_contextElement(contextElement) + , m_values(values) + { + } + + virtual void commitChange() + { + m_values.commitChange(m_contextElement.get()); + } + + virtual void processIncomingListItemValue(const ListItemType&, unsigned*) + { + // no-op for static lists + } + + virtual void processIncomingListItemWrapper(RefPtr<ListItemTearOff>&, unsigned*) + { + ASSERT_NOT_REACHED(); + } + +private: + RefPtr<SVGElement> m_contextElement; + PropertyType& m_values; +}; + +} + +#endif // ENABLE(SVG) +#endif // SVGStaticListPropertyTearOff_h diff --git a/WebCore/svg/properties/SVGStaticPropertyTearOff.h b/WebCore/svg/properties/SVGStaticPropertyTearOff.h new file mode 100644 index 0000000..8f31909 --- /dev/null +++ b/WebCore/svg/properties/SVGStaticPropertyTearOff.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) Research In Motion Limited 2010. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef SVGStaticPropertyTearOff_h +#define SVGStaticPropertyTearOff_h + +#if ENABLE(SVG) +#include "SVGPropertyTearOff.h" + +namespace WebCore { + +#if COMPILER(MSVC) +// UpdateMethod is 12 bytes. We have to pack to a size greater than or equal to that to avoid an +// alignment warning (C4121). 16 is the next-largest size allowed for packing, so we use that. +#pragma pack(push, 16) +#endif +template<typename ContextElement, typename PropertyType> +class SVGStaticPropertyTearOff : public SVGPropertyTearOff<PropertyType> { +public: + typedef SVGStaticPropertyTearOff<ContextElement, PropertyType> Self; + typedef void (ContextElement::*UpdateMethod)(); + + // Used for non-animated POD types that are not associated with a SVGAnimatedProperty object, nor with a XML DOM attribute + // (for example: SVGSVGElement::currentTranslate). + static PassRefPtr<Self> create(ContextElement* contextElement, PropertyType& value, UpdateMethod update) + { + ASSERT(contextElement); + return adoptRef(new Self(contextElement, value, update)); + } + + virtual void commitChange() { (m_contextElement.get()->*m_update)(); } + +private: + SVGStaticPropertyTearOff(ContextElement* contextElement, PropertyType& value, UpdateMethod update) + : SVGPropertyTearOff<PropertyType>(0, value) + , m_update(update) + , m_contextElement(contextElement) + { + } + + UpdateMethod m_update; + RefPtr<ContextElement> m_contextElement; +}; +#if COMPILER(MSVC) +#pragma pack(pop) +#endif + +} + +#endif // ENABLE(SVG) +#endif // SVGStaticPropertyTearOff_h |