Skip to content

Commit 342fe23

Browse files
pfgithubJarred-Sumnercirospaciari
authored
test-eventtarget.js (#19547)
Co-authored-by: Jarred Sumner <jarred@jarredsumner.com> Co-authored-by: Ciro Spaciari <ciro.spaciari@gmail.com>
1 parent 89c5e40 commit 342fe23

File tree

11 files changed

+889
-21
lines changed

11 files changed

+889
-21
lines changed

.vscode/launch.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/bun.js/bindings/BunProcess.cpp

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,12 +1390,40 @@ extern "C" void Bun__Process__emitWarning(Zig::GlobalObject* globalObject, Encod
13901390
JSValue::decode(ctor));
13911391
}
13921392

1393-
JSValue Process::emitWarning(JSC::JSGlobalObject* lexicalGlobalObject, JSValue warning, JSValue type, JSValue code, JSValue ctor)
1393+
JSValue Process::emitWarningErrorInstance(JSC::JSGlobalObject* lexicalGlobalObject, JSValue errorInstance)
13941394
{
13951395
Zig::GlobalObject* globalObject = defaultGlobalObject(lexicalGlobalObject);
13961396
VM& vm = getVM(globalObject);
13971397
auto scope = DECLARE_THROW_SCOPE(vm);
13981398
auto* process = jsCast<Process*>(globalObject->processObject());
1399+
1400+
auto warningName = errorInstance.get(lexicalGlobalObject, vm.propertyNames->name);
1401+
RETURN_IF_EXCEPTION(scope, {});
1402+
if (isJSValueEqualToASCIILiteral(globalObject, warningName, "DeprecationWarning"_s)) {
1403+
if (Bun__Node__ProcessNoDeprecation) {
1404+
return jsUndefined();
1405+
}
1406+
if (Bun__Node__ProcessThrowDeprecation) {
1407+
// // Delay throwing the error to guarantee that all former warnings were properly logged.
1408+
// return process.nextTick(() => {
1409+
// throw warning;
1410+
// });
1411+
auto func = JSFunction::create(vm, globalObject, 1, ""_s, jsFunction_throwValue, JSC::ImplementationVisibility::Private);
1412+
process->queueNextTick(globalObject, func, errorInstance);
1413+
return jsUndefined();
1414+
}
1415+
}
1416+
1417+
// process.nextTick(doEmitWarning, warning);
1418+
auto func = JSFunction::create(vm, globalObject, 1, ""_s, jsFunction_emitWarning, JSC::ImplementationVisibility::Private);
1419+
process->queueNextTick(globalObject, func, errorInstance);
1420+
return jsUndefined();
1421+
}
1422+
JSValue Process::emitWarning(JSC::JSGlobalObject* lexicalGlobalObject, JSValue warning, JSValue type, JSValue code, JSValue ctor)
1423+
{
1424+
Zig::GlobalObject* globalObject = defaultGlobalObject(lexicalGlobalObject);
1425+
VM& vm = getVM(globalObject);
1426+
auto scope = DECLARE_THROW_SCOPE(vm);
13991427
JSValue detail = jsUndefined();
14001428

14011429
if (Bun__Node__ProcessNoDeprecation && isJSValueEqualToASCIILiteral(globalObject, type, "DeprecationWarning"_s)) {
@@ -1453,25 +1481,7 @@ JSValue Process::emitWarning(JSC::JSGlobalObject* lexicalGlobalObject, JSValue w
14531481
if (!detail.isUndefined()) errorInstance->putDirect(vm, vm.propertyNames->detail, detail, JSC::PropertyAttribute::DontEnum | 0);
14541482
// ErrorCaptureStackTrace(warning, ctor || process.emitWarning);
14551483

1456-
if (isJSValueEqualToASCIILiteral(globalObject, type, "DeprecationWarning"_s)) {
1457-
if (Bun__Node__ProcessNoDeprecation) {
1458-
return jsUndefined();
1459-
}
1460-
if (Bun__Node__ProcessThrowDeprecation) {
1461-
// // Delay throwing the error to guarantee that all former warnings were properly logged.
1462-
// return process.nextTick(() => {
1463-
// throw warning;
1464-
// });
1465-
auto func = JSFunction::create(vm, globalObject, 1, ""_s, jsFunction_throwValue, JSC::ImplementationVisibility::Private);
1466-
process->queueNextTick(globalObject, func, errorInstance);
1467-
return jsUndefined();
1468-
}
1469-
}
1470-
1471-
// process.nextTick(doEmitWarning, warning);
1472-
auto func = JSFunction::create(vm, globalObject, 1, ""_s, jsFunction_emitWarning, JSC::ImplementationVisibility::Private);
1473-
process->queueNextTick(globalObject, func, errorInstance);
1474-
return jsUndefined();
1484+
RELEASE_AND_RETURN(scope, emitWarningErrorInstance(lexicalGlobalObject, errorInstance));
14751485
}
14761486

14771487
JSC_DEFINE_HOST_FUNCTION(Process_emitWarning, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))

src/bun.js/bindings/BunProcess.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class Process : public WebCore::JSEventEmitter {
6565
// This is equivalent to `process.nextTick(() => process.emit(eventName, event))` from JavaScript.
6666
void emitOnNextTick(Zig::GlobalObject* globalObject, ASCIILiteral eventName, JSValue event);
6767

68+
static JSValue emitWarningErrorInstance(JSC::JSGlobalObject* lexicalGlobalObject, JSValue errorInstance);
6869
static JSValue emitWarning(JSC::JSGlobalObject* lexicalGlobalObject, JSValue warning, JSValue type, JSValue code, JSValue ctor);
6970

7071
JSString* cachedCwd() { return m_cachedCwd.get(); }

src/bun.js/bindings/ErrorCode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ const errors: ErrorCodeMapping = [
7070
["ERR_DNS_SET_SERVERS_FAILED", Error],
7171
["ERR_ENCODING_INVALID_ENCODED_DATA", TypeError],
7272
["ERR_ENCODING_NOT_SUPPORTED", RangeError],
73+
["ERR_EVENT_RECURSION", Error],
7374
["ERR_EXECUTION_ENVIRONMENT_NOT_AVAILABLE", Error],
7475
["ERR_FORMDATA_PARSE_ERROR", TypeError],
7576
["ERR_FS_CP_DIR_TO_NON_DIR", Error],

src/bun.js/bindings/ExceptionCode.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,11 @@ enum ExceptionCode : uint8_t {
7373
// Used to indicate to the bindings that a JS exception was thrown below and it should be propagated.
7474
ExistingExceptionError,
7575

76+
// Node errors
7677
InvalidThisError,
7778
InvalidURLError,
7879
CryptoOperationFailedError,
80+
EVENT_RECURSION,
7981
};
8082

8183
} // namespace WebCore

src/bun.js/bindings/JSDOMExceptionHandling.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ JSValue createDOMException(JSGlobalObject* lexicalGlobalObject, ExceptionCode ec
185185
case ExceptionCode::CryptoOperationFailedError:
186186
return Bun::createError(lexicalGlobalObject, Bun::ErrorCode::ERR_CRYPTO_OPERATION_FAILED, message.isEmpty() ? "Crypto operation failed"_s : message);
187187

188+
case ExceptionCode::EVENT_RECURSION:
189+
return Bun::createError(lexicalGlobalObject, Bun::ErrorCode::ERR_EVENT_RECURSION, message);
190+
188191
default: {
189192
// FIXME: All callers to createDOMException need to pass in the correct global object.
190193
// For now, we're going to assume the lexicalGlobalObject. Which is wrong in cases like this:

src/bun.js/bindings/webcore/EventTarget.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include <wtf/SetForScope.h>
5757
#include <wtf/StdLibExtras.h>
5858
#include <wtf/Vector.h>
59+
#include "ErrorCode.h"
5960

6061
namespace WebCore {
6162

@@ -231,9 +232,13 @@ bool EventTarget::hasActiveEventListeners(const AtomString& eventType) const
231232

232233
ExceptionOr<bool> EventTarget::dispatchEventForBindings(Event& event)
233234
{
234-
if (!event.isInitialized() || event.isBeingDispatched())
235+
if (!event.isInitialized())
235236
return Exception { InvalidStateError };
236237

238+
if (event.isBeingDispatched()) {
239+
return Exception { EVENT_RECURSION, makeString("The event \""_s, event.type(), "\" is already being dispatched"_s) };
240+
}
241+
237242
if (!scriptExecutionContext())
238243
return false;
239244

src/bun.js/bindings/webcore/JSEvent.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@
5555
#include <wtf/GetPtr.h>
5656
#include <wtf/PointerPreparations.h>
5757
#include <wtf/URL.h>
58+
#include "ErrorCode.h"
59+
#include "NodeValidator.h"
5860

5961
namespace WebCore {
6062
using namespace JSC;
@@ -158,6 +160,10 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSEventDOMConstructor::c
158160
auto type = convert<IDLAtomStringAdaptor<IDLDOMString>>(*lexicalGlobalObject, argument0.value());
159161
RETURN_IF_EXCEPTION(throwScope, {});
160162
EnsureStillAliveScope argument1 = callFrame->argument(1);
163+
if (!argument1.value().isUndefinedOrNull() && !argument1.value().isObject() && !argument1.value().isCallable()) {
164+
Bun::ERR::INVALID_ARG_TYPE(throwScope, lexicalGlobalObject, "options"_s, "object"_s, argument1.value());
165+
}
166+
RETURN_IF_EXCEPTION(throwScope, {});
161167
auto eventInitDict = convert<IDLDictionary<EventInit>>(*lexicalGlobalObject, argument1.value());
162168
RETURN_IF_EXCEPTION(throwScope, {});
163169
auto object = Event::create(WTFMove(type), WTFMove(eventInitDict));

src/bun.js/bindings/webcore/JSEventListener.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "config.h"
2121
#include "JSEventListener.h"
2222

23+
#include "BunProcess.h"
2324
// #include "BeforeUnloadEvent.h"
2425
// #include "ContentSecurityPolicy.h"
2526
#include "EventNames.h"
@@ -123,6 +124,22 @@ void JSEventListener::visitJSFunction(SlotVisitor& visitor) { visitJSFunctionImp
123124
// event.setReturnValue(returnValue);
124125
// }
125126

127+
JSC_DEFINE_HOST_FUNCTION(jsFunctionEmitUncaughtException, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
128+
{
129+
auto exception = callFrame->argument(0);
130+
reportException(lexicalGlobalObject, exception);
131+
return JSValue::encode(JSC::jsUndefined());
132+
}
133+
JSC_DEFINE_HOST_FUNCTION(jsFunctionEmitUncaughtExceptionNextTick, (JSC::JSGlobalObject * lexicalGlobalObject, JSC::CallFrame* callFrame))
134+
{
135+
Zig::GlobalObject* globalObject = defaultGlobalObject(lexicalGlobalObject);
136+
Bun::Process* process = jsCast<Bun::Process*>(globalObject->processObject());
137+
auto exception = callFrame->argument(0);
138+
auto func = JSFunction::create(globalObject->vm(), globalObject, 1, String(), jsFunctionEmitUncaughtException, JSC::ImplementationVisibility::Private);
139+
process->queueNextTick(lexicalGlobalObject, func, exception);
140+
return JSC::JSValue::encode(JSC::jsUndefined());
141+
}
142+
126143
void JSEventListener::handleEvent(ScriptExecutionContext& scriptExecutionContext, Event& event)
127144
{
128145
if (scriptExecutionContext.isJSExecutionForbidden())
@@ -234,6 +251,32 @@ void JSEventListener::handleEvent(ScriptExecutionContext& scriptExecutionContext
234251
if (handleExceptionIfNeeded(uncaughtException))
235252
return;
236253

254+
// Node handles promises in the return value and throws an uncaught exception on nextTick if it rejects.
255+
// See event_target.js function addCatch in node
256+
if (retval.isObject()) {
257+
auto then = retval.get(lexicalGlobalObject, vm.propertyNames->then);
258+
if (UNLIKELY(scope.exception())) {
259+
auto* exception = scope.exception();
260+
scope.clearException();
261+
event.target()->uncaughtExceptionInEventHandler();
262+
reportException(lexicalGlobalObject, exception);
263+
return;
264+
}
265+
if (then.isCallable()) {
266+
MarkedArgumentBuffer arglist;
267+
arglist.append(JSValue(JSC::jsUndefined()));
268+
arglist.append(JSValue(JSC::JSFunction::create(vm, lexicalGlobalObject, 1, String(), jsFunctionEmitUncaughtExceptionNextTick, ImplementationVisibility::Public, NoIntrinsic))); // err => process.nextTick(() => throw err)
269+
JSC::call(lexicalGlobalObject, then, retval, arglist, "Promise.then is not callable"_s);
270+
if (UNLIKELY(scope.exception())) {
271+
auto* exception = scope.exception();
272+
scope.clearException();
273+
event.target()->uncaughtExceptionInEventHandler();
274+
reportException(lexicalGlobalObject, exception);
275+
return;
276+
}
277+
}
278+
}
279+
237280
if (!m_isAttribute) {
238281
// This is an EventListener and there is therefore no need for any return value handling.
239282
return;

src/bun.js/bindings/webcore/JSEventTarget.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <wtf/GetPtr.h>
5656
#include <wtf/PointerPreparations.h>
5757
#include <wtf/URL.h>
58+
#include "BunProcess.h"
5859

5960
namespace WebCore {
6061
using namespace JSC;
@@ -223,6 +224,23 @@ static inline JSC::EncodedJSValue jsEventTargetPrototypeFunction_addEventListene
223224
EnsureStillAliveScope argument2 = callFrame->argument(2);
224225
auto options = argument2.value().isUndefined() ? false : convert<IDLUnion<IDLDictionary<AddEventListenerOptions>, IDLBoolean>>(*lexicalGlobalObject, argument2.value());
225226
RETURN_IF_EXCEPTION(throwScope, {});
227+
// Emit a warning if listener is null, as it has no effect
228+
if (!listener) {
229+
String warningMessage;
230+
if (argument1.value().isNull()) {
231+
warningMessage = "addEventListener called with null listener, which has no effect."_s;
232+
} else {
233+
warningMessage = "addEventListener called with undefined listener, which has no effect."_s;
234+
}
235+
auto errorInstance = JSC::ErrorInstance::create(vm, lexicalGlobalObject->errorStructure(JSC::ErrorType::Error), warningMessage, JSValue(), nullptr, RuntimeType::TypeNothing, JSC::ErrorType::Error);
236+
errorInstance->putDirect(vm, vm.propertyNames->name, jsString(vm, String("AddEventListenerArgumentTypeWarning"_s)));
237+
JSObject& target = *castedThis;
238+
errorInstance->putDirect(vm, vm.propertyNames->target, &target);
239+
RETURN_IF_EXCEPTION(throwScope, {});
240+
errorInstance->putDirect(vm, vm.propertyNames->type, jsString(vm, WTFMove(type)));
241+
Bun::Process::emitWarningErrorInstance(lexicalGlobalObject, errorInstance);
242+
RETURN_IF_EXCEPTION(throwScope, {});
243+
}
226244
auto result = JSValue::encode(toJS<IDLUndefined>(*lexicalGlobalObject, throwScope, [&]() -> decltype(auto) { return impl.addEventListenerForBindings(WTFMove(type), WTFMove(listener), WTFMove(options)); }));
227245
RETURN_IF_EXCEPTION(throwScope, {});
228246
vm.writeBarrier(&static_cast<JSObject&>(*castedThis), argument1.value());

0 commit comments

Comments
 (0)