Skip to content

Commit 77a944c

Browse files
addaleaxrvagg
authored andcommitted
worker: use fake MessageEvent for port.onmessage
Instead of passing the payload for Workers directly to `.onmessage`, perform something more similar to what the browser API provides, namely create an event object with a `.data` property. This does not make `MessagePort` implement the `EventTarget` API, nor does it implement the full `MessageEvent` API, but it would make such extensions non-breaking changes if we desire them at some point in the future. (This would be a breaking change if Workers were not experimental. Currently, this method is also undocumented and only exists with the idea of enabling some degree of Web compatibility.) PR-URL: #26082 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Denys Otrishko <[email protected]>
1 parent f408d78 commit 77a944c

6 files changed

+34
-13
lines changed

lib/internal/worker/io.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,11 @@ MessagePort.prototype.unref = MessagePortPrototype.unref;
6161
// uv_async_t) which can receive information from other threads and emits
6262
// .onmessage events, and a function used for sending data to a MessagePort
6363
// in some other thread.
64-
MessagePort.prototype[kOnMessageListener] = function onmessage(payload) {
65-
if (payload.type !== messageTypes.STDIO_WANTS_MORE_DATA)
66-
debug(`[${threadId}] received message`, payload);
64+
MessagePort.prototype[kOnMessageListener] = function onmessage(event) {
65+
if (event.data && event.data.type !== messageTypes.STDIO_WANTS_MORE_DATA)
66+
debug(`[${threadId}] received message`, event);
6767
// Emit the deserialized object to userland.
68-
this.emit('message', payload);
68+
this.emit('message', event.data);
6969
};
7070

7171
// This is for compatibility with the Web's MessagePort API. It makes sense to

src/env.h

+3
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
146146
V(crypto_ec_string, "ec") \
147147
V(crypto_rsa_string, "rsa") \
148148
V(cwd_string, "cwd") \
149+
V(data_string, "data") \
149150
V(dest_string, "dest") \
150151
V(destroyed_string, "destroyed") \
151152
V(detached_string, "detached") \
@@ -291,6 +292,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
291292
V(subject_string, "subject") \
292293
V(subjectaltname_string, "subjectaltname") \
293294
V(syscall_string, "syscall") \
295+
V(target_string, "target") \
294296
V(thread_id_string, "threadId") \
295297
V(ticketkeycallback_string, "onticketkeycallback") \
296298
V(timeout_string, "timeout") \
@@ -359,6 +361,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
359361
V(inspector_console_extension_installer, v8::Function) \
360362
V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \
361363
V(message_port, v8::Object) \
364+
V(message_event_object_template, v8::ObjectTemplate) \
362365
V(message_port_constructor_template, v8::FunctionTemplate) \
363366
V(native_module_require, v8::Function) \
364367
V(performance_entry_callback, v8::Function) \

src/node_messaging.cc

+22-5
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ using v8::Maybe;
2525
using v8::MaybeLocal;
2626
using v8::Nothing;
2727
using v8::Object;
28+
using v8::ObjectTemplate;
2829
using v8::SharedArrayBuffer;
2930
using v8::String;
3031
using v8::Value;
@@ -589,12 +590,19 @@ void MessagePort::OnMessage() {
589590
// Call the JS .onmessage() callback.
590591
HandleScope handle_scope(env()->isolate());
591592
Context::Scope context_scope(context);
592-
Local<Value> args[] = {
593-
received.Deserialize(env(), context).FromMaybe(Local<Value>())
594-
};
595593

596-
if (args[0].IsEmpty() ||
597-
MakeCallback(env()->onmessage_string(), 1, args).IsEmpty()) {
594+
Local<Object> event;
595+
Local<Value> payload;
596+
Local<Value> cb_args[1];
597+
if (!received.Deserialize(env(), context).ToLocal(&payload) ||
598+
!env()->message_event_object_template()->NewInstance(context)
599+
.ToLocal(&event) ||
600+
event->Set(context, env()->data_string(), payload).IsNothing() ||
601+
event->Set(context, env()->target_string(), object()).IsNothing() ||
602+
(cb_args[0] = event, false) ||
603+
MakeCallback(env()->onmessage_string(),
604+
arraysize(cb_args),
605+
cb_args).IsEmpty()) {
598606
// Re-schedule OnMessage() execution in case of failure.
599607
if (data_)
600608
TriggerAsync();
@@ -763,6 +771,8 @@ MaybeLocal<Function> GetMessagePortConstructor(
763771
if (!templ.IsEmpty())
764772
return templ->GetFunction(context);
765773

774+
Isolate* isolate = env->isolate();
775+
766776
{
767777
Local<FunctionTemplate> m = env->NewFunctionTemplate(MessagePort::New);
768778
m->SetClassName(env->message_port_constructor_string());
@@ -775,6 +785,13 @@ MaybeLocal<Function> GetMessagePortConstructor(
775785
env->SetProtoMethod(m, "drain", MessagePort::Drain);
776786

777787
env->set_message_port_constructor_template(m);
788+
789+
Local<FunctionTemplate> event_ctor = FunctionTemplate::New(isolate);
790+
event_ctor->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "MessageEvent"));
791+
Local<ObjectTemplate> e = event_ctor->InstanceTemplate();
792+
e->Set(env->data_string(), Null(isolate));
793+
e->Set(env->target_string(), Null(isolate));
794+
env->set_message_event_object_template(e);
778795
}
779796

780797
return GetMessagePortConstructor(env, context);

test/parallel/test-worker-message-port-transfer-self.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ assert.throws(common.mustCall(() => {
2525

2626
// The failed transfer should not affect the ports in anyway.
2727
port2.onmessage = common.mustCall((message) => {
28-
assert.strictEqual(message, 2);
28+
assert.strictEqual(message.data, 2);
2929

3030
const inspectedPort1 = util.inspect(port1);
3131
const inspectedPort2 = util.inspect(port2);

test/parallel/test-worker-message-port.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@ const { MessageChannel, MessagePort } = require('worker_threads');
2121
const { port1, port2 } = new MessageChannel();
2222

2323
port1.onmessage = common.mustCall((message) => {
24-
assert.strictEqual(message, 4);
24+
assert.strictEqual(message.data, 4);
25+
assert.strictEqual(message.target, port1);
2526
port2.close(common.mustCall());
2627
});
2728

2829
port1.postMessage(2);
2930

3031
port2.onmessage = common.mustCall((message) => {
31-
port2.postMessage(message * 2);
32+
port2.postMessage(message.data * 2);
3233
});
3334
}
3435

test/parallel/test-worker-onmessage.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,6 @@ if (!process.env.HAS_STARTED_WORKER) {
1414
w.postMessage(2);
1515
} else {
1616
parentPort.onmessage = common.mustCall((message) => {
17-
parentPort.postMessage(message * 2);
17+
parentPort.postMessage(message.data * 2);
1818
});
1919
}

0 commit comments

Comments
 (0)