Unreviewed, rolling out r219334.
[WebKit-https.git] / Source / WebCore / svg / properties / SVGAnimatedListPropertyTearOff.h
1 /*
2  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
3  * Copyright (C) 2016 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #pragma once
22
23 #include "SVGAnimatedProperty.h"
24 #include "SVGListPropertyTearOff.h"
25 #include "SVGStaticListPropertyTearOff.h"
26
27 namespace WebCore {
28
29 template<typename PropertyType>
30 class SVGPropertyTearOff;
31
32 template<typename PropertyType>
33 class SVGAnimatedListPropertyTearOff : public SVGAnimatedProperty {
34 public:
35     using ListItemType = typename SVGPropertyTraits<PropertyType>::ListItemType;
36     using ListItemTearOff = typename SVGPropertyTraits<PropertyType>::ListItemTearOff;
37     using ListWrapperCache = Vector<RefPtr<ListItemTearOff>>;
38     using ListProperty = SVGListProperty<PropertyType>;
39     using ListPropertyTearOff = typename SVGPropertyTraits<PropertyType>::ListPropertyTearOff;
40     using ContentType = PropertyType;
41
42     static Ref<SVGAnimatedListPropertyTearOff<PropertyType>> create(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values)
43     {
44         ASSERT(contextElement);
45         return adoptRef(*new SVGAnimatedListPropertyTearOff<PropertyType>(contextElement, attributeName, animatedPropertyType, values));
46     }
47
48     virtual Ref<ListPropertyTearOff> baseVal()
49     {
50         if (m_baseVal)
51             return *m_baseVal;
52
53         auto property = ListPropertyTearOff::create(*this, BaseValRole, m_values, m_wrappers);
54         m_baseVal = property.ptr();
55         return property;
56     }
57
58     virtual Ref<ListPropertyTearOff> animVal()
59     {
60         if (m_animVal)
61             return *m_animVal;
62
63         auto property = ListPropertyTearOff::create(*this, AnimValRole, m_values, m_wrappers);
64         m_animVal = property.ptr();
65         return property;
66     }
67     
68     bool isAnimating() const override { return m_animatedProperty; }
69     bool isAnimatedListTearOff() const override { return true; }
70     void propertyWillBeDeleted(const SVGProperty& property) override
71     {
72         if (&property == m_baseVal)
73             m_baseVal = nullptr;
74         else if (&property == m_animVal)
75             m_animVal = nullptr;
76     }
77
78     int findItem(SVGProperty* property)
79     {
80         // This should ever be called for our baseVal, as animVal can't modify the list.
81         return baseVal()->findItem(static_cast<ListItemTearOff*>(property));
82     }
83
84     void removeItemFromList(size_t itemIndex, bool shouldSynchronizeWrappers)
85     {
86         // This should ever be called for our baseVal, as animVal can't modify the list.
87         baseVal()->removeItemFromList(itemIndex, shouldSynchronizeWrappers);
88     }
89
90     void detachListWrappers(unsigned newListSize)
91     {
92         ListProperty::detachListWrappersAndResize(&m_wrappers, newListSize);
93     }
94
95     PropertyType& currentAnimatedValue()
96     {
97         ASSERT(isAnimating());
98         return m_animatedProperty->values();
99     }
100
101     const PropertyType& currentBaseValue() const
102     {
103         return m_values;
104     }
105
106     void animationStarted(PropertyType* newAnimVal, bool shouldOwnValues = false)
107     {
108         ASSERT(!isAnimating());
109         ASSERT(newAnimVal);
110         ASSERT(m_values.size() == m_wrappers.size());
111         ASSERT(m_animatedWrappers.isEmpty());
112
113         // Switch to new passed in value type & new wrappers list. The new wrappers list must be created for the new value.
114         if (!newAnimVal->isEmpty())
115             m_animatedWrappers.fill(0, newAnimVal->size());
116
117         m_animatedProperty = animVal();
118         m_animatedProperty->setValuesAndWrappers(newAnimVal, &m_animatedWrappers, shouldOwnValues);
119         ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
120         ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
121     }
122
123     void animationEnded()
124     {
125         ASSERT(isAnimating());
126         ASSERT(m_values.size() == m_wrappers.size());
127
128         ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
129         ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
130
131         m_animatedProperty->setValuesAndWrappers(&m_values, &m_wrappers, false);
132         ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
133         ASSERT(m_animatedProperty->wrappers().size() == m_wrappers.size());
134
135         m_animatedWrappers.clear();
136         m_animatedProperty = nullptr;
137     }
138
139     void synchronizeWrappersIfNeeded()
140     {
141         ASSERT(isAnimating());
142
143         // Eventually the wrapper list needs synchronization because any SVGAnimateLengthList::calculateAnimatedValue() call may
144         // mutate the length of our values() list, and thus the wrapper() cache needs synchronization, to have the same size.
145         // Also existing wrappers which point directly at elements in the existing SVGLengthListValues have to be detached (so a copy
146         // of them is created, so existing animVal variables in JS are kept-alive). If we'd detach them later the underlying
147         // SVGLengthListValues was already mutated, and our list item wrapper tear offs would point nowhere. Assertions would fire.
148         m_animatedProperty->detachListWrappers(m_animatedProperty->values().size());
149
150         ASSERT(m_animatedProperty->values().size() == m_animatedProperty->wrappers().size());
151         ASSERT(m_animatedProperty->wrappers().size() == m_animatedWrappers.size());
152     }
153
154     void animValWillChange()
155     {
156         ASSERT(m_values.size() == m_wrappers.size());
157         synchronizeWrappersIfNeeded();
158     }
159
160     void animValDidChange()
161     {
162         ASSERT(m_values.size() == m_wrappers.size());
163         synchronizeWrappersIfNeeded();
164     }
165
166 protected:
167     SVGAnimatedListPropertyTearOff(SVGElement* contextElement, const QualifiedName& attributeName, AnimatedPropertyType animatedPropertyType, PropertyType& values)
168         : SVGAnimatedProperty(contextElement, attributeName, animatedPropertyType)
169         , m_values(values)
170     {
171         if (!values.isEmpty())
172             m_wrappers.fill(0, values.size());
173     }
174
175     PropertyType& m_values;
176
177     ListWrapperCache m_wrappers;
178     ListWrapperCache m_animatedWrappers;
179
180     // Cache the raw pointer but return a RefPtr<>. This will break the cyclic reference
181     // between SVGListPropertyTearOff and SVGAnimatedListPropertyTearOff once the property
182     // pointer is not needed.
183     ListPropertyTearOff* m_baseVal { nullptr };
184     ListPropertyTearOff* m_animVal { nullptr };
185
186     RefPtr<ListProperty> m_animatedProperty;
187 };
188
189 } // namespace WebCore