Skip to content

Commit

Permalink
Support emit Symbol events in EventEmitter (#1129)
Browse files Browse the repository at this point in the history
  • Loading branch information
zhuzilin authored Aug 22, 2022
1 parent 4b84090 commit 6a38441
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 122 deletions.
53 changes: 20 additions & 33 deletions src/bun.js/bindings/webcore/EventEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "EventEmitter.h"

#include "AddEventListenerOptions.h"
#include "DOMWrapperWorld.h"
#include "EventNames.h"
#include "JSErrorHandler.h"
Expand All @@ -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;
}

Expand All @@ -43,66 +42,60 @@ 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;

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;

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;
Expand All @@ -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);
}
Expand All @@ -120,15 +113,15 @@ void EventEmitter::uncaughtExceptionInEventHandler()
{
}

Vector<AtomString> EventEmitter::getEventNames()
Vector<Identifier> EventEmitter::getEventNames()
{
auto* data = eventTargetData();
if (!data)
return {};
return data->eventListenerMap.eventTypes();
}

int EventEmitter::listenerCount(const AtomString& eventType)
int EventEmitter::listenerCount(const Identifier& eventType)
{
auto* data = eventTargetData();
if (!data)
Expand All @@ -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)
Expand All @@ -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());

Expand All @@ -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());
Expand Down Expand Up @@ -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();
}

Expand Down
57 changes: 20 additions & 37 deletions src/bun.js/bindings/webcore/EventEmitter.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#pragma once

#include "EventListenerMap.h"
#include "EventListenerOptions.h"
#include "IdentifierEventListenerMap.h"
#include "ExceptionOr.h"
#include "ContextDestructionObserver.h"
#include "ScriptWrappable.h"
Expand All @@ -20,7 +19,6 @@ class JSObject;

namespace WebCore {

struct AddEventListenerOptions;
class DOMWrapperWorld;
class JSEventListener;

Expand All @@ -30,7 +28,7 @@ struct EventEmitterData {

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

Expand All @@ -48,34 +46,32 @@ class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr<EventEm

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;
Expand All @@ -90,7 +86,7 @@ class EventEmitter final : public ScriptWrappable, public CanMakeWeakPtr<EventEm
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;
Expand All @@ -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
29 changes: 0 additions & 29 deletions src/bun.js/bindings/webcore/EventListenerMap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down
2 changes: 0 additions & 2 deletions src/bun.js/bindings/webcore/EventListenerMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ class EventListenerMap {

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;
Expand Down
Loading

0 comments on commit 6a38441

Please sign in to comment.