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
30namespace WebCore {
31
32    class JSDOMGlobalObject;
33
34    class JSEventListener : public EventListener {
35    public:
36        static PassRefPtr<JSEventListener> create(JSC::JSObject* listener, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld& world)
37        {
38            return adoptRef(new JSEventListener(listener, wrapper, isAttribute, world));
39        }
40
41        static const JSEventListener* cast(const EventListener* listener)
42        {
43            return listener->type() == JSEventListenerType
44                ? static_cast<const JSEventListener*>(listener)
45                : 0;
46        }
47
48        virtual ~JSEventListener();
49
50        virtual bool operator==(const EventListener& other) override;
51
52        // Returns true if this event listener was created for an event handler attribute, like "onload" or "onclick".
53        bool isAttribute() const { return m_isAttribute; }
54
55        JSC::JSObject* jsFunction(ScriptExecutionContext*) const;
56        DOMWrapperWorld& isolatedWorld() const { return *m_isolatedWorld; }
57
58        JSC::JSObject* wrapper() const { return m_wrapper.get(); }
59        void setWrapper(JSC::VM&, JSC::JSObject* wrapper) const { m_wrapper = JSC::Weak<JSC::JSObject>(wrapper); }
60
61    private:
62        virtual JSC::JSObject* initializeJSFunction(ScriptExecutionContext*) const;
63        virtual void visitJSFunction(JSC::SlotVisitor&) override;
64        virtual bool virtualisAttribute() const override;
65
66    protected:
67        JSEventListener(JSC::JSObject* function, JSC::JSObject* wrapper, bool isAttribute, DOMWrapperWorld&);
68        virtual void handleEvent(ScriptExecutionContext*, Event*) override;
69
70    private:
71        mutable JSC::Weak<JSC::JSObject> m_jsFunction;
72        mutable JSC::Weak<JSC::JSObject> m_wrapper;
73
74        bool m_isAttribute;
75        RefPtr<DOMWrapperWorld> m_isolatedWorld;
76    };
77
78    inline JSC::JSObject* JSEventListener::jsFunction(ScriptExecutionContext* scriptExecutionContext) const
79    {
80        // initializeJSFunction can trigger code that deletes this event listener
81        // before we're done. It should always return 0 in this case.
82        Ref<JSEventListener> protect(const_cast<JSEventListener&>(*this));
83        JSC::Strong<JSC::JSObject> wrapper(m_isolatedWorld->vm(), m_wrapper.get());
84
85        if (!m_jsFunction) {
86            JSC::JSObject* function = initializeJSFunction(scriptExecutionContext);
87            JSC::JSObject* wrapper = m_wrapper.get();
88            if (wrapper)
89                JSC::Heap::heap(wrapper)->writeBarrier(wrapper, function);
90            m_jsFunction = JSC::Weak<JSC::JSObject>(function);
91        }
92
93        // Verify that we have a valid wrapper protecting our function from
94        // garbage collection. That is except for when we're not in the normal
95        // world and can have zombie m_jsFunctions.
96        ASSERT(!m_isolatedWorld->isNormal() || m_wrapper || !m_jsFunction);
97
98        // If m_wrapper is 0, then m_jsFunction is zombied, and should never be accessed.
99        if (!m_wrapper)
100            return 0;
101
102        // Try to verify that m_jsFunction wasn't recycled. (Not exact, since an
103        // event listener can be almost anything, but this makes test-writing easier).
104        ASSERT(!m_jsFunction || static_cast<JSC::JSCell*>(m_jsFunction.get())->isObject());
105
106        return m_jsFunction.get();
107    }
108
109    // Creates a JS EventListener for an "onXXX" event attribute.
110    inline PassRefPtr<JSEventListener> createJSAttributeEventListener(JSC::ExecState* exec, JSC::JSValue listener, JSC::JSObject* wrapper)
111    {
112        if (!listener.isObject())
113            return 0;
114
115        return JSEventListener::create(asObject(listener), wrapper, true, currentWorld(exec));
116    }
117
118
119} // namespace WebCore
120
121#endif // JSEventListener_h
122