Skip to content

Commit 9f310fd

Browse files
addaleaxjoesepi
authored andcommitted
worker: make MessageEvent class more Web-compatible
PR-URL: nodejs#35496 Fixes: nodejs#35495 Reviewed-By: Rich Trott <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Joyee Cheung <[email protected]> Reviewed-By: Myles Borins <[email protected]> Reviewed-By: Shelley Vohr <[email protected]> Reviewed-By: Michael Dawson <[email protected]> Reviewed-By: Khaidi Chu <[email protected]>
1 parent ed18ba6 commit 9f310fd

File tree

4 files changed

+154
-4
lines changed

4 files changed

+154
-4
lines changed

lib/internal/worker/io.js

+53-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ const {
44
ObjectAssign,
55
ObjectCreate,
66
ObjectDefineProperty,
7+
ObjectDefineProperties,
78
ObjectGetOwnPropertyDescriptors,
89
ObjectGetPrototypeOf,
910
ObjectSetPrototypeOf,
@@ -21,7 +22,8 @@ const {
2122
drainMessagePort,
2223
moveMessagePortToContext,
2324
receiveMessageOnPort: receiveMessageOnPort_,
24-
stopMessagePort
25+
stopMessagePort,
26+
checkMessagePort
2527
} = internalBinding('messaging');
2628
const {
2729
getEnvMessagePort
@@ -38,12 +40,20 @@ const {
3840
kRemoveListener,
3941
} = require('internal/event_target');
4042
const { inspect } = require('internal/util/inspect');
43+
const {
44+
ERR_INVALID_ARG_TYPE
45+
} = require('internal/errors').codes;
4146

47+
const kData = Symbol('kData');
4248
const kIncrementsPortRef = Symbol('kIncrementsPortRef');
49+
const kLastEventId = Symbol('kLastEventId');
4350
const kName = Symbol('kName');
51+
const kOrigin = Symbol('kOrigin');
4452
const kPort = Symbol('kPort');
53+
const kPorts = Symbol('kPorts');
4554
const kWaitingStreams = Symbol('kWaitingStreams');
4655
const kWritableCallbacks = Symbol('kWritableCallbacks');
56+
const kSource = Symbol('kSource');
4757
const kStartedReading = Symbol('kStartedReading');
4858
const kStdioWantsMoreDataCallback = Symbol('kStdioWantsMoreDataCallback');
4959

@@ -72,19 +82,57 @@ ObjectSetPrototypeOf(MessagePort.prototype, NodeEventTarget.prototype);
7282
MessagePort.prototype.ref = MessagePortPrototype.ref;
7383
MessagePort.prototype.unref = MessagePortPrototype.unref;
7484

85+
function validateMessagePort(port, name) {
86+
if (!checkMessagePort(port))
87+
throw new ERR_INVALID_ARG_TYPE(name, 'MessagePort', port);
88+
}
89+
7590
class MessageEvent extends Event {
76-
constructor(data, target, type) {
91+
constructor(type, {
92+
data = null,
93+
origin = '',
94+
lastEventId = '',
95+
source = null,
96+
ports = [],
97+
} = {}) {
7798
super(type);
78-
this.data = data;
99+
this[kData] = data;
100+
this[kOrigin] = `${origin}`;
101+
this[kLastEventId] = `${lastEventId}`;
102+
this[kSource] = source;
103+
this[kPorts] = [...ports];
104+
105+
if (this[kSource] !== null)
106+
validateMessagePort(this[kSource], 'init.source');
107+
for (let i = 0; i < this[kPorts].length; i++)
108+
validateMessagePort(this[kPorts][i], `init.ports[${i}]`);
79109
}
80110
}
81111

112+
ObjectDefineProperties(MessageEvent.prototype, {
113+
data: {
114+
get() { return this[kData]; }, enumerable: true, configurable: true
115+
},
116+
origin: {
117+
get() { return this[kOrigin]; }, enumerable: true, configurable: true
118+
},
119+
lastEventId: {
120+
get() { return this[kLastEventId]; }, enumerable: true, configurable: true
121+
},
122+
source: {
123+
get() { return this[kSource]; }, enumerable: true, configurable: true
124+
},
125+
ports: {
126+
get() { return this[kPorts]; }, enumerable: true, configurable: true
127+
},
128+
});
129+
82130
ObjectDefineProperty(
83131
MessagePort.prototype,
84132
kCreateEvent,
85133
{
86134
value: function(data, type) {
87-
return new MessageEvent(data, this, type);
135+
return new MessageEvent(type, { data });
88136
},
89137
configurable: false,
90138
writable: false,
@@ -283,6 +331,7 @@ module.exports = {
283331
moveMessagePortToContext,
284332
MessagePort,
285333
MessageChannel,
334+
MessageEvent,
286335
receiveMessageOnPort,
287336
setupPortReferencing,
288337
ReadableWorkerStdio,

src/node_messaging.cc

+8
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,12 @@ void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) {
10041004
port->Stop();
10051005
}
10061006

1007+
void MessagePort::CheckType(const FunctionCallbackInfo<Value>& args) {
1008+
Environment* env = Environment::GetCurrent(args);
1009+
args.GetReturnValue().Set(
1010+
GetMessagePortConstructorTemplate(env)->HasInstance(args[0]));
1011+
}
1012+
10071013
void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) {
10081014
MessagePort* port;
10091015
ASSIGN_OR_RETURN_UNWRAP(&port, args[0].As<Object>());
@@ -1339,6 +1345,7 @@ static void InitMessaging(Local<Object> target,
13391345
// These are not methods on the MessagePort prototype, because
13401346
// the browser equivalents do not provide them.
13411347
env->SetMethod(target, "stopMessagePort", MessagePort::Stop);
1348+
env->SetMethod(target, "checkMessagePort", MessagePort::CheckType);
13421349
env->SetMethod(target, "drainMessagePort", MessagePort::Drain);
13431350
env->SetMethod(target, "receiveMessageOnPort", MessagePort::ReceiveMessage);
13441351
env->SetMethod(target, "moveMessagePortToContext",
@@ -1363,6 +1370,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
13631370
registry->Register(MessagePort::PostMessage);
13641371
registry->Register(MessagePort::Start);
13651372
registry->Register(MessagePort::Stop);
1373+
registry->Register(MessagePort::CheckType);
13661374
registry->Register(MessagePort::Drain);
13671375
registry->Register(MessagePort::ReceiveMessage);
13681376
registry->Register(MessagePort::MoveToContext);

src/node_messaging.h

+1
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ class MessagePort : public HandleWrap {
195195
static void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
196196
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
197197
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
198+
static void CheckType(const v8::FunctionCallbackInfo<v8::Value>& args);
198199
static void Drain(const v8::FunctionCallbackInfo<v8::Value>& args);
199200
static void ReceiveMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
200201

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Flags: --expose-internals
2+
'use strict';
3+
require('../common');
4+
const assert = require('assert');
5+
const { MessageEvent, MessageChannel } = require('internal/worker/io');
6+
7+
const dummyPort = new MessageChannel().port1;
8+
9+
{
10+
for (const [ args, expected ] of [
11+
[
12+
['message'],
13+
{
14+
type: 'message', data: null, origin: '',
15+
lastEventId: '', source: null, ports: []
16+
}
17+
],
18+
[
19+
['message', { data: undefined, origin: 'foo' }],
20+
{
21+
type: 'message', data: null, origin: 'foo',
22+
lastEventId: '', source: null, ports: []
23+
}
24+
],
25+
[
26+
['message', { data: 2, origin: 1, lastEventId: 0 }],
27+
{
28+
type: 'message', data: 2, origin: '1',
29+
lastEventId: '0', source: null, ports: []
30+
}
31+
],
32+
[
33+
['message', { lastEventId: 'foo' }],
34+
{
35+
type: 'message', data: null, origin: '',
36+
lastEventId: 'foo', source: null, ports: []
37+
}
38+
],
39+
[
40+
['messageerror', { lastEventId: 'foo', source: dummyPort }],
41+
{
42+
type: 'messageerror', data: null, origin: '',
43+
lastEventId: 'foo', source: dummyPort, ports: []
44+
}
45+
],
46+
[
47+
['message', { ports: [dummyPort], source: null }],
48+
{
49+
type: 'message', data: null, origin: '',
50+
lastEventId: '', source: null, ports: [dummyPort]
51+
}
52+
],
53+
]) {
54+
const ev = new MessageEvent(...args);
55+
const { type, data, origin, lastEventId, source, ports } = ev;
56+
assert.deepStrictEqual(expected, {
57+
type, data, origin, lastEventId, source, ports
58+
});
59+
}
60+
}
61+
62+
{
63+
assert.throws(() => {
64+
new MessageEvent('message', { source: 1 });
65+
}, {
66+
code: 'ERR_INVALID_ARG_TYPE',
67+
message: /The "init\.source" property must be an instance of MessagePort/,
68+
});
69+
assert.throws(() => {
70+
new MessageEvent('message', { source: {} });
71+
}, {
72+
code: 'ERR_INVALID_ARG_TYPE',
73+
message: /The "init\.source" property must be an instance of MessagePort/,
74+
});
75+
assert.throws(() => {
76+
new MessageEvent('message', { ports: 0 });
77+
}, {
78+
message: /ports is not iterable/,
79+
});
80+
assert.throws(() => {
81+
new MessageEvent('message', { ports: [ null ] });
82+
}, {
83+
code: 'ERR_INVALID_ARG_TYPE',
84+
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
85+
});
86+
assert.throws(() => {
87+
new MessageEvent('message', { ports: [ {} ] });
88+
}, {
89+
code: 'ERR_INVALID_ARG_TYPE',
90+
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
91+
});
92+
}

0 commit comments

Comments
 (0)