blob: bebcc5a720201613cbc549a67afc0557e65774b3 [file] [log] [blame]
/*
* 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 SVGAnimatedListPropertyTearOff_h
#define SVGAnimatedListPropertyTearOff_h
#include "core/svg/properties/SVGAnimatedProperty.h"
#include "core/svg/properties/SVGListPropertyTearOff.h"
#include "core/svg/properties/SVGStaticListPropertyTearOff.h"
namespace WebCore {
template<typename PropertyType>
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;
typedef SVGListProperty<PropertyType> ListProperty;
typedef SVGListPropertyTearOff<PropertyType> ListPropertyTearOff;
typedef PropertyType ContentType;
virtual ~SVGAnimatedListPropertyTearOff()
{
if (m_baseVal)
static_cast<ListPropertyTearOff*>(m_baseVal.get())->clearAnimatedProperty();
if (m_animVal)
static_cast<ListPropertyTearOff*>(m_animVal.get())->clearAnimatedProperty();
}
virtual ListProperty* baseVal()
{
if (!m_baseVal)
m_baseVal = ListPropertyTearOff::create(this, BaseValRole, m_values, m_wrappers);
return static_cast<ListProperty*>(m_baseVal.get());
}
virtual ListProperty* animVal()
{
if (!m_animVal)
m_animVal = ListPropertyTearOff::create(this, AnimValRole, m_values, m_wrappers);
return static_cast<ListProperty*>(m_animVal.get());
}
virtual bool isAnimatedListTearOff() const { return true; }
int findItem(SVGProperty* property) const
{
// This should ever be called for our baseVal, as animVal can't modify the list.
// It's safe to cast to ListPropertyTearOff here as all classes inheriting from us supply their own removeItemFromList() method.
typedef SVGPropertyTearOff<typename SVGPropertyTraits<PropertyType>::ListItemType> ListItemTearOff;
return static_cast<ListPropertyTearOff*>(m_baseVal.get())->findItem(static_cast<ListItemTearOff*>(property));
}
void removeItemFromList(size_t itemIndex, bool shouldSynchronizeWrappers)
{
// This should ever be called for our baseVal, as animVal can't modify the list.
// It's safe to cast to ListPropertyTearOff here as all classes inheriting from us supply their own removeItemFromList() method.
static_cast<ListPropertyTearOff*>(m_baseVal.get())->removeItemFromList(itemIndex, shouldSynchronizeWrappers);
}
void detachListWrappers(unsigned newListSize)
{
ListProperty::detachListWrappersAndResize(&m_wrappers, newListSize);
}
PropertyType& currentAnimatedValue()
{
ASSERT(m_isAnimating);
ASSERT(m_animVal);
return static_cast<ListProperty*>(m_animVal.get())->values();
}
const PropertyType& currentBaseValue() const
{
return m_values;
}
void animationStarted(PropertyType* newAnimVal, bool shouldOwnValues = false)
{
ASSERT(!m_isAnimating);
ASSERT(newAnimVal);
ASSERT(m_values.size() == m_wrappers.size());
ASSERT(m_animatedWrappers.isEmpty());
// Switch to new passed in value type & new wrappers list. The new wrappers list must be created for the new value.
if (!newAnimVal->isEmpty())
m_animatedWrappers.fill(0, newAnimVal->size());
ListProperty* animVal = static_cast<ListProperty*>(this->animVal());
animVal->setValuesAndWrappers(newAnimVal, &m_animatedWrappers, shouldOwnValues);
ASSERT(animVal->values().size() == animVal->wrappers().size());
ASSERT(animVal->wrappers().size() == m_animatedWrappers.size());
m_isAnimating = true;
}
void animationEnded()
{
ASSERT(m_isAnimating);
ASSERT(m_animVal);
ASSERT(contextElement());
ASSERT(m_values.size() == m_wrappers.size());
ListProperty* animVal = static_cast<ListProperty*>(m_animVal.get());
ASSERT(animVal->values().size() == animVal->wrappers().size());
ASSERT(animVal->wrappers().size() == m_animatedWrappers.size());
animVal->setValuesAndWrappers(&m_values, &m_wrappers, false);
ASSERT(animVal->values().size() == animVal->wrappers().size());
ASSERT(animVal->wrappers().size() == m_wrappers.size());
m_animatedWrappers.clear();
m_isAnimating = false;
}
void synchronizeWrappersIfNeeded()
{
// Eventually the wrapper list needs synchronization because any SVGAnimateLengthList::calculateAnimatedValue() call may
// mutate the length of our values() list, and thus the wrapper() cache needs synchronization, to have the same size.
// Also existing wrappers which point directly at elements in the existing SVGLengthList have to be detached (so a copy
// of them is created, so existing animVal variables in JS are kept-alive). If we'd detach them later the underlying
// SVGLengthList was already mutated, and our list item wrapper tear offs would point nowhere. Assertions would fire.
ListProperty* animVal = static_cast<ListProperty*>(m_animVal.get());
animVal->detachListWrappers(animVal->values().size());
ASSERT(animVal->values().size() == animVal->wrappers().size());
ASSERT(animVal->wrappers().size() == m_animatedWrappers.size());
}
void animValWillChange()
{
ASSERT(m_isAnimating);
ASSERT(m_animVal);
ASSERT(m_values.size() == m_wrappers.size());
synchronizeWrappersIfNeeded();
}
void animValDidChange()
{
ASSERT(m_isAnimating);
ASSERT(m_animVal);
ASSERT(m_values.size() == m_wrappers.size());
synchronizeWrappersIfNeeded();
}
static PassRefPtr<SVGAnimatedListPropertyTearOff<PropertyType> > create(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values)
{
ASSERT(contextElement);
return adoptRef(new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, animatedPropertyType, values));
}
protected:
SVGAnimatedListPropertyTearOff(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values)
: SVGAnimatedProperty(contextElement, attributeName, animatedPropertyType)
, m_values(values)
{
if (!values.isEmpty())
m_wrappers.fill(0, values.size());
}
PropertyType& m_values;
ListWrapperCache m_wrappers;
ListWrapperCache m_animatedWrappers;
RefPtr<SVGProperty> m_baseVal;
RefPtr<SVGProperty> m_animVal;
};
}
#endif // SVGAnimatedListPropertyTearOff_h