Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
worker: make MessageEvent class more Web-compatible
PR-URL: #35496
Fixes: #35495
Reviewed-By: Rich Trott <rtrott@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Myles Borins <myles.borins@gmail.com>
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
Reviewed-By: Michael Dawson <midawson@redhat.com>
Reviewed-By: Khaidi Chu <i@2333.moe>
  • Loading branch information
addaleax authored and nodejs-github-bot committed Oct 11, 2020
1 parent 828518c commit 705d888
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 4 deletions.
57 changes: 53 additions & 4 deletions lib/internal/worker/io.js
Expand Up @@ -4,6 +4,7 @@ const {
ObjectAssign,
ObjectCreate,
ObjectDefineProperty,
ObjectDefineProperties,
ObjectGetOwnPropertyDescriptors,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
Expand All @@ -21,7 +22,8 @@ const {
drainMessagePort,
moveMessagePortToContext,
receiveMessageOnPort: receiveMessageOnPort_,
stopMessagePort
stopMessagePort,
checkMessagePort
} = internalBinding('messaging');
const {
getEnvMessagePort
Expand All @@ -38,12 +40,20 @@ const {
kRemoveListener,
} = require('internal/event_target');
const { inspect } = require('internal/util/inspect');
const {
ERR_INVALID_ARG_TYPE
} = require('internal/errors').codes;

const kData = Symbol('kData');
const kIncrementsPortRef = Symbol('kIncrementsPortRef');
const kLastEventId = Symbol('kLastEventId');
const kName = Symbol('kName');
const kOrigin = Symbol('kOrigin');
const kPort = Symbol('kPort');
const kPorts = Symbol('kPorts');
const kWaitingStreams = Symbol('kWaitingStreams');
const kWritableCallbacks = Symbol('kWritableCallbacks');
const kSource = Symbol('kSource');
const kStartedReading = Symbol('kStartedReading');
const kStdioWantsMoreDataCallback = Symbol('kStdioWantsMoreDataCallback');

Expand Down Expand Up @@ -72,19 +82,57 @@ ObjectSetPrototypeOf(MessagePort.prototype, NodeEventTarget.prototype);
MessagePort.prototype.ref = MessagePortPrototype.ref;
MessagePort.prototype.unref = MessagePortPrototype.unref;

function validateMessagePort(port, name) {
if (!checkMessagePort(port))
throw new ERR_INVALID_ARG_TYPE(name, 'MessagePort', port);
}

class MessageEvent extends Event {
constructor(data, target, type) {
constructor(type, {
data = null,
origin = '',
lastEventId = '',
source = null,
ports = [],
} = {}) {
super(type);
this.data = data;
this[kData] = data;
this[kOrigin] = `${origin}`;
this[kLastEventId] = `${lastEventId}`;
this[kSource] = source;
this[kPorts] = [...ports];

if (this[kSource] !== null)
validateMessagePort(this[kSource], 'init.source');
for (let i = 0; i < this[kPorts].length; i++)
validateMessagePort(this[kPorts][i], `init.ports[${i}]`);
}
}

ObjectDefineProperties(MessageEvent.prototype, {
data: {
get() { return this[kData]; }, enumerable: true, configurable: true
},
origin: {
get() { return this[kOrigin]; }, enumerable: true, configurable: true
},
lastEventId: {
get() { return this[kLastEventId]; }, enumerable: true, configurable: true
},
source: {
get() { return this[kSource]; }, enumerable: true, configurable: true
},
ports: {
get() { return this[kPorts]; }, enumerable: true, configurable: true
},
});

ObjectDefineProperty(
MessagePort.prototype,
kCreateEvent,
{
value: function(data, type) {
return new MessageEvent(data, this, type);
return new MessageEvent(type, { data });
},
configurable: false,
writable: false,
Expand Down Expand Up @@ -283,6 +331,7 @@ module.exports = {
moveMessagePortToContext,
MessagePort,
MessageChannel,
MessageEvent,
receiveMessageOnPort,
setupPortReferencing,
ReadableWorkerStdio,
Expand Down
8 changes: 8 additions & 0 deletions src/node_messaging.cc
Expand Up @@ -1004,6 +1004,12 @@ void MessagePort::Stop(const FunctionCallbackInfo<Value>& args) {
port->Stop();
}

void MessagePort::CheckType(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
args.GetReturnValue().Set(
GetMessagePortConstructorTemplate(env)->HasInstance(args[0]));
}

void MessagePort::Drain(const FunctionCallbackInfo<Value>& args) {
MessagePort* port;
ASSIGN_OR_RETURN_UNWRAP(&port, args[0].As<Object>());
Expand Down Expand Up @@ -1339,6 +1345,7 @@ static void InitMessaging(Local<Object> target,
// These are not methods on the MessagePort prototype, because
// the browser equivalents do not provide them.
env->SetMethod(target, "stopMessagePort", MessagePort::Stop);
env->SetMethod(target, "checkMessagePort", MessagePort::CheckType);
env->SetMethod(target, "drainMessagePort", MessagePort::Drain);
env->SetMethod(target, "receiveMessageOnPort", MessagePort::ReceiveMessage);
env->SetMethod(target, "moveMessagePortToContext",
Expand All @@ -1363,6 +1370,7 @@ static void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(MessagePort::PostMessage);
registry->Register(MessagePort::Start);
registry->Register(MessagePort::Stop);
registry->Register(MessagePort::CheckType);
registry->Register(MessagePort::Drain);
registry->Register(MessagePort::ReceiveMessage);
registry->Register(MessagePort::MoveToContext);
Expand Down
1 change: 1 addition & 0 deletions src/node_messaging.h
Expand Up @@ -195,6 +195,7 @@ class MessagePort : public HandleWrap {
static void PostMessage(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Start(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Stop(const v8::FunctionCallbackInfo<v8::Value>& args);
static void CheckType(const v8::FunctionCallbackInfo<v8::Value>& args);
static void Drain(const v8::FunctionCallbackInfo<v8::Value>& args);
static void ReceiveMessage(const v8::FunctionCallbackInfo<v8::Value>& args);

Expand Down
92 changes: 92 additions & 0 deletions test/parallel/test-worker-message-event.js
@@ -0,0 +1,92 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const { MessageEvent, MessageChannel } = require('internal/worker/io');

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 });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "init\.source" property must be an instance of MessagePort/,
});
assert.throws(() => {
new MessageEvent('message', { source: {} });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "init\.source" property must be an instance of MessagePort/,
});
assert.throws(() => {
new MessageEvent('message', { ports: 0 });
}, {
message: /ports is not iterable/,
});
assert.throws(() => {
new MessageEvent('message', { ports: [ null ] });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
});
assert.throws(() => {
new MessageEvent('message', { ports: [ {} ] });
}, {
code: 'ERR_INVALID_ARG_TYPE',
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
});
}

0 comments on commit 705d888

Please sign in to comment.