aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js
diff options
context:
space:
mode:
authorGravatar Zilin Zhu <zhuzilinallen@gmail.com> 2022-08-22 23:22:40 +0800
committerGravatar GitHub <noreply@github.com> 2022-08-22 08:22:40 -0700
commit6a384415623a97e2fee684754d5d9ff0d397143c (patch)
tree5d9eab6cd2b5a8f010c93acd10f367274c59f881 /src/bun.js
parent4b8409096d3f1d7dbc1ef4e550ed8ec1b71c25fd (diff)
downloadbun-6a384415623a97e2fee684754d5d9ff0d397143c.tar.gz
bun-6a384415623a97e2fee684754d5d9ff0d397143c.tar.zst
bun-6a384415623a97e2fee684754d5d9ff0d397143c.zip
Support emit Symbol events in EventEmitter (#1129)
Diffstat (limited to 'src/bun.js')
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.cpp53
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.h57
-rw-r--r--src/bun.js/bindings/webcore/EventListenerMap.cpp29
-rw-r--r--src/bun.js/bindings/webcore/EventListenerMap.h2
-rw-r--r--src/bun.js/bindings/webcore/IdentifierEventListenerMap.cpp156
-rw-r--r--src/bun.js/bindings/webcore/IdentifierEventListenerMap.h67
-rw-r--r--src/bun.js/bindings/webcore/JSEventEmitter.cpp36
7 files changed, 278 insertions, 122 deletions
diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp
index 1c71f9b72..2c80e9cc2 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/EventEmitter.cpp
@@ -4,7 +4,6 @@
#include "EventEmitter.h"
-#include "AddEventListenerOptions.h"
#include "DOMWrapperWorld.h"
#include "EventNames.h"
#include "JSErrorHandler.h"
@@ -26,15 +25,15 @@ Ref<EventEmitter> EventEmitter::create(ScriptExecutionContext& context)
return adoptRef(*new EventEmitter(context));
}
-bool EventEmitter::addListener(const AtomString& eventType, Ref<EventListener>&& listener, bool once, bool prepend)
+bool EventEmitter::addListener(const Identifier& eventType, Ref<EventListener>&& listener, bool once, bool prepend)
{
bool listenerCreatedFromScript = is<JSEventListener>(listener) && !downcast<JSEventListener>(listener.get()).wasCreatedFromMarkup();
if (prepend) {
- if (!ensureEventEmitterData().eventListenerMap.prepend(eventType, listener.copyRef(), { false, false, once }))
+ if (!ensureEventEmitterData().eventListenerMap.prepend(eventType, listener.copyRef(), once))
return false;
} else {
- if (!ensureEventEmitterData().eventListenerMap.add(eventType, listener.copyRef(), { false, false, once }))
+ if (!ensureEventEmitterData().eventListenerMap.add(eventType, listener.copyRef(), once))
return false;
}
@@ -43,7 +42,7 @@ bool EventEmitter::addListener(const AtomString& eventType, Ref<EventListener>&&
return true;
}
-void EventEmitter::addListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener, bool once, bool prepend)
+void EventEmitter::addListenerForBindings(const Identifier& eventType, RefPtr<EventListener>&& listener, bool once, bool prepend)
{
if (!listener)
return;
@@ -51,7 +50,7 @@ void EventEmitter::addListenerForBindings(const AtomString& eventType, RefPtr<Ev
addListener(eventType, listener.releaseNonNull(), once, prepend);
}
-void EventEmitter::removeListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&& listener)
+void EventEmitter::removeListenerForBindings(const Identifier& eventType, RefPtr<EventListener>&& listener)
{
if (!listener)
return;
@@ -59,50 +58,44 @@ void EventEmitter::removeListenerForBindings(const AtomString& eventType, RefPtr
removeListener(eventType, *listener);
}
-bool EventEmitter::removeListener(const AtomString& eventType, EventListener& listener)
+bool EventEmitter::removeListener(const Identifier& eventType, EventListener& listener)
{
auto* data = eventTargetData();
if (!data)
return false;
- if (data->eventListenerMap.remove(eventType, listener, false)) {
- if (eventNames().isWheelEventType(eventType))
- invalidateEventListenerRegions();
-
+ if (data->eventListenerMap.remove(eventType, listener)) {
eventListenersDidChange();
return true;
}
return false;
}
-void EventEmitter::removeAllListenersForBindings(const AtomString& eventType)
+void EventEmitter::removeAllListenersForBindings(const Identifier& eventType)
{
removeAllListeners(eventType);
}
-bool EventEmitter::removeAllListeners(const AtomString& eventType)
+bool EventEmitter::removeAllListeners(const Identifier& eventType)
{
auto* data = eventTargetData();
if (!data)
return false;
if (data->eventListenerMap.removeAll(eventType)) {
- if (eventNames().isWheelEventType(eventType))
- invalidateEventListenerRegions();
-
eventListenersDidChange();
return true;
}
return false;
}
-bool EventEmitter::hasActiveEventListeners(const AtomString& eventType) const
+bool EventEmitter::hasActiveEventListeners(const Identifier& eventType) const
{
auto* data = eventTargetData();
return data && data->eventListenerMap.containsActive(eventType);
}
-bool EventEmitter::emitForBindings(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+bool EventEmitter::emitForBindings(const Identifier& eventType, const MarkedArgumentBuffer& arguments)
{
if (!scriptExecutionContext())
return false;
@@ -111,7 +104,7 @@ bool EventEmitter::emitForBindings(const AtomString& eventType, const MarkedArgu
return true;
}
-void EventEmitter::emit(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+void EventEmitter::emit(const Identifier& eventType, const MarkedArgumentBuffer& arguments)
{
fireEventListeners(eventType, arguments);
}
@@ -120,7 +113,7 @@ void EventEmitter::uncaughtExceptionInEventHandler()
{
}
-Vector<AtomString> EventEmitter::getEventNames()
+Vector<Identifier> EventEmitter::getEventNames()
{
auto* data = eventTargetData();
if (!data)
@@ -128,7 +121,7 @@ Vector<AtomString> EventEmitter::getEventNames()
return data->eventListenerMap.eventTypes();
}
-int EventEmitter::listenerCount(const AtomString& eventType)
+int EventEmitter::listenerCount(const Identifier& eventType)
{
auto* data = eventTargetData();
if (!data)
@@ -147,7 +140,7 @@ int EventEmitter::listenerCount(const AtomString& eventType)
return result;
}
-Vector<JSObject*> EventEmitter::getListeners(const AtomString& eventType)
+Vector<JSObject*> EventEmitter::getListeners(const Identifier& eventType)
{
auto* data = eventTargetData();
if (!data)
@@ -166,14 +159,8 @@ Vector<JSObject*> EventEmitter::getListeners(const AtomString& eventType)
return listeners;
}
-static const AtomString& legacyType(const Event& event)
-{
-
- return nullAtom();
-}
-
// https://dom.spec.whatwg.org/#concept-event-listener-invoke
-void EventEmitter::fireEventListeners(const AtomString& eventType, const MarkedArgumentBuffer& arguments)
+void EventEmitter::fireEventListeners(const Identifier& eventType, const MarkedArgumentBuffer& arguments)
{
ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
@@ -192,7 +179,7 @@ void EventEmitter::fireEventListeners(const AtomString& eventType, const MarkedA
// Intentionally creates a copy of the listeners vector to avoid event listeners added after this point from being run.
// Note that removal still has an effect due to the removed field in RegisteredEventListener.
// https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke
-void EventEmitter::innerInvokeEventListeners(const AtomString& eventType, EventListenerVector listeners, const MarkedArgumentBuffer& arguments)
+void EventEmitter::innerInvokeEventListeners(const Identifier& eventType, SimpleEventListenerVector listeners, const MarkedArgumentBuffer& arguments)
{
Ref<EventEmitter> protectedThis(*this);
ASSERT(!listeners.isEmpty());
@@ -223,18 +210,18 @@ void EventEmitter::innerInvokeEventListeners(const AtomString& eventType, EventL
}
}
-Vector<AtomString> EventEmitter::eventTypes()
+Vector<Identifier> EventEmitter::eventTypes()
{
if (auto* data = eventTargetData())
return data->eventListenerMap.eventTypes();
return {};
}
-const EventListenerVector& EventEmitter::eventListeners(const AtomString& eventType)
+const SimpleEventListenerVector& EventEmitter::eventListeners(const Identifier& eventType)
{
auto* data = eventTargetData();
auto* listenerVector = data ? data->eventListenerMap.find(eventType) : nullptr;
- static NeverDestroyed<EventListenerVector> emptyVector;
+ static NeverDestroyed<SimpleEventListenerVector> emptyVector;
return listenerVector ? *listenerVector : emptyVector.get();
}
diff --git a/src/bun.js/bindings/webcore/EventEmitter.h b/src/bun.js/bindings/webcore/EventEmitter.h
index 35650f380..d6fd20c65 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.h
+++ b/src/bun.js/bindings/webcore/EventEmitter.h
@@ -1,7 +1,6 @@
#pragma once
-#include "EventListenerMap.h"
-#include "EventListenerOptions.h"
+#include "IdentifierEventListenerMap.h"
#include "ExceptionOr.h"
#include "ContextDestructionObserver.h"
#include "ScriptWrappable.h"
@@ -20,7 +19,6 @@ class JSObject;
namespace WebCore {
-struct AddEventListenerOptions;
class DOMWrapperWorld;
class JSEventListener;
@@ -30,7 +28,7 @@ struct EventEmitterData {
public:
EventEmitterData() = default;
- EventListenerMap eventListenerMap;
+ IdentifierEventListenerMap eventListenerMap;
bool isFiringEventListeners { false };
};
@@ -48,34 +46,32 @@ public:
WEBCORE_EXPORT bool isNode() const { return false; };
- WEBCORE_EXPORT void addListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&&, bool, bool);
- WEBCORE_EXPORT void removeListenerForBindings(const AtomString& eventType, RefPtr<EventListener>&&);
- WEBCORE_EXPORT void removeAllListenersForBindings(const AtomString& eventType);
- WEBCORE_EXPORT bool emitForBindings(const AtomString&, const MarkedArgumentBuffer&);
+ 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 AtomString& eventType, Ref<EventListener>&&, bool, bool);
- WEBCORE_EXPORT bool removeListener(const AtomString& eventType, EventListener&);
- WEBCORE_EXPORT bool removeAllListeners(const AtomString& eventType);
+ 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 AtomString&, const MarkedArgumentBuffer&);
+ WEBCORE_EXPORT void emit(const Identifier&, const MarkedArgumentBuffer&);
WEBCORE_EXPORT void uncaughtExceptionInEventHandler();
- WEBCORE_EXPORT Vector<AtomString> getEventNames();
- WEBCORE_EXPORT Vector<JSObject*> getListeners(const AtomString& eventType);
- WEBCORE_EXPORT int listenerCount(const AtomString& eventType);
+ 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 AtomString& eventType) const;
- bool hasCapturingEventListeners(const AtomString& eventType);
- bool hasActiveEventListeners(const AtomString& eventType) const;
+ bool hasEventListeners(const Identifier& eventType) const;
+ bool hasActiveEventListeners(const Identifier& eventType) const;
- Vector<AtomString> eventTypes();
- const EventListenerVector& eventListeners(const AtomString& eventType);
+ Vector<Identifier> eventTypes();
+ const SimpleEventListenerVector& eventListeners(const Identifier& eventType);
- void fireEventListeners(const AtomString& eventName, const MarkedArgumentBuffer& arguments);
+ void fireEventListeners(const Identifier& eventName, const MarkedArgumentBuffer& arguments);
bool isFiringEventListeners() const;
- template<typename Visitor> void visitJSEventListeners(Visitor&);
void invalidateJSEventListeners(JSC::JSObject*);
const EventEmitterData* eventTargetData() const;
@@ -90,7 +86,7 @@ private:
EventEmitterData& ensureEventEmitterData() { return m_eventTargetData; }
void eventListenersDidChange() {}
- void innerInvokeEventListeners(const AtomString&, EventListenerVector, const MarkedArgumentBuffer& arguments);
+ void innerInvokeEventListeners(const Identifier&, SimpleEventListenerVector, const MarkedArgumentBuffer& arguments);
void invalidateEventListenerRegions();
EventEmitterData m_eventTargetData;
@@ -113,23 +109,10 @@ inline bool EventEmitter::hasEventListeners() const
return data && !data->eventListenerMap.isEmpty();
}
-inline bool EventEmitter::hasEventListeners(const AtomString& eventType) const
+inline bool EventEmitter::hasEventListeners(const Identifier& eventType) const
{
auto* data = eventTargetData();
return data && data->eventListenerMap.contains(eventType);
}
-inline bool EventEmitter::hasCapturingEventListeners(const AtomString& eventType)
-{
- auto* data = eventTargetData();
- return data && data->eventListenerMap.containsCapturing(eventType);
-}
-
-template<typename Visitor>
-void EventEmitter::visitJSEventListeners(Visitor& visitor)
-{
- if (auto* data = eventTargetDataConcurrently())
- data->eventListenerMap.visitJSEventListeners(visitor);
-}
-
} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/EventListenerMap.cpp b/src/bun.js/bindings/webcore/EventListenerMap.cpp
index bb6cf0a21..5014cfa00 100644
--- a/src/bun.js/bindings/webcore/EventListenerMap.cpp
+++ b/src/bun.js/bindings/webcore/EventListenerMap.cpp
@@ -129,21 +129,6 @@ bool EventListenerMap::add(const AtomString& eventType, Ref<EventListener>&& lis
return true;
}
-bool EventListenerMap::prepend(const AtomString& eventType, Ref<EventListener>&& listener, const RegisteredEventListener::Options& options)
-{
- Locker locker { m_lock };
-
- if (auto* listeners = find(eventType)) {
- if (findListener(*listeners, listener, options.capture) != notFound)
- return false; // Duplicate listener.
- listeners->insert(0, RegisteredEventListener::create(WTFMove(listener), options));
- return true;
- }
-
- m_entries.append({ eventType, EventListenerVector { RegisteredEventListener::create(WTFMove(listener), options) } });
- return true;
-}
-
static bool removeListenerFromVector(EventListenerVector& listeners, EventListener& listener, bool useCapture)
{
size_t indexOfRemovedListener = findListener(listeners, listener, useCapture);
@@ -171,20 +156,6 @@ bool EventListenerMap::remove(const AtomString& eventType, EventListener& listen
return false;
}
-bool EventListenerMap::removeAll(const AtomString& eventType)
-{
- Locker locker { m_lock };
-
- for (unsigned i = 0; i < m_entries.size(); ++i) {
- if (m_entries[i].first == eventType) {
- m_entries.remove(i);
- return true;
- }
- }
-
- return false;
-}
-
EventListenerVector* EventListenerMap::find(const AtomString& eventType)
{
for (auto& entry : m_entries) {
diff --git a/src/bun.js/bindings/webcore/EventListenerMap.h b/src/bun.js/bindings/webcore/EventListenerMap.h
index 3467e8403..d5b83c220 100644
--- a/src/bun.js/bindings/webcore/EventListenerMap.h
+++ b/src/bun.js/bindings/webcore/EventListenerMap.h
@@ -58,9 +58,7 @@ public:
void replace(const AtomString& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, const RegisteredEventListener::Options&);
bool add(const AtomString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&);
- bool prepend(const AtomString& eventType, Ref<EventListener>&&, const RegisteredEventListener::Options&);
bool remove(const AtomString& eventType, EventListener&, bool useCapture);
- bool removeAll(const AtomString& eventType);
WEBCORE_EXPORT EventListenerVector* find(const AtomString& eventType);
const EventListenerVector* find(const AtomString& eventType) const { return const_cast<EventListenerMap*>(this)->find(eventType); }
Vector<AtomString> eventTypes() const;
diff --git a/src/bun.js/bindings/webcore/IdentifierEventListenerMap.cpp b/src/bun.js/bindings/webcore/IdentifierEventListenerMap.cpp
new file mode 100644
index 000000000..2a5d5f831
--- /dev/null
+++ b/src/bun.js/bindings/webcore/IdentifierEventListenerMap.cpp
@@ -0,0 +1,156 @@
+#include "config.h"
+#include "IdentifierEventListenerMap.h"
+
+#include "Event.h"
+#include "EventTarget.h"
+#include "JSEventListener.h"
+#include <wtf/MainThread.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Vector.h>
+
+
+namespace WebCore {
+
+IdentifierEventListenerMap::IdentifierEventListenerMap() = default;
+
+bool IdentifierEventListenerMap::containsActive(const JSC::Identifier& eventType) const
+{
+ return false;
+}
+
+void IdentifierEventListenerMap::clear()
+{
+ Locker locker { m_lock };
+
+ for (auto& entry : m_entries) {
+ for (auto& listener : entry.second)
+ listener->markAsRemoved();
+ }
+
+ m_entries.clear();
+}
+
+Vector<JSC::Identifier> IdentifierEventListenerMap::eventTypes() const
+{
+ return m_entries.map([](auto& entry) {
+ return entry.first;
+ });
+}
+
+static inline size_t findListener(const SimpleEventListenerVector& listeners, EventListener& listener)
+{
+ for (size_t i = 0; i < listeners.size(); ++i) {
+ auto& registeredListener = listeners[i];
+ if (registeredListener->callback() == listener)
+ return i;
+ }
+ return notFound;
+}
+
+void IdentifierEventListenerMap::replace(const JSC::Identifier& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, bool once)
+{
+ Locker locker { m_lock };
+
+ auto* listeners = find(eventType);
+ ASSERT(listeners);
+ size_t index = findListener(*listeners, oldListener);
+ ASSERT(index != notFound);
+ auto& registeredListener = listeners->at(index);
+ registeredListener->markAsRemoved();
+ registeredListener = SimpleRegisteredEventListener::create(WTFMove(newListener), once);
+}
+
+bool IdentifierEventListenerMap::add(const JSC::Identifier& eventType, Ref<EventListener>&& listener, bool once)
+{
+ Locker locker { m_lock };
+
+ if (auto* listeners = find(eventType)) {
+ if (findListener(*listeners, listener) != notFound)
+ return false; // Duplicate listener.
+ listeners->append(SimpleRegisteredEventListener::create(WTFMove(listener), once));
+ return true;
+ }
+
+ m_entries.append({ eventType, SimpleEventListenerVector { SimpleRegisteredEventListener::create(WTFMove(listener), once) } });
+ return true;
+}
+
+bool IdentifierEventListenerMap::prepend(const JSC::Identifier& eventType, Ref<EventListener>&& listener, bool once)
+{
+ Locker locker { m_lock };
+
+ if (auto* listeners = find(eventType)) {
+ if (findListener(*listeners, listener) != notFound)
+ return false; // Duplicate listener.
+ listeners->insert(0, SimpleRegisteredEventListener::create(WTFMove(listener), once));
+ return true;
+ }
+
+ m_entries.append({ eventType, SimpleEventListenerVector { SimpleRegisteredEventListener::create(WTFMove(listener), once) } });
+ return true;
+}
+
+static bool removeListenerFromVector(SimpleEventListenerVector& listeners, EventListener& listener)
+{
+ size_t indexOfRemovedListener = findListener(listeners, listener);
+ if (UNLIKELY(indexOfRemovedListener == notFound))
+ return false;
+
+ listeners[indexOfRemovedListener]->markAsRemoved();
+ listeners.remove(indexOfRemovedListener);
+ return true;
+}
+
+bool IdentifierEventListenerMap::remove(const JSC::Identifier& eventType, EventListener& listener)
+{
+ Locker locker { m_lock };
+
+ for (unsigned i = 0; i < m_entries.size(); ++i) {
+ if (m_entries[i].first == eventType) {
+ bool wasRemoved = removeListenerFromVector(m_entries[i].second, listener);
+ if (m_entries[i].second.isEmpty())
+ m_entries.remove(i);
+ return wasRemoved;
+ }
+ }
+
+ return false;
+}
+
+bool IdentifierEventListenerMap::removeAll(const JSC::Identifier& eventType)
+{
+ Locker locker { m_lock };
+
+ for (unsigned i = 0; i < m_entries.size(); ++i) {
+ if (m_entries[i].first == eventType) {
+ m_entries.remove(i);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+SimpleEventListenerVector* IdentifierEventListenerMap::find(const JSC::Identifier& eventType)
+{
+ for (auto& entry : m_entries) {
+ if (entry.first == eventType)
+ return &entry.second;
+ }
+
+ return nullptr;
+}
+
+static void removeFirstListenerCreatedFromMarkup(SimpleEventListenerVector& listenerVector)
+{
+ bool foundListener = listenerVector.removeFirstMatching([] (const auto& registeredListener) {
+ if (JSEventListener::wasCreatedFromMarkup(registeredListener->callback())) {
+ registeredListener->markAsRemoved();
+ return true;
+ }
+ return false;
+ });
+ ASSERT_UNUSED(foundListener, foundListener);
+}
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/IdentifierEventListenerMap.h b/src/bun.js/bindings/webcore/IdentifierEventListenerMap.h
new file mode 100644
index 000000000..34da21122
--- /dev/null
+++ b/src/bun.js/bindings/webcore/IdentifierEventListenerMap.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <wtf/Forward.h>
+#include <wtf/Lock.h>
+#include <wtf/Ref.h>
+#include <JavaScriptCore/Identifier.h>
+#include "EventListener.h"
+
+namespace WebCore {
+
+class SimpleRegisteredEventListener : public RefCounted<SimpleRegisteredEventListener> {
+public:
+ static Ref<SimpleRegisteredEventListener> create(Ref<EventListener>&& listener, bool once)
+ {
+ return adoptRef(*new SimpleRegisteredEventListener(WTFMove(listener), once));
+ }
+
+ EventListener& callback() const { return m_callback; }
+ bool isOnce() const { return m_isOnce; }
+ bool wasRemoved() const { return m_wasRemoved; }
+
+ void markAsRemoved() { m_wasRemoved = true; }
+
+private:
+ SimpleRegisteredEventListener(Ref<EventListener>&& listener, bool once)
+ : m_isOnce(once)
+ , m_wasRemoved(false)
+ , m_callback(WTFMove(listener))
+ {
+ }
+
+ bool m_isOnce : 1;
+ bool m_wasRemoved : 1;
+ Ref<EventListener> m_callback;
+};
+
+using SimpleEventListenerVector = Vector<RefPtr<SimpleRegisteredEventListener>, 1, CrashOnOverflow, 2>;
+
+class IdentifierEventListenerMap {
+public:
+ IdentifierEventListenerMap();
+
+ bool isEmpty() const { return m_entries.isEmpty(); }
+ bool contains(const JSC::Identifier& eventType) const { return find(eventType); }
+ bool containsActive(const JSC::Identifier& eventType) const;
+
+ void clear();
+
+ void replace(const JSC::Identifier& eventType, EventListener& oldListener, Ref<EventListener>&& newListener, bool once);
+ bool add(const JSC::Identifier& eventType, Ref<EventListener>&&, bool once);
+ bool prepend(const JSC::Identifier& eventType, Ref<EventListener>&&, bool once);
+ bool remove(const JSC::Identifier& eventType, EventListener&);
+ bool removeAll(const JSC::Identifier& eventType);
+ WEBCORE_EXPORT SimpleEventListenerVector* find(const JSC::Identifier& eventType);
+ const SimpleEventListenerVector* find(const JSC::Identifier& eventType) const { return const_cast<IdentifierEventListenerMap*>(this)->find(eventType); }
+ Vector<JSC::Identifier> eventTypes() const;
+
+ Lock& lock() { return m_lock; }
+
+private:
+ Vector<std::pair<JSC::Identifier, SimpleEventListenerVector>> m_entries;
+ Lock m_lock;
+};
+
+} // namespace WebCore
diff --git a/src/bun.js/bindings/webcore/JSEventEmitter.cpp b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
index 3971243f9..2b4edb178 100644
--- a/src/bun.js/bindings/webcore/JSEventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/JSEventEmitter.cpp
@@ -27,6 +27,7 @@
#include "WebCoreJSClientData.h"
#include <JavaScriptCore/FunctionPrototype.h>
#include <JavaScriptCore/HeapAnalyzer.h>
+#include <JavaScriptCore/Identifier.h>
#include <JavaScriptCore/JSCInlines.h>
#include <JavaScriptCore/JSDestructibleObjectHeapCellType.h>
#include <JavaScriptCore/SlotVisitorMacros.h>
@@ -217,12 +218,12 @@ static inline JSC::EncodedJSValue addListener(JSC::JSGlobalObject* lexicalGlobal
if (UNLIKELY(callFrame->argumentCount() < 2))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ auto eventType = argument0.value().toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "addListener"); });
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addListenerForBindings(WTFMove(type), WTFMove(listener), once, prepend); }));
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addListenerForBindings(WTFMove(eventType), WTFMove(listener), once, prepend); }));
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
vm.writeBarrier(&static_cast<JSObject&>(*castedThis), argument1.value());
return result;
@@ -278,12 +279,12 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeListener
if (UNLIKELY(callFrame->argumentCount() < 2))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ auto eventType = argument0.value().toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
EnsureStillAliveScope argument1 = callFrame->uncheckedArgument(1);
auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument1.value(), *castedThis, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 1, "listener", "EventEmitter", "removeListener"); });
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeListenerForBindings(WTFMove(type), WTFMove(listener)); }));
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeListenerForBindings(WTFMove(eventType), WTFMove(listener)); }));
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
vm.writeBarrier(&static_cast<JSObject&>(*castedThis), argument1.value());
return result;
@@ -304,9 +305,9 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_removeAllListe
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
EnsureStillAliveScope argument0 = callFrame->uncheckedArgument(0);
- auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
+ auto eventType = argument0.value().toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeAllListenersForBindings(WTFMove(type)); }));
+ auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.removeAllListenersForBindings(WTFMove(eventType)); }));
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
return result;
}
@@ -326,13 +327,12 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_emitBody(JSC::
size_t argumentCount = callFrame->argumentCount();
if (UNLIKELY(argumentCount < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
- auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(0).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
JSC::MarkedArgumentBuffer args;
for (size_t i = 1; i < argumentCount; ++i) {
args.append(callFrame->uncheckedArgument(i));
}
- auto eventType = argument0->toAtomString(lexicalGlobalObject);
RELEASE_AND_RETURN(throwScope, JSValue::encode(toJS<IDLBoolean>(*lexicalGlobalObject, throwScope, impl.emitForBindings(eventType, args))));
}
@@ -350,7 +350,7 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_eventNamesBody
auto& impl = castedThis->wrapped();
JSC::MarkedArgumentBuffer args;
for (auto& name : impl.getEventNames()) {
- args.append(jsOwnedString(vm, name));
+ args.append(JSC::identifierToSafePublicJSValue(vm, name));
}
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::constructArray(lexicalGlobalObject, static_cast<JSC::ArrayAllocationProfile*>(nullptr), WTFMove(args))));
}
@@ -369,9 +369,8 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_listenerCountB
auto& impl = castedThis->wrapped();
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
- auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(0).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument0->toAtomString(lexicalGlobalObject);
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl.listenerCount(eventType))));
}
@@ -389,9 +388,8 @@ static inline JSC::EncodedJSValue jsEventEmitterPrototypeFunction_listenersBody(
auto& impl = castedThis->wrapped();
if (UNLIKELY(callFrame->argumentCount() < 1))
return throwVMError(lexicalGlobalObject, throwScope, createNotEnoughArgumentsError(lexicalGlobalObject));
- auto* argument0 = callFrame->uncheckedArgument(0).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(0).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument0->toAtomString(lexicalGlobalObject);
JSC::MarkedArgumentBuffer args;
for (auto* listener : impl.getListeners(eventType)) {
args.append(listener);
@@ -480,9 +478,8 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionGetEventListeners,
return JSValue::encode(JSC::jsUndefined());
}
auto* impl = JSEventEmitter::toWrapped(vm, argument0);
- auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument1->toAtomString(lexicalGlobalObject);
JSC::MarkedArgumentBuffer args;
for (auto* listener : impl->getListeners(eventType)) {
args.append(listener);
@@ -505,9 +502,8 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionListenerCount,
return JSValue::encode(JSC::jsUndefined());
}
auto* impl = JSEventEmitter::toWrapped(vm, argument0);
- auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument1->toAtomString(lexicalGlobalObject);
RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::jsNumber(impl->listenerCount(eventType))));
}
@@ -527,9 +523,8 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOnce,
return JSValue::encode(JSC::jsUndefined());
}
auto* impl = JSEventEmitter::toWrapped(vm, argument0);
- auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument1->toAtomString(lexicalGlobalObject);
EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2);
auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); });
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
@@ -555,9 +550,8 @@ JSC_DEFINE_HOST_FUNCTION(Events_functionOn,
return JSValue::encode(JSC::jsUndefined());
}
auto* impl = JSEventEmitter::toWrapped(vm, argument0);
- auto* argument1 = callFrame->uncheckedArgument(1).toString(lexicalGlobalObject);
+ auto eventType = callFrame->uncheckedArgument(1).toPropertyKey(lexicalGlobalObject);
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
- auto eventType = argument1->toAtomString(lexicalGlobalObject);
EnsureStillAliveScope argument2 = callFrame->uncheckedArgument(2);
auto listener = convert<IDLNullable<IDLEventListener<JSEventListener>>>(*lexicalGlobalObject, argument2.value(), *argument0, [](JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope) { throwArgumentMustBeObjectError(lexicalGlobalObject, scope, 2, "listener", "EventEmitter", "removeListener"); });
RETURN_IF_EXCEPTION(throwScope, encodedJSValue());