pass test-worker-message-event.js (#19614)

Co-authored-by: 190n <7763597+190n@users.noreply.github.com>
This commit is contained in:
190n
2025-05-14 18:56:14 -07:00
committed by GitHub
parent 5e8fbf57cb
commit cd8d037c79
14 changed files with 412 additions and 40 deletions

View File

@@ -16,6 +16,7 @@ const errors: ErrorCodeMapping = [
["ABORT_ERR", Error, "AbortError"],
["ERR_ACCESS_DENIED", Error],
["ERR_AMBIGUOUS_ARGUMENT", TypeError],
["ERR_ARG_NOT_ITERABLE", TypeError],
["ERR_ASSERTION", Error],
["ERR_ASYNC_CALLBACK", TypeError],
["ERR_ASYNC_TYPE", TypeError],

View File

@@ -278,9 +278,13 @@ JSC::EncodedJSValue throwConstructorScriptExecutionContextUnavailableError(JSC::
return throwVMError(&lexicalGlobalObject, scope, createReferenceError(&lexicalGlobalObject, makeString(interfaceName, " constructor associated execution context is unavailable"_s)));
}
void throwSequenceTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope)
void throwSequenceTypeError(JSC::JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope, ASCIILiteral functionName, ASCIILiteral argumentName)
{
Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Value is not a sequence"_s);
if (functionName && argumentName) {
Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_ARG_NOT_ITERABLE, makeString(functionName, ": "_s, argumentName, " is not iterable."_s));
} else {
Bun::throwError(&lexicalGlobalObject, scope, Bun::ErrorCode::ERR_INVALID_ARG_TYPE, "Value is not a sequence"_s);
}
}
void throwNonFiniteTypeError(JSGlobalObject& lexicalGlobalObject, JSC::ThrowScope& scope)

View File

@@ -45,7 +45,7 @@ void throwInvalidStateError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral
WEBCORE_EXPORT void throwNonFiniteTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&);
void throwNotSupportedError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral);
void throwSecurityError(JSC::JSGlobalObject&, JSC::ThrowScope&, const String& message);
WEBCORE_EXPORT void throwSequenceTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&);
WEBCORE_EXPORT void throwSequenceTypeError(JSC::JSGlobalObject&, JSC::ThrowScope&, ASCIILiteral functionName = {}, ASCIILiteral argumentName = {});
WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeEnumError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName, ASCIILiteral expectedValues);
WEBCORE_EXPORT JSC::EncodedJSValue throwArgumentMustBeFunctionError(JSC::JSGlobalObject&, JSC::ThrowScope&, unsigned argumentIndex, ASCIILiteral argumentName, ASCIILiteral functionInterfaceName, ASCIILiteral functionName);

View File

