mirror of
https://github.com/nodejs/node.git
synced 2025-04-29 22:40:57 +00:00
lib: replace MessageEvent with undici's
undici's MessageEvent is better tested and has a complete WebIDL implementation for validation. Not only this, but it's also used in Node's current WebSocket implementation. There are a large number of webidl-related issues in the current MessageEvent, such as not implementing `MessageEvent.prototype.initMessageEvent`, not validating arguments passed to its constructor (https://github.com/nodejs/node/pull/51771), not validating the values passed to the constructor (such as not validating that `ports` is a sequence, not converting origin to a USVString, etc.), and other issues. fixup PR-URL: https://github.com/nodejs/node/pull/52370 Reviewed-By: Yagiz Nizipli <yagiz.nizipli@sentry.io> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com>
This commit is contained in:
parent
7c3dce0e4f
commit
3136fb0c28
@ -67,7 +67,7 @@ rules:
|
|||||||
- name: MessageChannel
|
- name: MessageChannel
|
||||||
message: Use `const { MessageChannel } = require('internal/worker/io');` instead of the global.
|
message: Use `const { MessageChannel } = require('internal/worker/io');` instead of the global.
|
||||||
- name: MessageEvent
|
- name: MessageEvent
|
||||||
message: Use `const { MessageEvent } = require('internal/worker/io');` instead of the global.
|
message: Use `const { MessageEvent } = require('internal/deps/undici/undici');` instead of the global.
|
||||||
- name: MessagePort
|
- name: MessagePort
|
||||||
message: Use `const { MessagePort } = require('internal/worker/io');` instead of the global.
|
message: Use `const { MessagePort } = require('internal/worker/io');` instead of the global.
|
||||||
- name: Navigator
|
- name: Navigator
|
||||||
|
@ -45,7 +45,7 @@ defineLazyProperties(globalThis, 'buffer', ['atob', 'btoa']);
|
|||||||
// https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts
|
// https://html.spec.whatwg.org/multipage/web-messaging.html#broadcasting-to-other-browsing-contexts
|
||||||
exposeLazyInterfaces(globalThis, 'internal/worker/io', ['BroadcastChannel']);
|
exposeLazyInterfaces(globalThis, 'internal/worker/io', ['BroadcastChannel']);
|
||||||
exposeLazyInterfaces(globalThis, 'internal/worker/io', [
|
exposeLazyInterfaces(globalThis, 'internal/worker/io', [
|
||||||
'MessageChannel', 'MessagePort', 'MessageEvent',
|
'MessageChannel', 'MessagePort',
|
||||||
]);
|
]);
|
||||||
// https://www.w3.org/TR/FileAPI/#dfn-Blob
|
// https://www.w3.org/TR/FileAPI/#dfn-Blob
|
||||||
exposeLazyInterfaces(globalThis, 'internal/blob', ['Blob']);
|
exposeLazyInterfaces(globalThis, 'internal/blob', ['Blob']);
|
||||||
@ -84,7 +84,7 @@ ObjectDefineProperty(globalThis, 'fetch', {
|
|||||||
// https://fetch.spec.whatwg.org/#request-class
|
// https://fetch.spec.whatwg.org/#request-class
|
||||||
// https://fetch.spec.whatwg.org/#response-class
|
// https://fetch.spec.whatwg.org/#response-class
|
||||||
exposeLazyInterfaces(globalThis, 'internal/deps/undici/undici', [
|
exposeLazyInterfaces(globalThis, 'internal/deps/undici/undici', [
|
||||||
'FormData', 'Headers', 'Request', 'Response',
|
'FormData', 'Headers', 'Request', 'Response', 'MessageEvent',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// https://websockets.spec.whatwg.org/
|
// https://websockets.spec.whatwg.org/
|
||||||
|
@ -20,7 +20,6 @@ const {
|
|||||||
} = primordials;
|
} = primordials;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
kEmptyObject,
|
|
||||||
kEnumerableProperty,
|
kEnumerableProperty,
|
||||||
setOwnProperty,
|
setOwnProperty,
|
||||||
} = require('internal/util');
|
} = require('internal/util');
|
||||||
@ -38,7 +37,6 @@ const {
|
|||||||
moveMessagePortToContext,
|
moveMessagePortToContext,
|
||||||
receiveMessageOnPort: receiveMessageOnPort_,
|
receiveMessageOnPort: receiveMessageOnPort_,
|
||||||
stopMessagePort,
|
stopMessagePort,
|
||||||
checkMessagePort,
|
|
||||||
DOMException,
|
DOMException,
|
||||||
} = internalBinding('messaging');
|
} = internalBinding('messaging');
|
||||||
const {
|
const {
|
||||||
@ -59,25 +57,19 @@ const {
|
|||||||
const { inspect } = require('internal/util/inspect');
|
const { inspect } = require('internal/util/inspect');
|
||||||
const {
|
const {
|
||||||
codes: {
|
codes: {
|
||||||
ERR_INVALID_ARG_TYPE,
|
|
||||||
ERR_INVALID_THIS,
|
ERR_INVALID_THIS,
|
||||||
ERR_MISSING_ARGS,
|
ERR_MISSING_ARGS,
|
||||||
},
|
},
|
||||||
} = require('internal/errors');
|
} = require('internal/errors');
|
||||||
|
|
||||||
const kData = Symbol('kData');
|
|
||||||
const kHandle = Symbol('kHandle');
|
const kHandle = Symbol('kHandle');
|
||||||
const kIncrementsPortRef = Symbol('kIncrementsPortRef');
|
const kIncrementsPortRef = Symbol('kIncrementsPortRef');
|
||||||
const kLastEventId = Symbol('kLastEventId');
|
|
||||||
const kName = Symbol('kName');
|
const kName = Symbol('kName');
|
||||||
const kOrigin = Symbol('kOrigin');
|
|
||||||
const kOnMessage = Symbol('kOnMessage');
|
const kOnMessage = Symbol('kOnMessage');
|
||||||
const kOnMessageError = Symbol('kOnMessageError');
|
const kOnMessageError = Symbol('kOnMessageError');
|
||||||
const kPort = Symbol('kPort');
|
const kPort = Symbol('kPort');
|
||||||
const kPorts = Symbol('kPorts');
|
|
||||||
const kWaitingStreams = Symbol('kWaitingStreams');
|
const kWaitingStreams = Symbol('kWaitingStreams');
|
||||||
const kWritableCallbacks = Symbol('kWritableCallbacks');
|
const kWritableCallbacks = Symbol('kWritableCallbacks');
|
||||||
const kSource = Symbol('kSource');
|
|
||||||
const kStartedReading = Symbol('kStartedReading');
|
const kStartedReading = Symbol('kStartedReading');
|
||||||
const kStdioWantsMoreDataCallback = Symbol('kStdioWantsMoreDataCallback');
|
const kStdioWantsMoreDataCallback = Symbol('kStdioWantsMoreDataCallback');
|
||||||
const kCurrentlyReceivingPorts =
|
const kCurrentlyReceivingPorts =
|
||||||
@ -93,6 +85,11 @@ const messageTypes = {
|
|||||||
LOAD_SCRIPT: 'loadScript',
|
LOAD_SCRIPT: 'loadScript',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let messageEvent;
|
||||||
|
function lazyMessageEvent() {
|
||||||
|
return messageEvent ??= require('internal/deps/undici/undici').MessageEvent;
|
||||||
|
}
|
||||||
|
|
||||||
// We have to mess with the MessagePort prototype a bit, so that a) we can make
|
// We have to mess with the MessagePort prototype a bit, so that a) we can make
|
||||||
// it inherit from NodeEventTarget, even though it is a C++ class, and b) we do
|
// it inherit from NodeEventTarget, even though it is a C++ class, and b) we do
|
||||||
// not provide methods that are not present in the Browser and not documented
|
// not provide methods that are not present in the Browser and not documented
|
||||||
@ -119,95 +116,6 @@ MessagePort.prototype.hasRef = function() {
|
|||||||
return !!FunctionPrototypeCall(MessagePortPrototype.hasRef, this);
|
return !!FunctionPrototypeCall(MessagePortPrototype.hasRef, this);
|
||||||
};
|
};
|
||||||
|
|
||||||
function validateMessagePort(port, name) {
|
|
||||||
if (!checkMessagePort(port))
|
|
||||||
throw new ERR_INVALID_ARG_TYPE(name, 'MessagePort', port);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isMessageEvent(value) {
|
|
||||||
return value != null && kData in value;
|
|
||||||
}
|
|
||||||
|
|
||||||
class MessageEvent extends Event {
|
|
||||||
constructor(type, {
|
|
||||||
data = null,
|
|
||||||
origin = '',
|
|
||||||
lastEventId = '',
|
|
||||||
source = null,
|
|
||||||
ports = [],
|
|
||||||
} = kEmptyObject) {
|
|
||||||
super(type);
|
|
||||||
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: {
|
|
||||||
__proto__: null,
|
|
||||||
get() {
|
|
||||||
if (!isMessageEvent(this))
|
|
||||||
throw new ERR_INVALID_THIS('MessageEvent');
|
|
||||||
return this[kData];
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
set: undefined,
|
|
||||||
},
|
|
||||||
origin: {
|
|
||||||
__proto__: null,
|
|
||||||
get() {
|
|
||||||
if (!isMessageEvent(this))
|
|
||||||
throw new ERR_INVALID_THIS('MessageEvent');
|
|
||||||
return this[kOrigin];
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
set: undefined,
|
|
||||||
},
|
|
||||||
lastEventId: {
|
|
||||||
__proto__: null,
|
|
||||||
get() {
|
|
||||||
if (!isMessageEvent(this))
|
|
||||||
throw new ERR_INVALID_THIS('MessageEvent');
|
|
||||||
return this[kLastEventId];
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
set: undefined,
|
|
||||||
},
|
|
||||||
source: {
|
|
||||||
__proto__: null,
|
|
||||||
get() {
|
|
||||||
if (!isMessageEvent(this))
|
|
||||||
throw new ERR_INVALID_THIS('MessageEvent');
|
|
||||||
return this[kSource];
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
set: undefined,
|
|
||||||
},
|
|
||||||
ports: {
|
|
||||||
__proto__: null,
|
|
||||||
get() {
|
|
||||||
if (!isMessageEvent(this))
|
|
||||||
throw new ERR_INVALID_THIS('MessageEvent');
|
|
||||||
return this[kPorts];
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
set: undefined,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const originalCreateEvent = EventTarget.prototype[kCreateEvent];
|
const originalCreateEvent = EventTarget.prototype[kCreateEvent];
|
||||||
ObjectDefineProperty(
|
ObjectDefineProperty(
|
||||||
MessagePort.prototype,
|
MessagePort.prototype,
|
||||||
@ -220,7 +128,7 @@ ObjectDefineProperty(
|
|||||||
}
|
}
|
||||||
const ports = this[kCurrentlyReceivingPorts];
|
const ports = this[kCurrentlyReceivingPorts];
|
||||||
this[kCurrentlyReceivingPorts] = undefined;
|
this[kCurrentlyReceivingPorts] = undefined;
|
||||||
return new MessageEvent(type, { data, ports });
|
return new (lazyMessageEvent())(type, { data, ports });
|
||||||
},
|
},
|
||||||
configurable: false,
|
configurable: false,
|
||||||
writable: false,
|
writable: false,
|
||||||
@ -413,7 +321,7 @@ function receiveMessageOnPort(port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onMessageEvent(type, data) {
|
function onMessageEvent(type, data) {
|
||||||
this.dispatchEvent(new MessageEvent(type, { data }));
|
this.dispatchEvent(new (lazyMessageEvent())(type, { data }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isBroadcastChannel(value) {
|
function isBroadcastChannel(value) {
|
||||||
@ -546,7 +454,6 @@ module.exports = {
|
|||||||
moveMessagePortToContext,
|
moveMessagePortToContext,
|
||||||
MessagePort,
|
MessagePort,
|
||||||
MessageChannel,
|
MessageChannel,
|
||||||
MessageEvent,
|
|
||||||
receiveMessageOnPort,
|
receiveMessageOnPort,
|
||||||
setupPortReferencing,
|
setupPortReferencing,
|
||||||
ReadableWorkerStdio,
|
ReadableWorkerStdio,
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
// Flags: --expose-internals
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
require('../common');
|
require('../common');
|
||||||
const assert = require('assert');
|
const assert = require('assert');
|
||||||
|
|
||||||
const {
|
|
||||||
MessageEvent,
|
|
||||||
} = require('internal/worker/io');
|
|
||||||
|
|
||||||
[
|
[
|
||||||
'data',
|
'data',
|
||||||
'origin',
|
'origin',
|
||||||
@ -15,7 +10,5 @@ const {
|
|||||||
'source',
|
'source',
|
||||||
'ports',
|
'ports',
|
||||||
].forEach((i) => {
|
].forEach((i) => {
|
||||||
assert.throws(() => Reflect.get(MessageEvent.prototype, i, {}), {
|
assert.throws(() => Reflect.get(MessageEvent.prototype, i, {}), TypeError);
|
||||||
code: 'ERR_INVALID_THIS',
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
@ -61,31 +61,31 @@ const dummyPort = new MessageChannel().port1;
|
|||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
new MessageEvent('message', { source: 1 });
|
new MessageEvent('message', { source: 1 });
|
||||||
}, {
|
}, {
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
name: 'TypeError',
|
||||||
message: /The "init\.source" property must be an instance of MessagePort/,
|
message: /Expected 1 to be an instance of MessagePort/,
|
||||||
});
|
});
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
new MessageEvent('message', { source: {} });
|
new MessageEvent('message', { source: {} });
|
||||||
}, {
|
}, {
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
name: 'TypeError',
|
||||||
message: /The "init\.source" property must be an instance of MessagePort/,
|
message: /Expected {} to be an instance of MessagePort/,
|
||||||
});
|
});
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
new MessageEvent('message', { ports: 0 });
|
new MessageEvent('message', { ports: 0 });
|
||||||
}, {
|
}, {
|
||||||
message: /ports is not iterable/,
|
message: /Sequence: Value of type Number is not an Object/,
|
||||||
});
|
});
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
new MessageEvent('message', { ports: [ null ] });
|
new MessageEvent('message', { ports: [ null ] });
|
||||||
}, {
|
}, {
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
name: 'TypeError',
|
||||||
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
|
message: /Expected null to be an instance of MessagePort/,
|
||||||
});
|
});
|
||||||
assert.throws(() => {
|
assert.throws(() => {
|
||||||
new MessageEvent('message', { ports: [ {} ] });
|
new MessageEvent('message', { ports: [ {} ] });
|
||||||
}, {
|
}, {
|
||||||
code: 'ERR_INVALID_ARG_TYPE',
|
name: 'TypeError',
|
||||||
message: /The "init\.ports\[0\]" property must be an instance of MessagePort/,
|
message: /Expected {} to be an instance of MessagePort/,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user