Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement MessagePort and MessageChannel #3336

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

anonrig
Copy link
Member

@anonrig anonrig commented Jan 14, 2025

Work in progress.

Fixes #3273

@anonrig anonrig requested a review from jasnell January 14, 2025 19:33
Copy link

The generated output of @cloudflare/workers-types has been changed by this PR. If this is intentional, run bazel build //types && rm -rf types/generated-snapshot && cp -r bazel-bin/types/definitions types/generated-snapshot to update the snapshot. Alternatively, you can download the full generated types:

Full Type Diff

@jasnell
Copy link
Member

jasnell commented Jan 14, 2025

No idea how all the comments got marked resolved. Very weird.

@anonrig anonrig force-pushed the yagiz/add-message-channel branch from 5c3d7b1 to 34e78d5 Compare January 14, 2025 21:15
src/workerd/api/events.h Outdated Show resolved Hide resolved
kj::Maybe<jsg::HashableV8Ref<v8::Object>> onmessageerror;

// Each MessagePort object can be entangled with another (a symmetric relationship)
kj::Maybe<jsg::Ref<MessagePort>> entangledWith{};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need the visitForGc method implemented in this class also visiting each of these fields (onmessage, onmessageerror, and entagledWith)

@anonrig anonrig force-pushed the yagiz/add-message-channel branch from 34e78d5 to e66571f Compare January 14, 2025 21:37
public:
CloseEvent(): Event("close") {}

CloseEvent(uint code, kj::String reason, bool clean)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth adding code comments here indicating that these constructor variations are specific to websockets.

static jsg::Ref<MessagePort> constructor();

struct StructuredSerializeOptions {
kj::Array<jsg::Object> transfer{};
Copy link
Member

@jasnell jasnell Jan 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

jsg::Object instances must always be held by a jsg::Ref... this should also be an optional argument also.

Suggested change
kj::Array<jsg::Object> transfer{};
jsg::Optional<kj::Array<jsg::Ref<jsg::Object>>> transfer{};


void postMessage(jsg::Lock& js,
jsg::Value message,
kj::OneOf<kj::Maybe<StructuredSerializeOptions>, kj::Array<jsg::Value>> options);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be inverted and made into a jsg::Optional ...

jsg::Optional<kj::OneOf<StructuredSerializeOptions, kj::Array<jsg::Value>> options

~MessagePort() noexcept(false);
KJ_DISALLOW_COPY(MessagePort);

static jsg::Ref<MessagePort> constructor();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the spec, new MessagePort() should fail with an error (it is not user constructible). This should be removed.

Suggested change
static jsg::Ref<MessagePort> constructor();

Comment on lines +58 to +60
MessageChannel::MessageChannel(jsg::Lock &js)
: port1(MessagePort::constructor()),
port2(MessagePort::constructor()) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MessageChannel::MessageChannel(jsg::Lock &js)
: port1(MessagePort::constructor()),
port2(MessagePort::constructor()) {}
MessageChannel::MessageChannel(jsg::Lock &js)
: port1(jsg::alloc<MessagePort>()),
port2(jsg::alloc<MessagePort>()) {}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be entangled with each other immediately after creation, shouldn't they?

Comment on lines +17 to +19
jsg::Ref<MessagePort> MessagePort::constructor() {
return jsg::alloc<MessagePort>();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MessagePort is not user-constructible so shouldn't implement this

Suggested change
jsg::Ref<MessagePort> MessagePort::constructor() {
return jsg::alloc<MessagePort>();
}

void MessagePort::disentangle(jsg::Lock &js) {
KJ_IF_SOME(e, entangledWith) {
// Fire an event named close at otherPort.
e->dispatchEvent(js, CloseEvent::constructor());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general practice, the static constructor is meant to the be implementation of the new T constructor in javascript. For internal construction, use jsg::alloc<T> instead.

Suggested change
e->dispatchEvent(js, CloseEvent::constructor());
e->dispatchEvent(js, jsg::alloc<CloseEvent>());

// If this is entangled, disentangle it.
disentangle(js);
// The close event will be fired even if the port is not explicitly closed.
dispatchEvent(js, CloseEvent::constructor());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
dispatchEvent(js, CloseEvent::constructor());
dispatchEvent(js, jsg::alloc<CloseEvent>());

void close(jsg::Lock& js);

JSG_RESOURCE_TYPE(MessagePort) {
JSG_NESTED_TYPE(EventTarget);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
JSG_NESTED_TYPE(EventTarget);
JSG_INHERIT(EventTarget)

Comment on lines +94 to +95
JSG_READONLY_PROTOTYPE_PROPERTY(port1, getPort1);
JSG_READONLY_PROTOTYPE_PROPERTY(port2, getPort2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the spec require these to be properties on the MessageChannel prototype? If not, using lazy instance properties would be more efficient. Not critical tho.

Suggested change
JSG_READONLY_PROTOTYPE_PROPERTY(port1, getPort1);
JSG_READONLY_PROTOTYPE_PROPERTY(port2, getPort2);
JSG_READONLY_LAZY_INSTANCE_PROPERTY(port1, getPort1);
JSG_READONLY_LAZY_INSTANCE_PROPERTY(port2, getPort2);

// Ref: https://html.spec.whatwg.org/multipage/web-messaging.html#message-channels
class MessageChannel: public jsg::Object {
public:
explicit MessageChannel(jsg::Lock& js);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the jsg::Lock& argument is not used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Runtime APIs: MessageChannel
2 participants