@@ -267,7 +267,7 @@ void BroadcastChannel::dispatchMessage(Ref<SerializedScriptValue>&& message)
auto& vm = JSC::getVM(globalObject);
auto scope = DECLARE_CATCH_SCOPE(vm);
Vector<RefPtr<MessagePort>> dummyPorts;
auto event = MessageEvent::create(*globalObject, WTFMove(message), {}, {}, std::nullopt, WTFMove(dummyPorts));
auto event = MessageEvent::create(*globalObject, WTFMove(message), {}, {}, nullptr, WTFMove(dummyPorts));
if (UNLIKELY(scope.exception())) {
// Currently, we assume that the only way we can get here is if we have a termination.
RELEASE_ASSERT(vm.hasPendingTerminationException());

View File

@@ -56,6 +56,8 @@ template<typename T> typename Converter<T>::ReturnType convert(JSC::JSGlobalObje
template<typename T> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, JSC::JSObject&);
template<typename T> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, JSDOMGlobalObject&);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, ExceptionThrower&&);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, ExceptionThrower&&, ASCIILiteral functionName, ASCIILiteral argumentName);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, JSC::JSObject&, ExceptionThrower&&);
template<typename T, typename ExceptionThrower> typename Converter<T>::ReturnType convert(JSC::JSGlobalObject&, JSC::JSValue, JSDOMGlobalObject&, ExceptionThrower&&);
@@ -79,6 +81,11 @@ template<typename T, typename ExceptionThrower> inline typename Converter<T>::Re
return Converter<T>::convert(lexicalGlobalObject, value, std::forward<ExceptionThrower>(exceptionThrower));
}
template<typename T, typename ExceptionThrower> inline typename Converter<T>::ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ExceptionThrower&& exceptionThrower, ASCIILiteral functionName, ASCIILiteral argumentName)
{
return Converter<T>::convert(lexicalGlobalObject, value, std::forward<ExceptionThrower>(exceptionThrower), functionName, argumentName);
}
template<typename T, typename ExceptionThrower> inline typename Converter<T>::ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, JSC::JSObject& thisObject, ExceptionThrower&& exceptionThrower)
{
return Converter<T>::convert(lexicalGlobalObject, value, thisObject, std::forward<ExceptionThrower>(exceptionThrower));

View File

@@ -60,6 +60,21 @@ struct GenericSequenceConverter {
return WTFMove(result);
}
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
ReturnType result;
forEachInIterable(&lexicalGlobalObject, object, [&result, &exceptionThrower](JSC::VM& vm, JSC::JSGlobalObject* lexicalGlobalObject, JSC::JSValue nextValue) {
auto scope = DECLARE_THROW_SCOPE(vm);
auto convertedValue = Converter<IDLType>::convert(*lexicalGlobalObject, nextValue, std::forward<ExceptionThrower>(exceptionThrower));
if (UNLIKELY(scope.exception()))
return;
result.append(WTFMove(convertedValue));
});
return WTFMove(result);
}
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method)
{
return convert(lexicalGlobalObject, object, method, ReturnType());
@@ -240,13 +255,58 @@ struct SequenceConverter {
return result;
}
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value)
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convertArray(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSArray* array, ExceptionThrower&& exceptionThrower = ExceptionThrower())
{
auto& vm = lexicalGlobalObject.vm();
auto scope = DECLARE_THROW_SCOPE(vm);
unsigned length = array->length();
ReturnType result;
if (!result.tryReserveCapacity(length)) {
// FIXME: Is the right exception to throw?
throwTypeError(&lexicalGlobalObject, scope);
return {};
}
JSC::IndexingType indexingType = array->indexingType() & JSC::IndexingShapeMask;
if (indexingType == JSC::ContiguousShape) {
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->butterfly()->contiguous().at(array, i).get();
if (!indexValue)
indexValue = JSC::jsUndefined();
auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, indexValue, std::forward<ExceptionThrower>(exceptionThrower));
RETURN_IF_EXCEPTION(scope, {});
result.append(convertedValue);
}
return result;
}
for (unsigned i = 0; i < length; i++) {
auto indexValue = array->getDirectIndex(&lexicalGlobalObject, i);
RETURN_IF_EXCEPTION(scope, {});
if (!indexValue)
indexValue = JSC::jsUndefined();
auto convertedValue = Converter<IDLType>::convert(lexicalGlobalObject, indexValue, std::forward<ExceptionThrower>(exceptionThrower));
RETURN_IF_EXCEPTION(scope, {});
result.append(convertedValue);
}
return result;
}
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ASCIILiteral functionName = {}, ASCIILiteral argumentName = {})
{
auto& vm = JSC::getVM(&lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
throwSequenceTypeError(lexicalGlobalObject, scope);
throwSequenceTypeError(lexicalGlobalObject, scope, functionName, argumentName);
return {};
}
@@ -264,6 +324,34 @@ struct SequenceConverter {
RELEASE_AND_RETURN(scope, (convertArray(lexicalGlobalObject, array)));
}
template<typename ExceptionThrower = DefaultExceptionThrower>
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject,
JSC::JSValue value,
ExceptionThrower&& exceptionThrower = ExceptionThrower(),
ASCIILiteral functionName = {}, ASCIILiteral argumentName = {})
{
auto& vm = JSC::getVM(&lexicalGlobalObject);
auto scope = DECLARE_THROW_SCOPE(vm);
if (!value.isObject()) {
throwSequenceTypeError(lexicalGlobalObject, scope, functionName, argumentName);
return {};
}
JSC::JSObject* object = JSC::asObject(value);
if (Converter<IDLType>::conversionHasSideEffects)
RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object, std::forward<ExceptionThrower>(exceptionThrower))));
if (!JSC::isJSArray(object))
RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object, std::forward<ExceptionThrower>(exceptionThrower))));
JSC::JSArray* array = JSC::asArray(object);
if (!array->isIteratorProtocolFastAndNonObservable())
RELEASE_AND_RETURN(scope, (GenericConverter::convert(lexicalGlobalObject, object, std::forward<ExceptionThrower>(exceptionThrower))));
RELEASE_AND_RETURN(scope, (convertArray(lexicalGlobalObject, array, std::forward<ExceptionThrower>(exceptionThrower))));
}
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method)
{
if (Converter<IDLType>::conversionHasSideEffects)
@@ -360,15 +448,20 @@ struct SequenceConverter<IDLUnrestrictedDouble> {
template<typename T> struct Converter<IDLSequence<T>> : DefaultConverter<IDLSequence<T>> {
using ReturnType = typename Detail::SequenceConverter<T>::ReturnType;
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value)
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ASCIILiteral functionName = {}, ASCIILiteral argumentName = {})
{
return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, value);
return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, value, functionName, argumentName);
}
static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSObject* object, JSC::JSValue method)
{
return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, object, method);
}
template<typename ExceptionThrower> static ReturnType convert(JSC::JSGlobalObject& lexicalGlobalObject, JSC::JSValue value, ExceptionThrower&& exceptionThrower, ASCIILiteral functionName = {}, ASCIILiteral argumentName = {})
{
return Detail::SequenceConverter<T>::convert(lexicalGlobalObject, value, std::forward<ExceptionThrower>(exceptionThrower), functionName, argumentName);
}
};
template<typename T> struct JSConverter<IDLSequence<T>> {

View File

@@ -56,6 +56,7 @@
#include <wtf/GetPtr.h>
#include <wtf/PointerPreparations.h>
#include <wtf/URL.h>
#include "ErrorCode.h"
namespace WebCore {
using namespace JSC;
@@ -151,7 +152,16 @@ template<> MessageEvent::Init convertDictionary<MessageEvent::Init>(JSGlobalObje
RETURN_IF_EXCEPTION(throwScope, {});
}
if (!portsValue.isUndefined()) {
result.ports = convert<IDLSequence<IDLInterface<MessagePort>>>(lexicalGlobalObject, portsValue);
result.ports = convert<IDLSequence<IDLInterface<MessagePort>>>(
lexicalGlobalObject,
portsValue,
[](JSGlobalObject& lexicalGlobalObject, ThrowScope& throwScope) {
Bun::ERR::INVALID_ARG_TYPE(throwScope,
&lexicalGlobalObject,
"MessageEvent constructor: Expected every item of eventInitDict.ports to be an instance of MessagePort."_s);
},
"MessageEvent constructor"_s,
"eventInitDict.ports"_s);
RETURN_IF_EXCEPTION(throwScope, {});
} else
result.ports = Converter<IDLSequence<IDLInterface<MessagePort>>>::ReturnType {};
@@ -162,11 +172,14 @@ template<> MessageEvent::Init convertDictionary<MessageEvent::Init>(JSGlobalObje
sourceValue = object->get(&lexicalGlobalObject, Identifier::fromString(vm, "source"_s));
RETURN_IF_EXCEPTION(throwScope, {});
}
// if (!sourceValue.isUndefined()) {
// result.source = convert<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(lexicalGlobalObject, sourceValue);
// RETURN_IF_EXCEPTION(throwScope, {});
// } else
result.source = std::nullopt;
if (!sourceValue.isUndefinedOrNull()) {
result.source = convert<IDLNullable<IDLInterface<MessagePort>>>(lexicalGlobalObject, sourceValue, [&sourceValue](JSGlobalObject& lexicalGlobalObject, ThrowScope& throwScope) {
Bun::ERR::INVALID_ARG_TYPE(throwScope, &lexicalGlobalObject, "eventInitDict.source"_s, "MessagePort"_s, sourceValue);
});
RETURN_IF_EXCEPTION(throwScope, {});
} else {
result.source = nullptr;
}
return result;
}
@@ -351,10 +364,10 @@ JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_lastEventId, (JSGlobalObject * lexicalGl
static inline JSValue jsMessageEvent_sourceGetter(JSGlobalObject& lexicalGlobalObject, JSMessageEvent& thisObject)
{
return jsNull();
// auto throwScope = DECLARE_THROW_SCOPE(vm);
// auto& impl = thisObject.wrapped();
// RELEASE_AND_RETURN(throwScope, (toJS<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.source())));
auto& vm = JSC::getVM(&lexicalGlobalObject);
auto throwScope = DECLARE_THROW_SCOPE(vm);
auto& impl = thisObject.wrapped();
RELEASE_AND_RETURN(throwScope, (toJS<IDLNullable<IDLInterface<MessagePort>>>(lexicalGlobalObject, *thisObject.globalObject(), throwScope, impl.source())));
}
JSC_DEFINE_CUSTOM_GETTER(jsMessageEvent_source, (JSGlobalObject * lexicalGlobalObject, JSC::EncodedJSValue thisValue, PropertyName attributeName))
@@ -412,8 +425,7 @@ static inline JSC::EncodedJSValue jsMessageEventPrototypeFunction_initMessageEve
auto lastEventId = argument5.value().isUndefined() ? emptyString() : convert<IDLDOMString>(*lexicalGlobalObject, argument5.value());
RETURN_IF_EXCEPTION(throwScope, {});
EnsureStillAliveScope argument6 = callFrame->argument(6);
auto source = WebCore::MessageEventSource();
// auto source = argument6.value().isUndefined() ? std::nullopt : convert<IDLNullable<IDLUnion<IDLInterface<WindowProxy>, IDLInterface<MessagePort>, IDLInterface<ServiceWorker>>>>(*lexicalGlobalObject, argument6.value());
RefPtr<MessagePort> source = argument6.value().isUndefined() ? nullptr : convert<IDLNullable<IDLInterface<MessagePort>>>(*lexicalGlobalObject, argument6.value());
RETURN_IF_EXCEPTION(throwScope, {});
EnsureStillAliveScope argument7 = callFrame->argument(7);
auto messagePorts = argument7.value().isUndefined() ? Converter<IDLSequence<IDLInterface<MessagePort>>>::ReturnType {} : convert<IDLSequence<IDLInterface<MessagePort>>>(*lexicalGlobalObject, argument7.value());

View File

@@ -54,7 +54,7 @@ inline MessageEvent::MessageEvent(const AtomString& type, Init&& initializer, Is
{
}
inline MessageEvent::MessageEvent(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports)
inline MessageEvent::MessageEvent(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports)
: Event(type, CanBubble::No, IsCancelable::No)
, m_data(WTFMove(data))
, m_origin(origin)
@@ -64,12 +64,12 @@ inline MessageEvent::MessageEvent(const AtomString& type, DataType&& data, const
{
}
Ref<MessageEvent> MessageEvent::create(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports)
Ref<MessageEvent> MessageEvent::create(const AtomString& type, DataType&& data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports)
{
return adoptRef(*new MessageEvent(type, WTFMove(data), origin, lastEventId, WTFMove(source), WTFMove(ports)));
}
Ref<MessageEvent> MessageEvent::create(DataType&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports)
Ref<MessageEvent> MessageEvent::create(DataType&& data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports)
{
return create(eventNames().messageEvent, WTFMove(data), origin, lastEventId, WTFMove(source), WTFMove(ports));
}
@@ -86,12 +86,12 @@ Ref<MessageEvent> MessageEvent::create(const AtomString& type, Init&& initialize
MessageEvent::~MessageEvent() = default;
auto MessageEvent::create(JSC::JSGlobalObject& globalObject, Ref<SerializedScriptValue>&& data, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) -> MessageEventWithStrongData
auto MessageEvent::create(JSC::JSGlobalObject& globalObject, Ref<SerializedScriptValue>&& data, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports) -> MessageEventWithStrongData
{
return create(globalObject, WTFMove(data), {}, {}, WTFMove(source), WTFMove(ports));
}
auto MessageEvent::create(JSC::JSGlobalObject& globalObject, Ref<SerializedScriptValue>&& data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports) -> MessageEventWithStrongData
auto MessageEvent::create(JSC::JSGlobalObject& globalObject, Ref<SerializedScriptValue>&& data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports) -> MessageEventWithStrongData
{
auto& vm = globalObject.vm();
// Locker<JSC::JSLock> locker(vm.apiLock());
@@ -115,7 +115,7 @@ auto MessageEvent::create(JSC::JSGlobalObject& globalObject, Ref<SerializedScrip
return MessageEventWithStrongData { event, WTFMove(strongWrapper) };
}
void MessageEvent::initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&& source, Vector<RefPtr<MessagePort>>&& ports)
void MessageEvent::initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSValue data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&& source, Vector<RefPtr<MessagePort>>&& ports)
{
if (isBeingDispatched())
return;

View File

@@ -39,9 +39,6 @@
namespace WebCore {
class MessageEventSource {
};
class MessageEvent final : public Event {
WTF_MAKE_TZONE_ALLOCATED(MessageEvent);
@@ -50,8 +47,8 @@ public:
};
// using DataType = std::variant<JSValueTag, Ref<SerializedScriptValue>, String, Ref<Blob>, Ref<ArrayBuffer>>;
using DataType = std::variant<JSValueTag, Ref<SerializedScriptValue>, String, Ref<ArrayBuffer>>;
static Ref<MessageEvent> create(const AtomString& type, DataType&&, const String& origin = {}, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {});
static Ref<MessageEvent> create(DataType&&, const String& origin = {}, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {});
static Ref<MessageEvent> create(const AtomString& type, DataType&&, const String& origin = {}, const String& lastEventId = {}, RefPtr<MessagePort>&& = nullptr, Vector<RefPtr<MessagePort>>&& = {});
static Ref<MessageEvent> create(DataType&&, const String& origin = {}, const String& lastEventId = {}, RefPtr<MessagePort>&& = nullptr, Vector<RefPtr<MessagePort>>&& = {});
static Ref<MessageEvent> createForBindings();
@@ -59,7 +56,7 @@ public:
JSC::JSValue data;
String origin;
String lastEventId;
std::optional<MessageEventSource> source;
RefPtr<MessagePort> source;
Vector<RefPtr<MessagePort>> ports;
};
static Ref<MessageEvent> create(const AtomString& type, Init&&, IsTrusted = IsTrusted::No);
@@ -69,17 +66,17 @@ public:
JSC::Strong<JSC::JSObject> strongWrapper; // Keep the wrapper alive until the event is fired, since it is what keeps `data` alive.
};
static MessageEventWithStrongData create(JSC::JSGlobalObject&, Ref<SerializedScriptValue>&&, const String& origin = {}, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {});
static MessageEventWithStrongData create(JSC::JSGlobalObject&, Ref<SerializedScriptValue>&&, const String& origin = {}, const String& lastEventId = {}, RefPtr<MessagePort>&& = nullptr, Vector<RefPtr<MessagePort>>&& = {});
static MessageEventWithStrongData create(JSC::JSGlobalObject&, Ref<SerializedScriptValue>&&, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {});
static MessageEventWithStrongData create(JSC::JSGlobalObject&, Ref<SerializedScriptValue>&&, RefPtr<MessagePort>&& = nullptr, Vector<RefPtr<MessagePort>>&& = {});
virtual ~MessageEvent();
void initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSC::JSValue data, const String& origin, const String& lastEventId, std::optional<MessageEventSource>&&, Vector<RefPtr<MessagePort>>&&);
void initMessageEvent(const AtomString& type, bool canBubble, bool cancelable, JSC::JSValue data, const String& origin, const String& lastEventId, RefPtr<MessagePort>&&, Vector<RefPtr<MessagePort>>&&);
const String& origin() const { return m_origin; }
const String& lastEventId() const { return m_lastEventId; }
const std::optional<MessageEventSource>& source() const { return m_source; }
const RefPtr<MessagePort>& source() const { return m_source; }
const Vector<RefPtr<MessagePort>>& ports() const { return m_ports; }
const DataType& data() const { return m_data; }
@@ -93,14 +90,14 @@ public:
private:
MessageEvent();
MessageEvent(const AtomString& type, Init&&, IsTrusted);
MessageEvent(const AtomString& type, DataType&&, const String& origin, const String& lastEventId = {}, std::optional<MessageEventSource>&& = std::nullopt, Vector<RefPtr<MessagePort>>&& = {});
MessageEvent(const AtomString& type, DataType&&, const String& origin, const String& lastEventId = {}, RefPtr<MessagePort>&& = nullptr, Vector<RefPtr<MessagePort>>&& = {});
EventInterface eventInterface() const final;
DataType m_data;
String m_origin;
String m_lastEventId;
std::optional<MessageEventSource> m_source;
RefPtr<MessagePort> m_source;
Vector<RefPtr<MessagePort>> m_ports;
JSValueInWrappedObject m_jsData;

View File

@@ -252,7 +252,7 @@ ExceptionOr<void> Worker::postMessage(JSC::JSGlobalObject& state, JSC::JSValue m
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(context.jsGlobalObject());
auto ports = MessagePort::entanglePorts(context, WTFMove(message.transferredPorts));
auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), std::nullopt, WTFMove(ports));
auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), nullptr, WTFMove(ports));
globalObject->globalEventScope->dispatchEvent(event.event);
});
@@ -649,7 +649,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionPostMessage,
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(context.jsGlobalObject());
auto ports = MessagePort::entanglePorts(context, WTFMove(message.transferredPorts));
auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), std::nullopt, WTFMove(ports));
auto event = MessageEvent::create(*globalObject, message.message.releaseNonNull(), nullptr, WTFMove(ports));
protectedThis->dispatchEvent(event.event);
});

