Add async context tracking for event listeners in JSEventListener

Co-authored-by: jarred <jarred@bun.sh>
This commit is contained in:
Cursor Agent
2025-07-01 22:30:00 +00:00
parent 1b5c6fcfb5
commit 862b8eb164
2 changed files with 53 additions and 1 deletions

View File

@@ -20,6 +20,7 @@
#include "config.h"
#include "JSEventListener.h"
#include "AsyncContextFrame.h"
#include "BunProcess.h"
// #include "BeforeUnloadEvent.h"
// #include "ContentSecurityPolicy.h"
@@ -58,6 +59,17 @@ JSEventListener::JSEventListener(JSObject* function, JSObject* wrapper, bool isA
ASSERT(wrapper);
m_jsFunction = JSC::Weak<JSC::JSObject>(function);
m_isInitialized = true;
// Capture the current async context if available
if (auto* globalObject = wrapper->globalObject()) {
if (globalObject->isAsyncContextTrackingEnabled()) {
JSValue context = globalObject->m_asyncContextData.get()->getInternalField(0);
if (!context.isUndefined()) {
// Store the current async context
m_asyncContext = JSC::Weak<JSC::Unknown>(context);
}
}
}
}
}
@@ -87,6 +99,23 @@ void JSEventListener::replaceJSFunctionForAttributeListener(JSObject* function,
m_wrapper = Weak { wrapper };
m_isInitialized = true;
}
// Capture the current async context for the new function
if (auto* globalObject = wrapper->globalObject()) {
if (globalObject->isAsyncContextTrackingEnabled()) {
JSValue context = globalObject->m_asyncContextData.get()->getInternalField(0);
if (!context.isUndefined()) {
// Store the current async context
m_asyncContext = JSC::Weak<JSC::Unknown>(context);
} else {
// Clear the async context if no context is available
m_asyncContext = JSC::Weak<JSC::Unknown>();
}
} else {
// Clear the async context if tracking is disabled
m_asyncContext = JSC::Weak<JSC::Unknown>();
}
}
}
JSValue eventHandlerAttribute(EventTarget& eventTarget, const AtomString& eventType, DOMWrapperWorld& isolatedWorld)
@@ -109,6 +138,10 @@ inline void JSEventListener::visitJSFunctionImpl(Visitor& visitor)
return;
visitor.append(m_jsFunction);
// Visit the async context to prevent it from being garbage collected
if (m_asyncContext)
visitor.append(m_asyncContext);
}
void JSEventListener::visitJSFunction(AbstractSlotVisitor& visitor) { visitJSFunctionImpl(visitor); }
@@ -228,7 +261,21 @@ void JSEventListener::handleEvent(ScriptExecutionContext& scriptExecutionContext
JSValue thisValue = handleEventFunction == jsFunction ? toJS(lexicalGlobalObject, globalObject, event.currentTarget()) : jsFunction;
NakedPtr<JSC::Exception> uncaughtException;
JSValue retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException);
JSValue retval;
// If we have an async context, restore it during the call
if (m_asyncContext && m_asyncContext.get()) {
auto* asyncContextData = lexicalGlobalObject->m_asyncContextData.get();
JSValue restoreAsyncContext = asyncContextData->getInternalField(0);
asyncContextData->putInternalField(lexicalGlobalObject->vm(), 0, m_asyncContext.get());
retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException);
// Restore the previous async context
asyncContextData->putInternalField(lexicalGlobalObject->vm(), 0, restoreAsyncContext);
} else {
retval = JSC::profiledCall(lexicalGlobalObject, JSC::ProfilingReason::Other, handleEventFunction, callData, thisValue, args, uncaughtException);
}
// InspectorInstrumentation::didCallFunction(&scriptExecutionContext);

View File

@@ -34,6 +34,8 @@
namespace WebCore {
class AsyncContextFrame;
class JSEventListener : public EventListener {
public:
WEBCORE_EXPORT static Ref<JSEventListener> create(JSC::JSObject& listener, JSC::JSObject& wrapper, bool isAttribute, DOMWrapperWorld&);
@@ -88,6 +90,9 @@ private:
mutable JSC::Weak<JSC::JSObject> m_jsFunction;
mutable JSC::Weak<JSC::JSObject> m_wrapper;
// Optional async context captured when the listener was created
mutable JSC::Weak<JSC::Unknown> m_asyncContext;
Ref<DOMWrapperWorld> m_isolatedWorld;
};