blob: 60c4ba06e12278cd367bd3a8e635a010f04ae53b [file] [log] [blame]
/*
* Copyright (C) Research In Motion Limited 2010. All rights reserved.
* Copyright (C) 2016 Apple Inc. 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.
*/
#pragma once
#include "SVGAnimatedProperty.h"
#include "SVGListPropertyTearOff.h"
#include "SVGStaticListPropertyTearOff.h"
namespace WebCore {
template<typename PropertyType>
class SVGPropertyTearOff;
template<typename PropertyType>
class SVGAnimatedListPropertyTearOff : public SVGAnimatedProperty {
public:
using ListItemType = typename SVGPropertyTraits<PropertyType>::ListItemType;
using ListItemTearOff = typename SVGPropertyTraits<PropertyType>::ListItemTearOff;
using ListWrapperCache = Vector<WeakPtr<SVGPropertyTearOff<ListItemType>>>;
using ListProperty = SVGListProperty<PropertyType>;
using ListPropertyTearOff = typename SVGPropertyTraits<PropertyType>::ListPropertyTearOff;
using ContentType = PropertyType;
static Ref<SVGAnimatedListPropertyTearOff<PropertyType>> create(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values)
{
ASSERT(contextElement);
return adoptRef(*new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, animatedPropertyType, values));
}
virtual Ref<ListPropertyTearOff> baseVal()
{
if (m_baseVal)
return *static_cast<ListPropertyTearOff*>(m_baseVal.get());
auto property = ListPropertyTearOff::create(*this, BaseValRole, m_values, m_wrappers);
m_baseVal = property->createWeakPtr();
return property;
}
virtual Ref<ListPropertyTearOff> animVal()
{
if (m_animVal)
return *static_cast<ListPropertyTearOff*>(m_animVal.get());
auto property = ListPropertyTearOff::create(*this, AnimValRole, m_values, m_wrappers);
m_animVal = property->createWeakPtr();
return property;
}
bool isAnimating() const override { return m_animatedProperty; }
bool isAnimatedListTearOff() const override { return true; }
int findItem(SVGProperty* property)
{
// This should ever be called for our baseVal, as animVal can't modify the list.
return baseVal()->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.
baseVal()->removeItemFromList(itemIndex, shouldSynchronizeWrappers);
}
void detachListWrappers(unsigned newListSize)
{
ListProperty::detachListWrappersAndResize(&m_wrappers, newListSize);
}
PropertyType& currentAnimatedValue()
{
ASSERT(isAnimating());
return m_animatedProperty->values();
}
const PropertyType& currentBaseValue() const
{
return m_values;
}
void animationStarted(PropertyType* newAnimVal, bool shouldOwnValues = false)
{
ASSERT(!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());
m_animatedProperty = animVal();
m_animatedProperty->setValuesAndWrappers(newAnimVal, &m_animatedWrappers, shouldOwnValues);
ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
}
void animationEnded()
{
ASSERT(isAnimating());
ASSERT(m_values.size() == m_wrappers.size());
ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
m_animatedProperty->setValuesAndWrappers(&m_values, &m_wrappers, false);
ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
ASSERT(m_animatedProperty->wrappers().size() == m_wrappers.size());
m_animatedWrappers.clear();
m_animatedProperty = nullptr;
}
void synchronizeWrappersIfNeeded()
{
if (!isAnimating()) {
// This should never happen, but we've seen it in the field. Please comment in bug #181316 if you hit this.
ASSERT_NOT_REACHED();
return;
}
// 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 SVGLengthListValues 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
// SVGLengthListValues was already mutated, and our list item wrapper tear offs would point nowhere. Assertions would fire.
m_animatedProperty->detachListWrappers(m_animatedProperty->values().size());
ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
}
void animValWillChange()
{
ASSERT(m_values.size() == m_wrappers.size());
synchronizeWrappersIfNeeded();
}
void animValDidChange()
{
ASSERT(m_values.size() == m_wrappers.size());
synchronizeWrappersIfNeeded();
}
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;
// Cache weak pointers but return Ref pointers. This will break the cyclic reference
// between SVGListPropertyTearOff and SVGAnimatedListPropertyTearOff once the property
// pointer is not needed.
WeakPtr<SVGListProperty<PropertyType>> m_baseVal;
WeakPtr<SVGListProperty<PropertyType>> m_animVal;
RefPtr<ListProperty> m_animatedProperty;
};
} // namespace WebCore