View File

@@ -0,0 +1,93 @@
'use strict';
require('../common');
const assert = require('assert');
const dummyPort = new MessageChannel().port1;
{
for (const [args, expected] of [
[
['message'],
{
type: 'message', data: null, origin: '',
lastEventId: '', source: null, ports: []
},
],
[
['message', { data: undefined, origin: 'foo' }],
{
type: 'message', data: null, origin: 'foo',
lastEventId: '', source: null, ports: []
},
],
[
['message', { data: 2, origin: 1, lastEventId: 0 }],
{
type: 'message', data: 2, origin: '1',
lastEventId: '0', source: null, ports: []
},
],
[
['message', { lastEventId: 'foo' }],
{
type: 'message', data: null, origin: '',
lastEventId: 'foo', source: null, ports: []
},
],
[
['messageerror', { lastEventId: 'foo', source: dummyPort }],
{
type: 'messageerror', data: null, origin: '',
lastEventId: 'foo', source: dummyPort, ports: []
},
],
[
['message', { ports: [dummyPort], source: null }],
{
type: 'message', data: null, origin: '',
lastEventId: '', source: null, ports: [dummyPort]
},
],
]) {
const ev = new MessageEvent(...args);
const { type, data, origin, lastEventId, source, ports } = ev;
assert.deepStrictEqual(expected, {
type, data, origin, lastEventId, source, ports
});
}
}
{
assert.throws(() => {
new MessageEvent('message', { source: 1 });
}, e => (
e.name == 'TypeError' &&
e.message.includes('eventInitDict.source') && e.message.match(/(an instance of|of type) MessagePort/) && e.message.includes('1')
));
assert.throws(() => {
new MessageEvent('message', { source: {} });
}, e => (
e.name == 'TypeError' &&
e.message.includes('eventInitDict.source') && e.message.match(/(an instance of|of type) MessagePort/) && (e.message.includes('{}') || e.message.includes('an instance of Object'))
));
assert.throws(() => {
new MessageEvent('message', { ports: 0 });
}, {
message: /MessageEvent constructor: eventInitDict\.ports( \(0\))? is not iterable\./,
});
assert.throws(() => {
new MessageEvent('message', { ports: [ null ] });
}, {
name: 'TypeError',
message: /MessageEvent constructor: Expected (every item of )?eventInitDict\.ports(\[0\])? (\("null"\) )?to be an instance of MessagePort\./,
});
assert.throws(() => {
new MessageEvent('message', { ports: [ {} ] });
}, {
name: 'TypeError',
message: /MessageEvent constructor: Expected (every item of )?eventInitDict\.ports(\[0\])? (\("\{\}"\) )?to be an instance of MessagePort\./,
});
}
{
assert(new MessageEvent('message') instanceof Event);
}

