aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/bindings/webcore/EventEmitter.h
blob: 23687d43a19b5dd5a14a07f1fdfac318300895dc (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#pragma once

#include "root.h"

#include "IdentifierEventListenerMap.h"
#include "ExceptionOr.h"
#include "ContextDestructionObserver.h"
#include "ScriptWrappable.h"
#include <memory>
#include <variant>
#include <wtf/Forward.h>

#include <wtf/WeakPtr.h>

namespace JSC {
class JSValue;
class JSObject;
}

namespace WebCore {

class DOMWrapperWorld;
class JSEventListener;

struct EventEmitterData {
    WTF_MAKE_NONCOPYABLE(EventEmitterData);
    WTF_MAKE_FAST_ALLOCATED;

public:
    EventEmitterData() = default;
    IdentifierEventListenerMap eventListenerMap;
    bool isFiringEventListeners { false };
};

class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr<EventEmitter>, public RefCounted<EventEmitter>, public ContextDestructionObserver {
    WTF_MAKE_ISO_ALLOCATED(EventEmitter);

public:
    static Ref<EventEmitter> create(ScriptExecutionContext&);
    WEBCORE_EXPORT ~EventEmitter() = default;

    using RefCounted::deref;
    using RefCounted::ref;

    ScriptExecutionContext* scriptExecutionContext() const { return ContextDestructionObserver::scriptExecutionContext(); };

    WEBCORE_EXPORT bool isNode() const { return false; };
    bool removeAllListeners();
    WEBCORE_EXPORT void addListenerForBindings(const Identifier& eventType, RefPtr<EventListener>&&, bool, bool);
    WEBCORE_EXPORT void removeListenerForBindings(const Identifier& eventType, RefPtr<EventListener>&&);
    WEBCORE_EXPORT void removeAllListenersForBindings(const Identifier& eventType);
    WEBCORE_EXPORT bool emitForBindings(const Identifier&, const MarkedArgumentBuffer&);

    WEBCORE_EXPORT bool addListener(const Identifier& eventType, Ref<EventListener>&&, bool, bool);
    WEBCORE_EXPORT bool removeListener(const Identifier& eventType, EventListener&);
    WEBCORE_EXPORT bool removeAllListeners(const Identifier& eventType);

    WEBCORE_EXPORT void emit(const Identifier&, const MarkedArgumentBuffer&);
    WEBCORE_EXPORT void uncaughtExceptionInEventHandler();

    WEBCORE_EXPORT Vector<Identifier> getEventNames();
    WEBCORE_EXPORT Vector<JSObject*> getListeners(const Identifier& eventType);
    WEBCORE_EXPORT int listenerCount(const Identifier& eventType);

    bool hasEventListeners() const;
    bool hasEventListeners(const Identifier& eventType) const;
    bool hasActiveEventListeners(const Identifier& eventType) const;
    bool hasEventListeners(JSC::VM& vm, ASCIILiteral eventType) const;

    WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)> onDidChangeListener = WTF::Function<void(EventEmitter&, const Identifier& eventName, bool isAdded)>(nullptr);

    unsigned getMaxListeners() const { return m_maxListeners; };

    void setMaxListeners(unsigned count);

    Vector<Identifier> eventTypes();
    const SimpleEventListenerVector& eventListeners(const Identifier& eventType);

    void fireEventListeners(const Identifier& eventName, const MarkedArgumentBuffer& arguments);
    bool isFiringEventListeners() const;

    void invalidateJSEventListeners(JSC::JSObject*);

    const EventEmitterData* eventTargetData() const;

    IdentifierEventListenerMap& eventListenerMap() { return ensureEventEmitterData().eventListenerMap; }

    void setThisObject(JSC::JSValue thisObject)
    {
        m_thisObject.clear();

        if (thisObject.isCell()) {
            m_thisObject = JSC::Weak<JSC::JSObject>(thisObject.getObject());
        }
    }

    bool m_hasIPCRef { false };

private:
    EventEmitter(ScriptExecutionContext& context)
        : ContextDestructionObserver(&context)
    {
    }

    EventEmitterData* eventTargetData() { return &m_eventTargetData; }
    EventEmitterData* eventTargetDataConcurrently() { return &m_eventTargetData; }
    EventEmitterData& ensureEventEmitterData() { return m_eventTargetData; }
    void eventListenersDidChange()
    {
    }

    void innerInvokeEventListeners(const Identifier&, SimpleEventListenerVector, const MarkedArgumentBuffer& arguments);
    void invalidateEventListenerRegions();

    EventEmitterData m_eventTargetData;
    unsigned m_maxListeners { 10 };

    mutable JSC::Weak<JSC::JSObject> m_thisObject { nullptr };
};

inline const EventEmitterData* EventEmitter::eventTargetData() const
{
    return const_cast<EventEmitter*>(this)->eventTargetData();
}

inline bool EventEmitter::isFiringEventListeners() const
{
    auto* data = eventTargetData();
    return data && data->isFiringEventListeners;
}

inline bool EventEmitter::hasEventListeners() const
{
    auto* data = eventTargetData();
    return data && !data->eventListenerMap.isEmpty();
}

inline bool EventEmitter::hasEventListeners(const Identifier& eventType) const
{
    auto* data = eventTargetData();
    return data && data->eventListenerMap.contains(eventType);
}

inline bool EventEmitter::hasEventListeners(JSC::VM& vm, ASCIILiteral eventType) const
{
    return this->hasEventListeners(Identifier::fromString(vm, eventType));
}

inline void EventEmitter::setMaxListeners(unsigned count)
{
    this->m_maxListeners = count;
}

} // namespace WebCore