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

lib: add EventTarget-related browser globals #35496

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions .eslintrc.js
Expand Up @@ -287,6 +287,11 @@ module.exports = {
BigInt: 'readable',
BigInt64Array: 'readable',
BigUint64Array: 'readable',
Event: 'readable',
EventTarget: 'readable',
MessageChannel: 'readable',
MessageEvent: 'readable',
MessagePort: 'readable',
TextEncoder: 'readable',
TextDecoder: 'readable',
queueMicrotask: 'readable',
Expand Down
21 changes: 18 additions & 3 deletions doc/api/events.md
Expand Up @@ -1067,9 +1067,15 @@ const ac = new AbortController();
process.nextTick(() => ac.abort());
```

<a id="event-target-and-event-api"></a>
## `EventTarget` and `Event` API
<!-- YAML
added: v14.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/35496
description:
The `EventTarget` and `Event` classes are now available as globals.
-->

> Stability: 1 - Experimental
Expand All @@ -1080,7 +1086,7 @@ Neither the `EventTarget` nor `Event` classes are available for end
user code to create.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence is no longer true.

Suggested change
user code to create.

I’m fixing that in #37059.


```js
const target = getEventTargetSomehow();
const target = new EventTarget();

target.addEventListener('foo', (event) => {
console.log('foo event happened!');
Expand Down Expand Up @@ -1166,7 +1172,7 @@ const handler4 = {
}
};

const target = getEventTargetSomehow();
const target = new EventTarget();

target.addEventListener('foo', handler1);
target.addEventListener('foo', handler2);
Expand All @@ -1187,6 +1193,10 @@ The `EventTarget` does not implement any special default handling for
### Class: `Event`
<!-- YAML
added: v14.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/35496
description: The `Event` class is now available through the global object.
-->

The `Event` object is an adaptation of the [`Event` Web API][]. Instances
Expand Down Expand Up @@ -1339,6 +1349,11 @@ The event type identifier.
### Class: `EventTarget`
<!-- YAML
added: v14.5.0
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/35496
description:
The `EventTarget` class is now available through the global object.
-->

#### `eventTarget.addEventListener(type, listener[, options])`
Expand Down Expand Up @@ -1372,7 +1387,7 @@ a `listener`. Any individual `listener` may be added once with
```js
function handler(event) {}

const target = getEventTargetSomehow();
const target = new EventTarget();
target.addEventListener('foo', handler, { capture: true }); // first
target.addEventListener('foo', handler, { capture: false }); // second

Expand Down
55 changes: 55 additions & 0 deletions doc/api/globals.md
Expand Up @@ -169,6 +169,30 @@ added: v0.1.100

Used to print to stdout and stderr. See the [`console`][] section.

## `Event`
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

> Stability: 1 - Experimental

A browser-compatible implementation of the `Event` class. See
[`EventTarget` and `Event` API][] for more details.

## `EventTarget`
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

> Stability: 1 - Experimental

A browser-compatible implementation of the `EventTarget` class. See
[`EventTarget` and `Event` API][] for more details.

## `exports`

This variable may appear to be global but is not. See [`exports`][].
Expand All @@ -187,6 +211,33 @@ within the browser `var something` will define a new global variable. In
Node.js this is different. The top-level scope is not the global scope;
`var something` inside a Node.js module will be local to that module.

## `MessageChannel`
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

The `MessageChannel` class. See [`MessageChannel`][] for more details.

## `MessageEvent`
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

The `MessageEvent` class. See [`MessageEvent`][] for more details.

## `MessagePort`
<!-- YAML
added: REPLACEME
-->

<!-- type=global -->

The `MessagePort` class. See [`MessagePort`][] for more details.

## `module`

This variable may appear to be global but is not. See [`module`][].
Expand Down Expand Up @@ -322,6 +373,10 @@ The object that acts as the namespace for all W3C
[Mozilla Developer Network][webassembly-mdn] for usage and compatibility.

[`AbortController`]: https://developer.mozilla.org/en-US/docs/Web/API/AbortController
[`EventTarget` and `Event` API]: events.md#event-target-and-event-api
[`MessageChannel`]: worker_threads.md#worker_threads_class_messagechannel
[`MessageEvent`]: https://developer.mozilla.org/en-US/docs/Web/API/MessageEvent/MessageEvent
[`MessagePort`]: worker_threads.md#worker_threads_class_messageport
[`TextDecoder`]: util.md#util_class_util_textdecoder
[`TextEncoder`]: util.md#util_class_util_textencoder
[`URLSearchParams`]: url.md#url_class_urlsearchparams
Expand Down
15 changes: 15 additions & 0 deletions lib/internal/bootstrap/node.js
Expand Up @@ -140,6 +140,21 @@ if (!config.noBrowserGlobals) {
exposeInterface(global, 'AbortController', AbortController);
exposeInterface(global, 'AbortSignal', AbortSignal);

const {
EventTarget,
Event,
} = require('internal/event_target');
exposeInterface(global, 'EventTarget', EventTarget);
exposeInterface(global, 'Event', Event);
const {
MessageChannel,
MessagePort,
MessageEvent,
} = require('internal/worker/io');
exposeInterface(global, 'MessageChannel', MessageChannel);
exposeInterface(global, 'MessagePort', MessagePort);
exposeInterface(global, 'MessageEvent', MessageEvent);

// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope
const timers = require('timers');
defineOperation(global, 'clearInterval', timers.clearInterval);
Expand Down
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
3 changes: 2 additions & 1 deletion src/node_external_reference.h
Expand Up @@ -64,7 +64,8 @@ class ExternalReferenceRegistry {
V(string_decoder) \
V(trace_events) \
V(timers) \
V(types)
V(types) \
V(worker)

#if NODE_HAVE_I18N_SUPPORT
#define EXTERNAL_REFERENCE_BINDING_LIST_I18N(V) V(icu)
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
14 changes: 13 additions & 1 deletion src/node_worker.cc
Expand Up @@ -2,6 +2,7 @@
#include "debug_utils-inl.h"
#include "memory_tracker-inl.h"
#include "node_errors.h"
#include "node_external_reference.h"
#include "node_buffer.h"
#include "node_options-inl.h"
#include "node_perf.h"
Expand Down Expand Up @@ -851,9 +852,20 @@ void InitWorker(Local<Object> target,
NODE_DEFINE_CONSTANT(target, kTotalResourceLimitCount);
}

} // anonymous namespace
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(GetEnvMessagePort);
registry->Register(Worker::New);
registry->Register(Worker::StartThread);
registry->Register(Worker::StopThread);
registry->Register(Worker::Ref);
registry->Register(Worker::Unref);
registry->Register(Worker::GetResourceLimits);
registry->Register(Worker::TakeHeapSnapshot);
}

} // anonymous namespace
} // namespace worker
} // namespace node

NODE_MODULE_CONTEXT_AWARE_INTERNAL(worker, node::worker::InitWorker)
NODE_MODULE_EXTERNAL_REFERENCE(worker, node::worker::RegisterExternalReferences)
14 changes: 14 additions & 0 deletions test/parallel/test-bootstrap-modules.js
Expand Up @@ -31,6 +31,7 @@ const expectedModules = new Set([
'Internal Binding types',
'Internal Binding url',
'Internal Binding util',
'Internal Binding worker',
'NativeModule buffer',
'NativeModule events',
'NativeModule fs',
Expand Down Expand Up @@ -76,6 +77,17 @@ const expectedModules = new Set([
'NativeModule internal/process/warning',
'NativeModule internal/querystring',
'NativeModule internal/source_map/source_map_cache',
'NativeModule internal/streams/buffer_list',
'NativeModule internal/streams/destroy',
'NativeModule internal/streams/duplex',
'NativeModule internal/streams/end-of-stream',
'NativeModule internal/streams/legacy',
'NativeModule internal/streams/passthrough',
'NativeModule internal/streams/pipeline',
'NativeModule internal/streams/readable',
'NativeModule internal/streams/state',
'NativeModule internal/streams/transform',
'NativeModule internal/streams/writable',
'NativeModule internal/timers',
'NativeModule internal/url',
'NativeModule internal/util',
Expand All @@ -84,8 +96,10 @@ const expectedModules = new Set([
'NativeModule internal/util/types',
'NativeModule internal/validators',
'NativeModule internal/vm/module',
'NativeModule internal/worker/io',
'NativeModule internal/worker/js_transferable',
'NativeModule path',
'NativeModule stream',
'NativeModule timers',
'NativeModule url',
'NativeModule util',
Expand Down