View File

@@ -0,0 +1,27 @@
'use strict';
const common = require('../common');
const { parentPort, MessageChannel, Worker } = require('worker_threads');
// Do not use isMainThread so that this test itself can be run inside a Worker.
if (!process.env.HAS_STARTED_WORKER) {
process.env.HAS_STARTED_WORKER = 1;
const w = new Worker(__filename);
w.once('message', common.mustCall(() => {
w.once('message', common.mustNotCall());
setTimeout(() => w.terminate(), 100);
}));
} else {
const { port1 } = new MessageChannel();
parentPort.postMessage('ready');
// Make sure we dont end up running JS after the infinite loop is broken.
port1.postMessage({}, {
// eslint-disable-next-line require-yield
transfer: (function*() { while (true); })()
});
parentPort.postMessage('UNREACHABLE');
process.kill(process.pid, 'SIGINT');
}

View File

@@ -0,0 +1,34 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const { Worker } = require('worker_threads');
// Do not use isMainThread so that this test itself can be run inside a Worker.
if (!process.env.HAS_STARTED_WORKER) {
process.env.HAS_STARTED_WORKER = 1;
const w = new Worker(__filename);
w.on('message', common.mustNotCall());
w.on('error', common.mustCall((err) => {
console.log(err.message);
assert.match(String(err), /^Error: foo$/);
}));
w.on('exit', common.mustCall((code) => {
// uncaughtException is code 1
assert.strictEqual(code, 1);
}));
} else {
// Cannot use common.mustCall as it cannot catch this
let called = false;
process.on('exit', (code) => {
if (!called) {
called = true;
} else {
assert.fail('Exit callback called twice in worker');
}
});
setTimeout(() => assert.fail('Timeout executed after uncaughtException'),
2000);
throw new Error('foo');
}

