REGRESSION (r125251): wrapper lifetimes of SVGElementInstance are incorrect
[WebKit-https.git] / Source / WebCore / bindings / js / JSEventListener.h
1 /*
2  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
3  *  Copyright (C) 2003, 2008, 2009 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 Lesser 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  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19
20 #ifndef JSEventListener_h
21 #define JSEventListener_h
22
23 #include "DOMWrapperWorld.h"
24 #include "EventListener.h"
25 #include <heap/StrongInlines.h>
26 #include <heap/Weak.h>
27 #include <heap/WeakInlines.h>
28 #include <wtf/Ref.h>
29
30 namespace WebCore {
31
32     class JSDOMGlobalObject;
33     class JSSVGElementInstance;
34
35     class JSEventListener : public EventListener {
36     public:
37         static Ref<JSEventListener> create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld& world)
38         {
39             return adoptRef(*new JSEventListener(listener, wrapper, isAttribute, world));
40         }
41
42         static const JSEventListener* cast(const EventListener* listener)
43         {
44             return listener->type() == JSEventListenerType
45                 ? static_cast<const JSEventListener*>(listener)
46                 : 0;
47         }
48
49         virtual ~JSEventListener();
50
51         virtual bool operator==(const EventListener& other) override;
52
53         // Returns true if this event listener was created for an event handler attribute, like "onload" or "onclick".
54         bool isAttribute() const { return m_isAttribute; }
55
56         JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
57         DOMWrapperWorld& isolatedWorld() const { return *m_isolatedWorld; }
58
59         JSC::JSObject* wrapper() const { return m_wrapper.get(); }
60         void setWrapper(JSC::VM&, JSC::JSObject* wrapper) const { m_wrapper = JSC::Weak<JSC::JSObject>(wrapper); }
61
62     private:
63         virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext*) const;
64         virtual void visitJSFunction(JSC::SlotVisitor&) override;
65         virtual bool virtualisAttribute() const override;
66
67     protected:
68         JSEventListener(JSC::JSObject* function, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld&);
69         virtual void handleEvent(ScriptExecutionContext*, Event*) override;
70
71     private:
72         mutable JSC::Weak<JSC::JSObject> m_jsFunction;
73         mutable JSC::Weak<JSC::JSObject> m_wrapper;
74
75         bool m_isAttribute;
76         RefPtr<DOMWrapperWorld> m_isolatedWorld;
77     };
78
79     // For "onXXX" event attributes.
80     RefPtr<JSEventListener> createJSEventListenerForAttribute(JSC::ExecState&, JSC::JSValue listener, JSC::JSObject& wrapper);
81     RefPtr<JSEventListener> createJSEventListenerForAttribute(JSC::ExecState&, JSC::JSValue listener, JSSVGElementInstance& wrapper);
82
83     Ref<JSEventListener> createJSEventListenerForAdd(JSC::ExecState&, JSC::JSObject& listener, JSC::JSObject& wrapper);
84     Ref<JSEventListener> createJSEventListenerForRemove(JSC::ExecState&, JSC::JSObject& listener, JSC::JSObject& wrapper);
85
86     bool forwardsEventListeners(JSC::JSObject& wrapper);
87
88     inline JSC::JSObject* JSEventListener::jsFunction(ScriptExecutionContext* scriptExecutionContext) const
89     {
90         // initializeJSFunction can trigger code that deletes this event listener
91         // before we're done. It should always return 0 in this case.
92         Ref<JSEventListener> protect(const_cast<JSEventListener&>(*this));
93         JSC::Strong<JSC::JSObject> wrapper(m_isolatedWorld->vm(), m_wrapper.get());
94
95         if (!m_jsFunction) {
96             JSC::JSObject* function = initializeJSFunction(scriptExecutionContext);
97             JSC::JSObject* wrapper = m_wrapper.get();
98             if (wrapper)
99                 JSC::Heap::heap(wrapper)->writeBarrier(wrapper, function);
100             m_jsFunction = JSC::Weak<JSC::JSObject>(function);
101         }
102
103         // Verify that we have a valid wrapper protecting our function from
104         // garbage collection. That is except for when we're not in the normal
105         // world and can have zombie m_jsFunctions.
106         ASSERT(!m_isolatedWorld->isNormal() || m_wrapper || !m_jsFunction);
107
108         // If m_wrapper is 0, then m_jsFunction is zombied, and should never be accessed.
109         if (!m_wrapper)
110             return 0;
111
112         // Try to verify that m_jsFunction wasn't recycled. (Not exact, since an
113         // event listener can be almost anything, but this makes test-writing easier).
114         ASSERT(!m_jsFunction || static_cast<JSC::JSCell*>(m_jsFunction.get())->isObject());
115
116         return m_jsFunction.get();
117     }
118
119     inline RefPtr<JSEventListener> createJSEventListenerForAttribute(JSC::ExecState& state, JSC::JSValue listener, JSC::JSObject& wrapper)
120     {
121         ASSERT(!forwardsEventListeners(wrapper));
122         if (!listener.isObject())
123             return nullptr;
124         return JSEventListener::create(asObject(listener), &wrapper, true, currentWorld(&state));
125     }
126
127     inline Ref<JSEventListener> createJSEventListenerForRemove(JSC::ExecState& state, JSC::JSObject& listener, JSC::JSObject& wrapper)
128     {
129         return createJSEventListenerForAdd(state, listener, wrapper);
130     }
131
132 } // namespace WebCore
133
134 #endif // JSEventListener_h