Fix lifetime handling of SVGPropertyTearOffs
[WebKit-https.git] / Source / WebCore / svg / properties / SVGPropertyTearOff.h
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 SVGPropertyTearOff_h
21 #define SVGPropertyTearOff_h
22
23 #include "SVGAnimatedProperty.h"
24 #include "SVGElement.h"
25 #include "SVGProperty.h"
26 #include <wtf/WeakPtr.h>
27
28 namespace WebCore {
29
30 class SVGPropertyTearOffBase : public SVGProperty {
31 public:
32     virtual void detachWrapper() = 0;
33 };
34
35 template<typename PropertyType>
36 class SVGPropertyTearOff : public SVGPropertyTearOffBase {
37 public:
38     typedef SVGPropertyTearOff<PropertyType> Self;
39
40     // Used for child types (baseVal/animVal) of a SVGAnimated* property (for example: SVGAnimatedLength::baseVal()).
41     // Also used for list tear offs (for example: text.x.baseVal.getItem(0)).
42     static PassRefPtr<Self> create(SVGAnimatedProperty* animatedProperty, SVGPropertyRole role, PropertyType& value)
43     {
44         ASSERT(animatedProperty);
45         return adoptRef(new Self(animatedProperty, role, value));
46     }
47
48     // Used for non-animated POD types (for example: SVGSVGElement::createSVGLength()).
49     static PassRefPtr<Self> create(const PropertyType& initialValue)
50     {
51         return adoptRef(new Self(initialValue));
52     }
53
54     PropertyType& propertyReference() { return *m_value; }
55     SVGAnimatedProperty* animatedProperty() const { return m_animatedProperty; }
56
57     void setValue(PropertyType& value)
58     {
59         if (m_valueIsCopy)
60             delete m_value;
61         m_valueIsCopy = false;
62         m_value = &value;
63     }
64
65     void setAnimatedProperty(SVGAnimatedProperty* animatedProperty)
66     {
67         m_animatedProperty = animatedProperty;
68
69         if (m_animatedProperty)
70             m_contextElement = m_animatedProperty->contextElement();
71     }
72
73     SVGElement* contextElement() const
74     {
75         if (!m_animatedProperty || m_valueIsCopy)
76             return 0;
77         return m_contextElement.get();
78     }
79
80     void addChild(WeakPtr<SVGPropertyTearOffBase> child)
81     {
82         m_childTearOffs.append(child);
83     }
84
85     virtual void detachWrapper() override
86     {
87         if (m_valueIsCopy)
88             return;
89
90         detachChildren();
91
92         // Switch from a live value, to a non-live value.
93         // For example: <text x="50"/>
94         // var item = text.x.baseVal.getItem(0);
95         // text.setAttribute("x", "100");
96         // item.value still has to report '50' and it has to be possible to modify 'item'
97         // w/o changing the "new item" (with x=100) in the text element.
98         // Whenever the XML DOM modifies the "x" attribute, all existing wrappers are detached, using this function.
99         m_value = new PropertyType(*m_value);
100         m_valueIsCopy = true;
101         m_animatedProperty = 0;
102     }
103
104     virtual void commitChange() override
105     {
106         if (!m_animatedProperty || m_valueIsCopy)
107             return;
108         m_animatedProperty->commitChange();
109     }
110
111     virtual bool isReadOnly() const override
112     {
113         if (m_role == AnimValRole)
114             return true;
115         if (m_animatedProperty && m_animatedProperty->isReadOnly())
116             return true;
117         return false;
118     }
119
120 protected:
121     SVGPropertyTearOff(SVGAnimatedProperty* animatedProperty, SVGPropertyRole role, PropertyType& value)
122         : m_animatedProperty(animatedProperty)
123         , m_role(role)
124         , m_value(&value)
125         , m_valueIsCopy(false)
126     {
127         // Using operator & is completely fine, as SVGAnimatedProperty owns this reference,
128         // and we're guaranteed to live as long as SVGAnimatedProperty does.
129
130         if (m_animatedProperty)
131             m_contextElement = m_animatedProperty->contextElement();
132     }
133
134     SVGPropertyTearOff(const PropertyType& initialValue)
135         : m_animatedProperty(0)
136         , m_role(UndefinedRole)
137         , m_value(new PropertyType(initialValue))
138         , m_valueIsCopy(true)
139     {
140     }
141
142     virtual ~SVGPropertyTearOff()
143     {
144         if (m_valueIsCopy)
145             delete m_value;
146     }
147
148     void detachChildren()
149     {
150         for (const auto& childTearOff : m_childTearOffs) {
151             if (childTearOff.get())
152                 childTearOff.get()->detachWrapper();
153         }
154         m_childTearOffs.clear();
155     }
156
157     RefPtr<SVGElement> m_contextElement;
158     SVGAnimatedProperty* m_animatedProperty;
159     SVGPropertyRole m_role;
160     PropertyType* m_value;
161     Vector<WeakPtr<SVGPropertyTearOffBase>> m_childTearOffs;
162     bool m_valueIsCopy : 1;
163 };
164
165 }
166
167 #endif // SVGPropertyTearOff_h