View File

@@ -0,0 +1,104 @@
import { describe, expect, test } from "bun:test";
describe("MessageEvent constructor", () => {
test("returns an Event instance", () => {
expect(new MessageEvent("message")).toBeInstanceOf(Event);
});
test("has the right defaults", () => {
const expected = {
type: "custom type",
data: null,
origin: "",
lastEventId: "",
source: null,
ports: [],
};
expect(new MessageEvent("custom type")).toMatchObject(expected);
expect(new MessageEvent("custom type", undefined)).toMatchObject(expected);
expect(new MessageEvent("custom type", {})).toMatchObject(expected);
// @ts-expect-error
expect(new MessageEvent("custom type", null)).toMatchObject(expected);
});
test("includes all options in the returned object", () => {
const { port1 } = new MessageChannel();
expect(
new MessageEvent("custom type", {
data: 123,
origin: "origin",
lastEventId: "id",
source: port1,
ports: [port1],
}),
).toMatchObject({
type: "custom type",
data: 123,
origin: "origin",
lastEventId: "id",
source: port1,
ports: [port1],
});
});
test("coerces the type to a string", () => {
// @ts-expect-error
expect(new MessageEvent(5)).toMatchObject({ type: "5" });
// @ts-expect-error
expect(new MessageEvent(undefined)).toMatchObject({ type: "undefined" });
});
test("throws if you pass no arguments", () => {
// @ts-expect-error
expect(() => new MessageEvent()).toThrow({
name: "TypeError",
message: "Not enough arguments",
});
});
test("throws if options is not an object", () => {
// @ts-expect-error
expect(() => new MessageEvent("message", 5)).toThrow(TypeError);
});
test("coerces options.origin to a string", () => {
// @ts-expect-error
expect(new MessageEvent("message", { origin: 123 })).toMatchObject({ origin: "123" });
});
test("coerces options.lastEventId to a string", () => {
// @ts-expect-error
expect(new MessageEvent("message", { lastEventId: 123 })).toMatchObject({ lastEventId: "123" });
});
test("throws if options.source is the wrong type", () => {
// @ts-expect-error
expect(() => new MessageEvent("message", { source: 1 })).toThrow({
name: "TypeError",
message: 'The "eventInitDict.source" property must be of type MessagePort. Received type number (1)',
});
// @ts-expect-error
expect(() => new MessageEvent("message", { source: {} })).toThrow({
name: "TypeError",
message: 'The "eventInitDict.source" property must be of type MessagePort. Received an instance of Object',
});
});
test("throws if options.ports is the wrong type", () => {
// @ts-expect-error
expect(() => new MessageEvent("message", { ports: 1 })).toThrow({
name: "TypeError",
message: "MessageEvent constructor: eventInitDict.ports is not iterable.",
});
// @ts-expect-error
expect(() => new MessageEvent("message", { ports: [1] })).toThrow({
name: "TypeError",
message: "MessageEvent constructor: Expected every item of eventInitDict.ports to be an instance of MessagePort.",
});
// @ts-expect-error
expect(() => new MessageEvent("message", { ports: [{}] })).toThrow({
name: "TypeError",
message: "MessageEvent constructor: Expected every item of eventInitDict.ports to be an instance of MessagePort.",
});
});
});