From f9df004175ab79da8cbabfd058873b9dfc28084a Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 5 Jan 2022 20:01:49 +0900 Subject: [PATCH] src: add kNoBrowserGlobals flag for Environment PR-URL: https://github.com/nodejs/node/pull/40532 Reviewed-By: Anna Henningsen Reviewed-By: Joyee Cheung Reviewed-By: James M Snell Reviewed-By: Minwoo Jung Reviewed-By: Benjamin Gruenbaum Reviewed-By: Antoine du Hamel --- configure.py | 3 +- lib/internal/bootstrap/browser.js | 118 +++++++++++++++++++++++ lib/internal/bootstrap/node.js | 153 ++++-------------------------- lib/internal/util.js | 22 +++++ src/env-inl.h | 9 ++ src/env.h | 1 + src/node.cc | 15 ++- src/node.h | 4 +- src/node_worker.cc | 2 + 9 files changed, 185 insertions(+), 142 deletions(-) create mode 100644 lib/internal/bootstrap/browser.js diff --git a/configure.py b/configure.py index a1b5c5570b6cb7..817bcbf8bfc570 100755 --- a/configure.py +++ b/configure.py @@ -703,7 +703,8 @@ dest='no_browser_globals', default=None, help='do not export browser globals like setTimeout, console, etc. ' + - '(This mode is not officially supported for regular applications)') + '(This mode is deprecated and not officially supported for regular ' + + 'applications)') parser.add_argument('--without-inspector', action='store_true', diff --git a/lib/internal/bootstrap/browser.js b/lib/internal/bootstrap/browser.js new file mode 100644 index 00000000000000..68dac3b1ca4b8c --- /dev/null +++ b/lib/internal/bootstrap/browser.js @@ -0,0 +1,118 @@ +'use strict'; + +const { + ObjectDefineProperty, + globalThis, +} = primordials; + +const { + defineOperation, + exposeInterface, + lazyDOMExceptionClass, +} = require('internal/util'); +const config = internalBinding('config'); + +// Override global console from the one provided by the VM +// to the one implemented by Node.js +// https://console.spec.whatwg.org/#console-namespace +exposeNamespace(globalThis, 'console', + createGlobalConsole(globalThis.console)); + +const { URL, URLSearchParams } = require('internal/url'); +// https://url.spec.whatwg.org/#url +exposeInterface(globalThis, 'URL', URL); +// https://url.spec.whatwg.org/#urlsearchparams +exposeInterface(globalThis, 'URLSearchParams', URLSearchParams); +exposeGetterAndSetter(globalThis, + 'DOMException', + lazyDOMExceptionClass, + (value) => { + exposeInterface(globalThis, 'DOMException', value); + }); + +const { + TextEncoder, TextDecoder +} = require('internal/encoding'); +// https://encoding.spec.whatwg.org/#textencoder +exposeInterface(globalThis, 'TextEncoder', TextEncoder); +// https://encoding.spec.whatwg.org/#textdecoder +exposeInterface(globalThis, 'TextDecoder', TextDecoder); + +const { + AbortController, + AbortSignal, +} = require('internal/abort_controller'); +exposeInterface(globalThis, 'AbortController', AbortController); +exposeInterface(globalThis, 'AbortSignal', AbortSignal); + +const { + EventTarget, + Event, +} = require('internal/event_target'); +exposeInterface(globalThis, 'EventTarget', EventTarget); +exposeInterface(globalThis, 'Event', Event); +const { + MessageChannel, + MessagePort, + MessageEvent, +} = require('internal/worker/io'); +exposeInterface(globalThis, 'MessageChannel', MessageChannel); +exposeInterface(globalThis, 'MessagePort', MessagePort); +exposeInterface(globalThis, 'MessageEvent', MessageEvent); + +// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope +const timers = require('timers'); +defineOperation(globalThis, 'clearInterval', timers.clearInterval); +defineOperation(globalThis, 'clearTimeout', timers.clearTimeout); +defineOperation(globalThis, 'setInterval', timers.setInterval); +defineOperation(globalThis, 'setTimeout', timers.setTimeout); + +// https://www.w3.org/TR/hr-time-2/#the-performance-attribute +defineReplacableAttribute(globalThis, 'performance', + require('perf_hooks').performance); + +function createGlobalConsole(consoleFromVM) { + const consoleFromNode = + require('internal/console/global'); + if (config.hasInspector) { + const inspector = require('internal/util/inspector'); + // This will be exposed by `require('inspector').console` later. + inspector.consoleFromVM = consoleFromVM; + // TODO(joyeecheung): postpone this until the first time inspector + // is activated. + inspector.wrapConsole(consoleFromNode, consoleFromVM); + const { setConsoleExtensionInstaller } = internalBinding('inspector'); + // Setup inspector command line API. + setConsoleExtensionInstaller(inspector.installConsoleExtensions); + } + return consoleFromNode; +} + +// https://heycam.github.io/webidl/#es-namespaces +function exposeNamespace(target, name, namespaceObject) { + ObjectDefineProperty(target, name, { + writable: true, + enumerable: false, + configurable: true, + value: namespaceObject + }); +} + +function exposeGetterAndSetter(target, name, getter, setter = undefined) { + ObjectDefineProperty(target, name, { + enumerable: false, + configurable: true, + get: getter, + set: setter, + }); +} + +// https://heycam.github.io/webidl/#Replaceable +function defineReplacableAttribute(target, name, value) { + ObjectDefineProperty(target, name, { + writable: true, + enumerable: true, + configurable: true, + value, + }); +} diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index 74d1bd44ff0846..239f5258620c04 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -56,7 +56,11 @@ const { } = primordials; const config = internalBinding('config'); const internalTimers = require('internal/timers'); -const { deprecate, lazyDOMExceptionClass } = require('internal/util'); +const { + defineOperation, + deprecate, + exposeInterface, +} = require('internal/util'); setupProcessObject(); @@ -205,79 +209,20 @@ const { queueMicrotask } = require('internal/process/task_queues'); -if (!config.noBrowserGlobals) { - // Override global console from the one provided by the VM - // to the one implemented by Node.js - // https://console.spec.whatwg.org/#console-namespace - exposeNamespace(globalThis, 'console', - createGlobalConsole(globalThis.console)); - - const { URL, URLSearchParams } = require('internal/url'); - // https://url.spec.whatwg.org/#url - exposeInterface(globalThis, 'URL', URL); - // https://url.spec.whatwg.org/#urlsearchparams - exposeInterface(globalThis, 'URLSearchParams', URLSearchParams); - exposeGetterAndSetter(globalThis, - 'DOMException', - lazyDOMExceptionClass, - (value) => { - exposeInterface(globalThis, 'DOMException', value); - }); +// Non-standard extensions: +const { BroadcastChannel } = require('internal/worker/io'); +exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel); - const { - TextEncoder, TextDecoder - } = require('internal/encoding'); - // https://encoding.spec.whatwg.org/#textencoder - exposeInterface(globalThis, 'TextEncoder', TextEncoder); - // https://encoding.spec.whatwg.org/#textdecoder - exposeInterface(globalThis, 'TextDecoder', TextDecoder); +defineOperation(globalThis, 'queueMicrotask', queueMicrotask); - const { - AbortController, - AbortSignal, - } = require('internal/abort_controller'); - exposeInterface(globalThis, 'AbortController', AbortController); - exposeInterface(globalThis, 'AbortSignal', AbortSignal); +const timers = require('timers'); +defineOperation(globalThis, 'clearImmediate', timers.clearImmediate); +defineOperation(globalThis, 'setImmediate', timers.setImmediate); - const { - EventTarget, - Event, - } = require('internal/event_target'); - exposeInterface(globalThis, 'EventTarget', EventTarget); - exposeInterface(globalThis, 'Event', Event); - const { - MessageChannel, - MessagePort, - MessageEvent, - BroadcastChannel, - } = require('internal/worker/io'); - exposeInterface(globalThis, 'MessageChannel', MessageChannel); - exposeInterface(globalThis, 'MessagePort', MessagePort); - exposeInterface(globalThis, 'MessageEvent', MessageEvent); - exposeInterface(globalThis, 'BroadcastChannel', BroadcastChannel); - - // https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope - const timers = require('timers'); - defineOperation(globalThis, 'clearInterval', timers.clearInterval); - defineOperation(globalThis, 'clearTimeout', timers.clearTimeout); - defineOperation(globalThis, 'setInterval', timers.setInterval); - defineOperation(globalThis, 'setTimeout', timers.setTimeout); - - defineOperation(globalThis, 'queueMicrotask', queueMicrotask); - - // https://www.w3.org/TR/hr-time-2/#the-performance-attribute - defineReplacableAttribute(globalThis, 'performance', - require('perf_hooks').performance); - - // Non-standard extensions: - defineOperation(globalThis, 'clearImmediate', timers.clearImmediate); - defineOperation(globalThis, 'setImmediate', timers.setImmediate); - - const { - structuredClone, - } = require('internal/structured_clone'); - defineOperation(globalThis, 'structuredClone', structuredClone); -} +const { + structuredClone, +} = require('internal/structured_clone'); +defineOperation(globalThis, 'structuredClone', structuredClone); // Set the per-Environment callback that will be called // when the TrackingTraceStateObserver updates trace state. @@ -483,69 +428,3 @@ function setupBuffer() { }, }); } - -function createGlobalConsole(consoleFromVM) { - const consoleFromNode = - require('internal/console/global'); - if (config.hasInspector) { - const inspector = require('internal/util/inspector'); - // This will be exposed by `require('inspector').console` later. - inspector.consoleFromVM = consoleFromVM; - // TODO(joyeecheung): postpone this until the first time inspector - // is activated. - inspector.wrapConsole(consoleFromNode, consoleFromVM); - const { setConsoleExtensionInstaller } = internalBinding('inspector'); - // Setup inspector command line API. - setConsoleExtensionInstaller(inspector.installConsoleExtensions); - } - return consoleFromNode; -} - -// https://heycam.github.io/webidl/#es-namespaces -function exposeNamespace(target, name, namespaceObject) { - ObjectDefineProperty(target, name, { - writable: true, - enumerable: false, - configurable: true, - value: namespaceObject - }); -} - -// https://heycam.github.io/webidl/#es-interfaces -function exposeInterface(target, name, interfaceObject) { - ObjectDefineProperty(target, name, { - writable: true, - enumerable: false, - configurable: true, - value: interfaceObject - }); -} - -function exposeGetterAndSetter(target, name, getter, setter = undefined) { - ObjectDefineProperty(target, name, { - enumerable: false, - configurable: true, - get: getter, - set: setter, - }); -} - -// https://heycam.github.io/webidl/#define-the-operations -function defineOperation(target, name, method) { - ObjectDefineProperty(target, name, { - writable: true, - enumerable: true, - configurable: true, - value: method - }); -} - -// https://heycam.github.io/webidl/#Replaceable -function defineReplacableAttribute(target, name, value) { - ObjectDefineProperty(target, name, { - writable: true, - enumerable: true, - configurable: true, - value, - }); -} diff --git a/lib/internal/util.js b/lib/internal/util.js index e0f26f35717219..1e04efe75d5234 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -466,6 +466,26 @@ function createDeferredPromise() { return { promise, resolve, reject }; } +// https://heycam.github.io/webidl/#define-the-operations +function defineOperation(target, name, method) { + ObjectDefineProperty(target, name, { + writable: true, + enumerable: true, + configurable: true, + value: method + }); +} + +// https://heycam.github.io/webidl/#es-interfaces +function exposeInterface(target, name, interfaceObject) { + ObjectDefineProperty(target, name, { + writable: true, + enumerable: false, + configurable: true, + value: interfaceObject + }); +} + let _DOMException; const lazyDOMExceptionClass = () => { _DOMException ??= internalBinding('messaging').DOMException; @@ -484,8 +504,10 @@ module.exports = { createClassWrapper, createDeferredPromise, decorateErrorStack, + defineOperation, deprecate, emitExperimentalWarning, + exposeInterface, filterDuplicateStrings, getConstructorOf, getSystemErrorMap, diff --git a/src/env-inl.h b/src/env-inl.h index e679780900abc9..0a2a70eddd938c 100644 --- a/src/env-inl.h +++ b/src/env-inl.h @@ -882,6 +882,15 @@ inline bool Environment::no_global_search_paths() const { !options_->global_search_paths; } +inline bool Environment::no_browser_globals() const { + // configure --no-browser-globals +#ifdef NODE_NO_BROWSER_GLOBALS + return true; +#else + return flags_ & EnvironmentFlags::kNoBrowserGlobals; +#endif +} + bool Environment::filehandle_close_warning() const { return emit_filehandle_warning_; } diff --git a/src/env.h b/src/env.h index 5af2eaa6669f0b..92c9d1d1404563 100644 --- a/src/env.h +++ b/src/env.h @@ -1212,6 +1212,7 @@ class Environment : public MemoryRetainer { inline bool tracks_unmanaged_fds() const; inline bool hide_console_windows() const; inline bool no_global_search_paths() const; + inline bool no_browser_globals() const; inline uint64_t thread_id() const; inline worker::Worker* worker_context() const; Environment* worker_parent_env() const; diff --git a/src/node.cc b/src/node.cc index 7f18a21f672537..4840b18feed691 100644 --- a/src/node.cc +++ b/src/node.cc @@ -360,7 +360,16 @@ MaybeLocal Environment::BootstrapNode() { this, "internal/bootstrap/node", &node_params, &node_args); if (result.IsEmpty()) { - return scope.EscapeMaybe(result); + return MaybeLocal(); + } + + if (!no_browser_globals()) { + result = ExecuteBootstrapper( + this, "internal/bootstrap/browser", &node_params, &node_args); + + if (result.IsEmpty()) { + return MaybeLocal(); + } } // TODO(joyeecheung): skip these in the snapshot building for workers. @@ -371,7 +380,7 @@ MaybeLocal Environment::BootstrapNode() { ExecuteBootstrapper(this, thread_switch_id, &node_params, &node_args); if (result.IsEmpty()) { - return scope.EscapeMaybe(result); + return MaybeLocal(); } auto process_state_switch_id = @@ -382,7 +391,7 @@ MaybeLocal Environment::BootstrapNode() { this, process_state_switch_id, &node_params, &node_args); if (result.IsEmpty()) { - return scope.EscapeMaybe(result); + return MaybeLocal(); } Local env_string = FIXED_ONE_BYTE_STRING(isolate_, "env"); diff --git a/src/node.h b/src/node.h index 8d9e9935258c35..1631fff20cf30c 100644 --- a/src/node.h +++ b/src/node.h @@ -439,7 +439,9 @@ enum Flags : uint64_t { // $HOME/.node_modules and $NODE_PATH. This is used by standalone apps that // do not expect to have their behaviors changed because of globally // installed modules. - kNoGlobalSearchPaths = 1 << 7 + kNoGlobalSearchPaths = 1 << 7, + // Do not export browser globals like setTimeout, console, etc. + kNoBrowserGlobals = 1 << 8, }; } // namespace EnvironmentFlags diff --git a/src/node_worker.cc b/src/node_worker.cc index a734d65965a9d7..6e0475511d5770 100644 --- a/src/node_worker.cc +++ b/src/node_worker.cc @@ -574,6 +574,8 @@ void Worker::New(const FunctionCallbackInfo& args) { worker->environment_flags_ |= EnvironmentFlags::kNoNativeAddons; if (env->no_global_search_paths()) worker->environment_flags_ |= EnvironmentFlags::kNoGlobalSearchPaths; + if (env->no_browser_globals()) + worker->environment_flags_ |= EnvironmentFlags::kNoBrowserGlobals; } void Worker::StartThread(const FunctionCallbackInfo& args) {